@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,414 @@
1
+ import test from 'ava';
2
+ import fs from 'fs';
3
+ import bcrypt from 'bcrypt';
4
+ import {
5
+ create_recovery_token,
6
+ is_token_valid,
7
+ record_failed_recovery_attempt,
8
+ change_password,
9
+ get_recovery_status,
10
+ initialize_recovery_manager,
11
+ reset_recovery_state
12
+ } from '../../../src/server/lib/recovery_manager.js';
13
+
14
+ const RECOVERY_TOKEN_FILE = './recovery_token.json';
15
+
16
+ test.beforeEach(() => {
17
+ // Clean up any existing files and environment variables
18
+ reset_recovery_state();
19
+ if (fs.existsSync(RECOVERY_TOKEN_FILE)) {
20
+ fs.unlinkSync(RECOVERY_TOKEN_FILE);
21
+ }
22
+ delete process.env.JOYSTICK_DB_SETTINGS;
23
+ });
24
+
25
+ test.afterEach(() => {
26
+ // Clean up files and environment variables after each test
27
+ reset_recovery_state();
28
+ if (fs.existsSync(RECOVERY_TOKEN_FILE)) {
29
+ fs.unlinkSync(RECOVERY_TOKEN_FILE);
30
+ }
31
+ delete process.env.JOYSTICK_DB_SETTINGS;
32
+ });
33
+
34
+ test('create_recovery_token generates valid token with expiration', (t) => {
35
+ const recovery_info = create_recovery_token();
36
+
37
+ t.true(typeof recovery_info.token === 'string');
38
+ t.true(recovery_info.token.length > 0);
39
+ t.true(typeof recovery_info.expires_at === 'number');
40
+ t.true(recovery_info.expires_at > Date.now());
41
+ t.true(recovery_info.url.includes(recovery_info.token));
42
+
43
+ // Verify token file was created
44
+ t.true(fs.existsSync(RECOVERY_TOKEN_FILE));
45
+
46
+ const token_data = JSON.parse(fs.readFileSync(RECOVERY_TOKEN_FILE, 'utf8'));
47
+ t.is(token_data.token, recovery_info.token);
48
+ t.is(token_data.expires_at, recovery_info.expires_at);
49
+ });
50
+
51
+ test('create_recovery_token sets 10 minute expiration', (t) => {
52
+ const before_creation = Date.now();
53
+ const recovery_info = create_recovery_token();
54
+ const after_creation = Date.now();
55
+
56
+ const expected_min_expiry = before_creation + (10 * 60 * 1000);
57
+ const expected_max_expiry = after_creation + (10 * 60 * 1000);
58
+
59
+ t.true(recovery_info.expires_at >= expected_min_expiry);
60
+ t.true(recovery_info.expires_at <= expected_max_expiry);
61
+ });
62
+
63
+ test('is_token_valid returns true for valid token', (t) => {
64
+ const recovery_info = create_recovery_token();
65
+ const validation = is_token_valid(recovery_info.token);
66
+
67
+ t.true(validation.valid);
68
+ });
69
+
70
+ test('is_token_valid returns false for invalid token', (t) => {
71
+ create_recovery_token();
72
+ const validation = is_token_valid('invalid-token');
73
+
74
+ t.false(validation.valid);
75
+ t.is(validation.reason, 'invalid');
76
+ });
77
+
78
+ test('is_token_valid returns false when no token exists', (t) => {
79
+ const validation = is_token_valid('any-token');
80
+
81
+ t.false(validation.valid);
82
+ t.is(validation.reason, 'no_token');
83
+ });
84
+
85
+ test('is_token_valid returns false for expired token', (t) => {
86
+ const recovery_info = create_recovery_token();
87
+
88
+ // Manually expire the token by modifying the file
89
+ const token_data = JSON.parse(fs.readFileSync(RECOVERY_TOKEN_FILE, 'utf8'));
90
+ token_data.expires_at = Date.now() - 1000; // 1 second ago
91
+ fs.writeFileSync(RECOVERY_TOKEN_FILE, JSON.stringify(token_data, null, 2));
92
+
93
+ // Reload state to pick up the expired token (but don't initialize, which would clean it up)
94
+ // Instead, directly test the validation which will detect expiration and clean up
95
+ const validation = is_token_valid(recovery_info.token);
96
+
97
+ t.false(validation.valid);
98
+ t.is(validation.reason, 'expired');
99
+ });
100
+
101
+ test('record_failed_recovery_attempt increments counter', (t) => {
102
+ create_recovery_token();
103
+
104
+ record_failed_recovery_attempt('192.168.1.100');
105
+
106
+ const status = get_recovery_status();
107
+ t.is(status.failed_attempts, 1);
108
+ });
109
+
110
+ test('record_failed_recovery_attempt locks after max attempts', (t) => {
111
+ create_recovery_token();
112
+
113
+ // Make 3 failed attempts (the maximum)
114
+ for (let i = 0; i < 3; i++) {
115
+ record_failed_recovery_attempt('192.168.1.100');
116
+ }
117
+
118
+ const status = get_recovery_status();
119
+ t.is(status.failed_attempts, 3);
120
+ t.true(status.is_locked);
121
+ t.true(typeof status.locked_until === 'number');
122
+ t.true(status.locked_until > Date.now());
123
+ });
124
+
125
+ test('is_token_valid returns locked when recovery is locked', (t) => {
126
+ const recovery_info = create_recovery_token();
127
+
128
+ // Lock the recovery by making too many failed attempts
129
+ for (let i = 0; i < 3; i++) {
130
+ record_failed_recovery_attempt('192.168.1.100');
131
+ }
132
+
133
+ const validation = is_token_valid(recovery_info.token);
134
+
135
+ t.false(validation.valid);
136
+ t.is(validation.reason, 'locked');
137
+ });
138
+
139
+ test('change_password validates password strength', async (t) => {
140
+ // Create environment variable with authentication
141
+ const settings_data = {
142
+ authentication: {
143
+ password_hash: await bcrypt.hash('old_password', 12),
144
+ created_at: new Date().toISOString(),
145
+ last_updated: new Date().toISOString(),
146
+ failed_attempts: {},
147
+ rate_limits: {}
148
+ }
149
+ };
150
+ process.env.JOYSTICK_DB_SETTINGS = JSON.stringify(settings_data);
151
+
152
+ // Test short password
153
+ const error = await t.throwsAsync(async () => {
154
+ await change_password('short', '192.168.1.100');
155
+ });
156
+
157
+ t.is(error.message, 'Password must be at least 12 characters long');
158
+ });
159
+
160
+ test('change_password requires environment variable', async (t) => {
161
+ const error = await t.throwsAsync(async () => {
162
+ await change_password('valid_password_123', '192.168.1.100');
163
+ });
164
+
165
+ t.is(error.message, 'JOYSTICK_DB_SETTINGS environment variable not found');
166
+ });
167
+
168
+ test('change_password requires authentication configuration', async (t) => {
169
+ // Create environment variable without authentication
170
+ const settings_data = { port: 1983 };
171
+ process.env.JOYSTICK_DB_SETTINGS = JSON.stringify(settings_data);
172
+
173
+ const error = await t.throwsAsync(async () => {
174
+ await change_password('valid_password_123', '192.168.1.100');
175
+ });
176
+
177
+ t.is(error.message, 'Authentication not configured');
178
+ });
179
+
180
+ test('change_password successfully updates password', async (t) => {
181
+ // Create environment variable with authentication
182
+ const old_password = 'old_password_123';
183
+ const new_password = 'new_password_456';
184
+
185
+ const settings_data = {
186
+ authentication: {
187
+ password_hash: await bcrypt.hash(old_password, 12),
188
+ created_at: new Date().toISOString(),
189
+ last_updated: new Date().toISOString(),
190
+ failed_attempts: { '192.168.1.50': [Date.now()] },
191
+ rate_limits: { '192.168.1.50': { expires_at: Date.now() + 60000, attempts: 1 } }
192
+ }
193
+ };
194
+ process.env.JOYSTICK_DB_SETTINGS = JSON.stringify(settings_data);
195
+
196
+ const result = await change_password(new_password, '192.168.1.100');
197
+
198
+ t.true(result.success);
199
+ t.is(result.message, 'Password changed successfully');
200
+ t.true(typeof result.timestamp === 'string');
201
+
202
+ // Verify password was updated in environment variable
203
+ const updated_settings = JSON.parse(process.env.JOYSTICK_DB_SETTINGS);
204
+ t.true(await bcrypt.compare(new_password, updated_settings.authentication.password_hash));
205
+ t.false(await bcrypt.compare(old_password, updated_settings.authentication.password_hash));
206
+
207
+ // Verify failed attempts and rate limits were reset
208
+ t.deepEqual(updated_settings.authentication.failed_attempts, {});
209
+ t.deepEqual(updated_settings.authentication.rate_limits, {});
210
+ });
211
+
212
+ test('change_password calls connection terminator', async (t) => {
213
+ // Create environment variable with authentication
214
+ const settings_data = {
215
+ authentication: {
216
+ password_hash: await bcrypt.hash('old_password_123', 12),
217
+ created_at: new Date().toISOString(),
218
+ last_updated: new Date().toISOString(),
219
+ failed_attempts: {},
220
+ rate_limits: {}
221
+ }
222
+ };
223
+ process.env.JOYSTICK_DB_SETTINGS = JSON.stringify(settings_data);
224
+
225
+ let terminator_called = false;
226
+ const connection_terminator = () => {
227
+ terminator_called = true;
228
+ };
229
+
230
+ await change_password('new_password_456', '192.168.1.100', connection_terminator);
231
+
232
+ t.true(terminator_called);
233
+ });
234
+
235
+ test('change_password cleans up recovery token', async (t) => {
236
+ // Create recovery token
237
+ create_recovery_token();
238
+ t.true(fs.existsSync(RECOVERY_TOKEN_FILE));
239
+
240
+ // Create environment variable with authentication
241
+ const settings_data = {
242
+ authentication: {
243
+ password_hash: await bcrypt.hash('old_password_123', 12),
244
+ created_at: new Date().toISOString(),
245
+ last_updated: new Date().toISOString(),
246
+ failed_attempts: {},
247
+ rate_limits: {}
248
+ }
249
+ };
250
+ process.env.JOYSTICK_DB_SETTINGS = JSON.stringify(settings_data);
251
+
252
+ await change_password('new_password_456', '192.168.1.100');
253
+
254
+ // Verify recovery token was cleaned up
255
+ t.false(fs.existsSync(RECOVERY_TOKEN_FILE));
256
+
257
+ const status = get_recovery_status();
258
+ t.false(status.token_active);
259
+ });
260
+
261
+ test('get_recovery_status returns correct status', (t) => {
262
+ // Test with no token
263
+ let status = get_recovery_status();
264
+ t.false(status.token_active);
265
+ t.is(status.failed_attempts, 0);
266
+ t.false(status.is_locked);
267
+
268
+ // Test with active token
269
+ create_recovery_token();
270
+ status = get_recovery_status();
271
+ t.true(status.token_active);
272
+ t.true(typeof status.expires_at === 'number');
273
+
274
+ // Test with failed attempts
275
+ record_failed_recovery_attempt('192.168.1.100');
276
+ status = get_recovery_status();
277
+ t.is(status.failed_attempts, 1);
278
+ });
279
+
280
+ test('initialize_recovery_manager loads existing state', (t) => {
281
+ // Create a recovery token file manually
282
+ const token_data = {
283
+ token: 'test-token-123',
284
+ expires_at: Date.now() + 600000, // 10 minutes from now
285
+ failed_attempts: 2,
286
+ locked_until: null
287
+ };
288
+ fs.writeFileSync(RECOVERY_TOKEN_FILE, JSON.stringify(token_data, null, 2));
289
+
290
+ initialize_recovery_manager();
291
+
292
+ const status = get_recovery_status();
293
+ t.true(status.token_active);
294
+ t.is(status.failed_attempts, 2);
295
+
296
+ const validation = is_token_valid('test-token-123');
297
+ t.true(validation.valid);
298
+ });
299
+
300
+ test('initialize_recovery_manager cleans up expired tokens', (t) => {
301
+ // Create an expired recovery token file
302
+ const token_data = {
303
+ token: 'expired-token-123',
304
+ expires_at: Date.now() - 1000, // 1 second ago
305
+ failed_attempts: 1,
306
+ locked_until: null
307
+ };
308
+ fs.writeFileSync(RECOVERY_TOKEN_FILE, JSON.stringify(token_data, null, 2));
309
+
310
+ initialize_recovery_manager();
311
+
312
+ const status = get_recovery_status();
313
+ t.false(status.token_active);
314
+ t.is(status.failed_attempts, 0);
315
+
316
+ // Token file should be cleaned up
317
+ t.false(fs.existsSync(RECOVERY_TOKEN_FILE));
318
+ });
319
+
320
+ test('initialize_recovery_manager cleans up expired locks', (t) => {
321
+ // Create a recovery state with expired lock
322
+ const token_data = {
323
+ token: 'test-token-123',
324
+ expires_at: Date.now() + 600000, // 10 minutes from now
325
+ failed_attempts: 3,
326
+ locked_until: Date.now() - 1000 // 1 second ago (expired)
327
+ };
328
+ fs.writeFileSync(RECOVERY_TOKEN_FILE, JSON.stringify(token_data, null, 2));
329
+
330
+ initialize_recovery_manager();
331
+
332
+ const status = get_recovery_status();
333
+ t.false(status.is_locked);
334
+ t.is(status.failed_attempts, 0); // Should be reset when lock expires
335
+ });
336
+
337
+ test('reset_recovery_state cleans up everything', (t) => {
338
+ // Create recovery token and make some failed attempts
339
+ create_recovery_token();
340
+ record_failed_recovery_attempt('192.168.1.100');
341
+
342
+ t.true(fs.existsSync(RECOVERY_TOKEN_FILE));
343
+
344
+ reset_recovery_state();
345
+
346
+ t.false(fs.existsSync(RECOVERY_TOKEN_FILE));
347
+
348
+ const status = get_recovery_status();
349
+ t.false(status.token_active);
350
+ t.is(status.failed_attempts, 0);
351
+ t.false(status.is_locked);
352
+ });
353
+
354
+ test('recovery token file has secure permissions', (t) => {
355
+ create_recovery_token();
356
+
357
+ const stats = fs.statSync(RECOVERY_TOKEN_FILE);
358
+ const mode = stats.mode & parseInt('777', 8);
359
+
360
+ // Should have 600 permissions (owner read/write only)
361
+ t.is(mode, parseInt('600', 8));
362
+ });
363
+
364
+ test('multiple recovery tokens not allowed', (t) => {
365
+ const first_token = create_recovery_token();
366
+ const second_token = create_recovery_token();
367
+
368
+ // Second token should replace the first
369
+ t.not(first_token.token, second_token.token);
370
+
371
+ // First token should no longer be valid
372
+ const first_validation = is_token_valid(first_token.token);
373
+ t.false(first_validation.valid);
374
+
375
+ // Second token should be valid
376
+ const second_validation = is_token_valid(second_token.token);
377
+ t.true(second_validation.valid);
378
+ });
379
+
380
+ test('password strength validation edge cases', async (t) => {
381
+ // Create environment variable with authentication
382
+ const settings_data = {
383
+ authentication: {
384
+ password_hash: await bcrypt.hash('old_password_123', 12),
385
+ created_at: new Date().toISOString(),
386
+ last_updated: new Date().toISOString(),
387
+ failed_attempts: {},
388
+ rate_limits: {}
389
+ }
390
+ };
391
+ process.env.JOYSTICK_DB_SETTINGS = JSON.stringify(settings_data);
392
+
393
+ // Test null password
394
+ let error = await t.throwsAsync(async () => {
395
+ await change_password(null, '192.168.1.100');
396
+ });
397
+ t.is(error.message, 'Password is required');
398
+
399
+ // Test undefined password
400
+ error = await t.throwsAsync(async () => {
401
+ await change_password(undefined, '192.168.1.100');
402
+ });
403
+ t.is(error.message, 'Password is required');
404
+
405
+ // Test non-string password
406
+ error = await t.throwsAsync(async () => {
407
+ await change_password(123456789012, '192.168.1.100');
408
+ });
409
+ t.is(error.message, 'Password is required');
410
+
411
+ // Test exactly 12 characters (should pass)
412
+ const result = await change_password('exactly12chr', '192.168.1.100');
413
+ t.true(result.success);
414
+ });
@@ -0,0 +1,202 @@
1
+ import test from 'ava';
2
+ import { get_replication_manager, initialize_replication_manager, shutdown_replication_manager } from '../../../src/server/lib/replication_manager.js';
3
+
4
+ test.beforeEach(async () => {
5
+ // Clean up any existing replication manager
6
+ await shutdown_replication_manager();
7
+ });
8
+
9
+ test.afterEach(async () => {
10
+ // Clean up after each test
11
+ await shutdown_replication_manager();
12
+ });
13
+
14
+ test('replication manager singleton', t => {
15
+ const manager1 = get_replication_manager();
16
+ const manager2 = get_replication_manager();
17
+
18
+ t.is(manager1, manager2, 'Should return same instance');
19
+ });
20
+
21
+ test('replication manager initialization without settings', t => {
22
+ const manager = get_replication_manager();
23
+
24
+ // Should not throw when no settings are available
25
+ t.notThrows(() => {
26
+ manager.initialize();
27
+ });
28
+
29
+ t.false(manager.enabled, 'Should be disabled without settings');
30
+ });
31
+
32
+ test('replication manager default configuration', t => {
33
+ const manager = get_replication_manager();
34
+
35
+ t.false(manager.enabled, 'Should be disabled by default');
36
+ t.is(manager.mode, 'async', 'Should default to async mode');
37
+ t.is(manager.timeout_ms, 5000, 'Should have default timeout');
38
+ t.is(manager.retry_attempts, 3, 'Should have default retry attempts');
39
+ t.is(manager.batch_size, 100, 'Should have default batch size');
40
+ });
41
+
42
+ test('replication manager queue operation when disabled', t => {
43
+ const manager = get_replication_manager();
44
+
45
+ // Should not throw when queuing operations while disabled
46
+ t.notThrows(() => {
47
+ manager.queue_replication('insert_one', 'test_collection', { document: { name: 'test' } });
48
+ });
49
+
50
+ t.is(manager.replication_queue.length, 0, 'Should not queue when disabled');
51
+ });
52
+
53
+ test('replication manager get status when disabled', t => {
54
+ const manager = get_replication_manager();
55
+
56
+ const status = manager.get_replication_status();
57
+
58
+ t.false(status.enabled, 'Should report as disabled');
59
+ t.is(status.connected_secondaries, 0, 'Should have no connected secondaries');
60
+ t.is(status.queue_length, 0, 'Should have empty queue');
61
+ t.false(status.processing, 'Should not be processing');
62
+ t.is(status.secondaries.length, 0, 'Should have no secondaries');
63
+ });
64
+
65
+ test('replication manager get secondary health when disabled', t => {
66
+ const manager = get_replication_manager();
67
+
68
+ const health = manager.get_secondary_health();
69
+
70
+ t.is(health.total_secondaries, 0, 'Should have no secondaries');
71
+ t.is(health.healthy_secondaries, 0, 'Should have no healthy secondaries');
72
+ t.is(health.secondaries.length, 0, 'Should have empty secondaries array');
73
+ });
74
+
75
+ test('replication manager add secondary when disabled', async t => {
76
+ const manager = get_replication_manager();
77
+
78
+ const secondary = {
79
+ id: 'test-secondary',
80
+ ip: '127.0.0.1',
81
+ port: 1984,
82
+ private_key: Buffer.from('test-key').toString('base64'),
83
+ enabled: true
84
+ };
85
+
86
+ // Should not throw but won't actually connect
87
+ const result = await manager.add_secondary(secondary);
88
+
89
+ t.true(result.success, 'Should return success');
90
+ t.is(result.secondary_id, 'test-secondary', 'Should return secondary ID');
91
+ });
92
+
93
+ test('replication manager remove non-existent secondary', t => {
94
+ const manager = get_replication_manager();
95
+
96
+ const error = t.throws(() => {
97
+ manager.remove_secondary('non-existent');
98
+ });
99
+
100
+ t.true(error.message.includes('not found'), 'Should throw not found error');
101
+ });
102
+
103
+ test('replication manager sync when disabled', async t => {
104
+ const manager = get_replication_manager();
105
+
106
+ const error = await t.throwsAsync(async () => {
107
+ await manager.sync_secondaries();
108
+ });
109
+
110
+ t.true(error.message.includes('not enabled'), 'Should throw not enabled error');
111
+ });
112
+
113
+ test('replication manager shutdown', async t => {
114
+ const manager = get_replication_manager();
115
+
116
+ // Should not throw when shutting down
117
+ await t.notThrowsAsync(async () => {
118
+ await manager.shutdown();
119
+ });
120
+
121
+ t.false(manager.enabled, 'Should be disabled after shutdown');
122
+ t.false(manager.processing_replication, 'Should not be processing after shutdown');
123
+ t.is(manager.secondary_connections.size, 0, 'Should have no connections after shutdown');
124
+ t.is(manager.replication_queue.length, 0, 'Should have empty queue after shutdown');
125
+ });
126
+
127
+ test('replication manager statistics tracking', t => {
128
+ const manager = get_replication_manager();
129
+
130
+ const initialStats = manager.stats;
131
+
132
+ t.is(initialStats.total_operations_replicated, 0, 'Should start with zero operations');
133
+ t.is(initialStats.successful_replications, 0, 'Should start with zero successful replications');
134
+ t.is(initialStats.failed_replications, 0, 'Should start with zero failed replications');
135
+ t.is(initialStats.connected_secondaries, 0, 'Should start with zero connected secondaries');
136
+ t.is(initialStats.avg_replication_latency_ms, 0, 'Should start with zero average latency');
137
+ });
138
+
139
+ test('replication manager sequence number generation', t => {
140
+ const manager = get_replication_manager();
141
+
142
+ const initialSequence = manager.sequence_number;
143
+
144
+ // Enable manager temporarily to test sequence generation
145
+ manager.enabled = true;
146
+ manager.secondary_connections.set('test', { authenticated: true });
147
+
148
+ manager.queue_replication('insert_one', 'test', { document: { name: 'test1' } });
149
+ manager.queue_replication('insert_one', 'test', { document: { name: 'test2' } });
150
+
151
+ t.is(manager.sequence_number, initialSequence + 2, 'Should increment sequence number for each operation');
152
+ t.true(manager.sequence_number > initialSequence, 'Should generate increasing sequence numbers');
153
+
154
+ // Clean up
155
+ manager.enabled = false;
156
+ manager.secondary_connections.clear();
157
+ manager.replication_queue = [];
158
+ });
159
+
160
+ test('replication manager sequence number increment', t => {
161
+ const manager = get_replication_manager();
162
+
163
+ const initialSequence = manager.sequence_number;
164
+
165
+ // Enable manager temporarily to test queue operation
166
+ manager.enabled = true;
167
+ manager.secondary_connections.set('test', { authenticated: true });
168
+
169
+ manager.queue_replication('insert_one', 'test', { document: { name: 'test' } });
170
+
171
+ t.is(manager.sequence_number, initialSequence + 1, 'Should increment sequence number');
172
+
173
+ // Clean up
174
+ manager.enabled = false;
175
+ manager.secondary_connections.clear();
176
+ manager.replication_queue = [];
177
+ });
178
+
179
+ test('replication manager batch processing preparation', t => {
180
+ const manager = get_replication_manager();
181
+
182
+ // Enable manager and add mock secondary
183
+ manager.enabled = true;
184
+ manager.secondary_connections.set('test', {
185
+ authenticated: true,
186
+ socket: { write: () => {} },
187
+ pending_operations: new Map()
188
+ });
189
+
190
+ // Add multiple operations to queue
191
+ for (let i = 0; i < 5; i++) {
192
+ manager.queue_replication('insert_one', 'test', { document: { id: i } });
193
+ }
194
+
195
+ t.is(manager.replication_queue.length, 5, 'Should queue all operations');
196
+ t.is(manager.stats.total_operations_replicated, 5, 'Should track total operations');
197
+
198
+ // Clean up
199
+ manager.enabled = false;
200
+ manager.secondary_connections.clear();
201
+ manager.replication_queue = [];
202
+ });
@@ -0,0 +1,45 @@
1
+ import test from 'ava';
2
+ import safe_json_parse from '../../../src/server/lib/safe_json_parse.js';
3
+
4
+ test('safe_json_parse parses valid JSON string', (t = {}) => {
5
+ const valid_json = '{"name": "test", "value": 123}';
6
+ const result = safe_json_parse(valid_json);
7
+
8
+ t.deepEqual(result, { name: "test", value: 123 });
9
+ });
10
+
11
+ test('safe_json_parse returns null for invalid JSON string', (t = {}) => {
12
+ const invalid_json = '{"name": "test", "value":}';
13
+ const result = safe_json_parse(invalid_json);
14
+
15
+ t.is(result, null);
16
+ });
17
+
18
+ test('safe_json_parse returns null for empty string', (t = {}) => {
19
+ const empty_string = '';
20
+ const result = safe_json_parse(empty_string);
21
+
22
+ t.is(result, null);
23
+ });
24
+
25
+ test('safe_json_parse returns null for non-string input', (t = {}) => {
26
+ const non_string = undefined;
27
+ const result = safe_json_parse(non_string);
28
+
29
+ t.is(result, null);
30
+ });
31
+
32
+ test('safe_json_parse parses array JSON', (t = {}) => {
33
+ const array_json = '[1, 2, 3]';
34
+ const result = safe_json_parse(array_json);
35
+
36
+ t.deepEqual(result, [1, 2, 3]);
37
+ });
38
+
39
+ test('safe_json_parse parses primitive JSON values', (t = {}) => {
40
+ t.is(safe_json_parse('true'), true);
41
+ t.is(safe_json_parse('false'), false);
42
+ t.is(safe_json_parse('null'), null);
43
+ t.is(safe_json_parse('42'), 42);
44
+ t.is(safe_json_parse('"hello"'), "hello");
45
+ });