@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,1232 @@
1
+ import test from 'ava';
2
+ import { create_server } from '../../src/server/index.js';
3
+ import joystickdb from '../../src/client/index.js';
4
+ import { writeFileSync, unlinkSync, existsSync } from 'fs';
5
+ import { reset_auth_state } from '../../src/server/lib/auth_manager.js';
6
+
7
+ let server;
8
+ let client;
9
+
10
+ // NOTE: Simple port allocation using a fixed range to avoid conflicts.
11
+ let current_port = 2000;
12
+ const get_next_port = () => {
13
+ return ++current_port;
14
+ };
15
+
16
+ test.beforeEach(async () => {
17
+ await reset_auth_state();
18
+
19
+ // Clean up database files to ensure test isolation
20
+ try {
21
+ if (existsSync('./data/data.mdb')) {
22
+ unlinkSync('./data/data.mdb');
23
+ }
24
+ if (existsSync('./data/lock.mdb')) {
25
+ unlinkSync('./data/lock.mdb');
26
+ }
27
+ } catch (error) {
28
+ // Ignore cleanup errors
29
+ }
30
+ });
31
+
32
+ test.afterEach(async () => {
33
+ if (client) {
34
+ client.disconnect();
35
+ client = null;
36
+ }
37
+
38
+ if (server) {
39
+ await server.cleanup();
40
+ server.close();
41
+ server = null;
42
+ }
43
+
44
+ await reset_auth_state();
45
+ });
46
+
47
+ test('client can be created with custom options', (t) => {
48
+ client = joystickdb.client({
49
+ host: '127.0.0.1',
50
+ port: 9999,
51
+ timeout: 10000,
52
+ reconnect: false,
53
+ max_reconnect_attempts: 5,
54
+ reconnect_delay: 2000,
55
+ password: 'test_password',
56
+ auto_connect: false
57
+ });
58
+
59
+ t.is(client.host, '127.0.0.1');
60
+ t.is(client.port, 9999);
61
+ t.is(client.timeout, 10000);
62
+ t.is(client.reconnect, false);
63
+ t.is(client.max_reconnect_attempts, 5);
64
+ t.is(client.reconnect_delay, 2000);
65
+ t.is(client.password, 'test_password');
66
+ });
67
+
68
+ test('client can setup authentication', async (t) => {
69
+ const test_port = get_next_port();
70
+ server = await create_server();
71
+
72
+ await new Promise((resolve) => {
73
+ server.listen(test_port, () => {
74
+ setTimeout(resolve, 100);
75
+ });
76
+ });
77
+
78
+ client = joystickdb.client({
79
+ port: test_port,
80
+ reconnect: false
81
+ });
82
+
83
+ await new Promise((resolve) => {
84
+ client.on('connect', resolve);
85
+ });
86
+
87
+ const result = await client.setup();
88
+
89
+ t.is(result.ok, 1);
90
+ t.truthy(result.password);
91
+ t.truthy(result.message);
92
+ });
93
+
94
+ test('client can authenticate with valid password', async (t) => {
95
+ const test_port = get_next_port();
96
+ server = await create_server();
97
+
98
+ await new Promise((resolve) => {
99
+ server.listen(test_port, () => {
100
+ setTimeout(resolve, 100);
101
+ });
102
+ });
103
+
104
+ const setup_client = joystickdb.client({
105
+ port: test_port,
106
+ reconnect: false
107
+ });
108
+
109
+ await new Promise((resolve) => {
110
+ setup_client.on('connect', resolve);
111
+ });
112
+
113
+ const setup_result = await setup_client.setup();
114
+ setup_client.disconnect();
115
+
116
+ client = joystickdb.client({
117
+ port: test_port,
118
+ reconnect: false,
119
+ password: setup_result.password
120
+ });
121
+
122
+ await new Promise((resolve) => {
123
+ client.on('authenticated', resolve);
124
+ });
125
+
126
+ t.is(client.is_authenticated, true);
127
+ });
128
+
129
+ test('client authentication fails with invalid password', async (t) => {
130
+ const test_port = get_next_port();
131
+ server = await create_server();
132
+
133
+ await new Promise((resolve) => {
134
+ server.listen(test_port, () => {
135
+ setTimeout(resolve, 100);
136
+ });
137
+ });
138
+
139
+ const setup_client = joystickdb.client({
140
+ port: test_port,
141
+ reconnect: false
142
+ });
143
+
144
+ await new Promise((resolve) => {
145
+ setup_client.on('connect', resolve);
146
+ });
147
+
148
+ await setup_client.setup();
149
+ setup_client.disconnect();
150
+
151
+ client = joystickdb.client({
152
+ port: test_port,
153
+ reconnect: false,
154
+ password: 'invalid_password'
155
+ });
156
+
157
+ const error = await new Promise((resolve) => {
158
+ client.on('error', resolve);
159
+ });
160
+
161
+ t.truthy(error.message.includes('Authentication'));
162
+ });
163
+
164
+ test('client can perform ping operation', async (t) => {
165
+ const test_port = get_next_port();
166
+ server = await create_server();
167
+
168
+ await new Promise((resolve) => {
169
+ server.listen(test_port, () => {
170
+ setTimeout(resolve, 100);
171
+ });
172
+ });
173
+
174
+ client = joystickdb.client({
175
+ port: test_port,
176
+ reconnect: false
177
+ });
178
+
179
+ await new Promise((resolve) => {
180
+ client.on('connect', resolve);
181
+ });
182
+
183
+ const result = await client.ping();
184
+
185
+ t.is(result.ok, 1);
186
+ });
187
+
188
+ test('client can perform CRUD operations', async (t) => {
189
+ const test_port = get_next_port();
190
+ server = await create_server();
191
+
192
+ await new Promise((resolve) => {
193
+ server.listen(test_port, () => {
194
+ setTimeout(resolve, 100);
195
+ });
196
+ });
197
+
198
+ const setup_client = joystickdb.client({
199
+ port: test_port,
200
+ reconnect: false
201
+ });
202
+
203
+ await new Promise((resolve) => {
204
+ setup_client.on('connect', resolve);
205
+ });
206
+
207
+ const setup_result = await setup_client.setup();
208
+ setup_client.disconnect();
209
+
210
+ client = joystickdb.client({
211
+ port: test_port,
212
+ reconnect: false,
213
+ password: setup_result.password
214
+ });
215
+
216
+ await new Promise((resolve) => {
217
+ client.on('authenticated', resolve);
218
+ });
219
+
220
+ const insert_result = await client.db('default').collection('test_collection').insert_one({
221
+ name: 'John Doe',
222
+ age: 30
223
+ });
224
+
225
+ t.is(insert_result.ok, 1);
226
+ t.truthy(insert_result.inserted_id);
227
+
228
+ const find_result = await client.db('default').collection('test_collection').find_one({
229
+ name: 'John Doe'
230
+ });
231
+
232
+ t.is(find_result.name, 'John Doe');
233
+ t.is(find_result.age, 30);
234
+
235
+ const update_result = await client.db('default').collection('test_collection').update_one({
236
+ name: 'John Doe'
237
+ }, {
238
+ $set: { age: 31 }
239
+ });
240
+
241
+ t.is(update_result.ok, 1);
242
+ t.is(update_result.modified_count, 1);
243
+
244
+ const updated_doc = await client.db('default').collection('test_collection').find_one({
245
+ name: 'John Doe'
246
+ });
247
+
248
+ t.is(updated_doc.age, 31);
249
+
250
+ const delete_result = await client.db('default').collection('test_collection').delete_one({
251
+ name: 'John Doe'
252
+ });
253
+
254
+ t.is(delete_result.ok, 1);
255
+ t.is(delete_result.deleted_count, 1);
256
+
257
+ const deleted_doc = await client.db('default').collection('test_collection').find_one({
258
+ name: 'John Doe'
259
+ });
260
+
261
+ t.is(deleted_doc, null);
262
+ });
263
+
264
+ test('client can perform find operation with multiple results', async (t) => {
265
+ const test_port = get_next_port();
266
+ server = await create_server();
267
+
268
+ await new Promise((resolve) => {
269
+ server.listen(test_port, () => {
270
+ setTimeout(resolve, 100);
271
+ });
272
+ });
273
+
274
+ const setup_client = joystickdb.client({
275
+ port: test_port,
276
+ reconnect: false
277
+ });
278
+
279
+ await new Promise((resolve) => {
280
+ setup_client.on('connect', resolve);
281
+ });
282
+
283
+ const setup_result = await setup_client.setup();
284
+ setup_client.disconnect();
285
+
286
+ client = joystickdb.client({
287
+ port: test_port,
288
+ reconnect: false,
289
+ password: setup_result.password
290
+ });
291
+
292
+ await new Promise((resolve) => {
293
+ client.on('authenticated', resolve);
294
+ });
295
+
296
+ await client.db('default').collection('test_collection').insert_one({ category: 'test', value: 1 });
297
+ await client.db('default').collection('test_collection').insert_one({ category: 'test', value: 2 });
298
+ await client.db('default').collection('test_collection').insert_one({ category: 'test', value: 3 });
299
+
300
+ const find_result = await client.db('default').collection('test_collection').find({
301
+ category: 'test'
302
+ });
303
+
304
+ t.truthy(find_result);
305
+ t.is(find_result.length, 3);
306
+ t.truthy(find_result.find(doc => doc.value === 1));
307
+ t.truthy(find_result.find(doc => doc.value === 2));
308
+ t.truthy(find_result.find(doc => doc.value === 3));
309
+ });
310
+
311
+ test('client can perform bulk write operations', async (t) => {
312
+ const test_port = get_next_port();
313
+ server = await create_server();
314
+
315
+ await new Promise((resolve) => {
316
+ server.listen(test_port, () => {
317
+ setTimeout(resolve, 100);
318
+ });
319
+ });
320
+
321
+ const setup_client = joystickdb.client({
322
+ port: test_port,
323
+ reconnect: false
324
+ });
325
+
326
+ await new Promise((resolve) => {
327
+ setup_client.on('connect', resolve);
328
+ });
329
+
330
+ const setup_result = await setup_client.setup();
331
+ setup_client.disconnect();
332
+
333
+ client = joystickdb.client({
334
+ port: test_port,
335
+ reconnect: false,
336
+ password: setup_result.password
337
+ });
338
+
339
+ await new Promise((resolve) => {
340
+ client.on('authenticated', resolve);
341
+ });
342
+
343
+ const bulk_operations = [
344
+ { insert_one: { document: { name: 'Alice', age: 25 } } },
345
+ { insert_one: { document: { name: 'Bob', age: 30 } } },
346
+ { insert_one: { document: { name: 'Charlie', age: 35 } } }
347
+ ];
348
+
349
+ const bulk_result = await client.db('default').collection('test_collection').bulk_write(bulk_operations);
350
+
351
+ t.is(bulk_result.ok, 1);
352
+ t.is(bulk_result.inserted_count, 3);
353
+
354
+ const find_result = await client.db('default').collection('test_collection').find({});
355
+ t.truthy(find_result);
356
+ t.is(find_result.length, 3);
357
+ });
358
+
359
+ test('client can perform index operations', async (t) => {
360
+ const test_port = get_next_port();
361
+ server = await create_server();
362
+
363
+ await new Promise((resolve) => {
364
+ server.listen(test_port, () => {
365
+ setTimeout(resolve, 100);
366
+ });
367
+ });
368
+
369
+ const setup_client = joystickdb.client({
370
+ port: test_port,
371
+ reconnect: false
372
+ });
373
+
374
+ await new Promise((resolve) => {
375
+ setup_client.on('connect', resolve);
376
+ });
377
+
378
+ const setup_result = await setup_client.setup();
379
+ setup_client.disconnect();
380
+
381
+ client = joystickdb.client({
382
+ port: test_port,
383
+ reconnect: false,
384
+ password: setup_result.password
385
+ });
386
+
387
+ await new Promise((resolve) => {
388
+ client.on('authenticated', resolve);
389
+ });
390
+
391
+ const create_index_result = await client.db('default').collection('test_collection').create_index('name');
392
+
393
+ t.is(create_index_result.ok, 1);
394
+ t.truthy(create_index_result.message);
395
+
396
+ const get_indexes_result = await client.db('default').collection('test_collection').get_indexes();
397
+
398
+ t.is(get_indexes_result.ok, 1);
399
+ t.truthy(get_indexes_result.indexes.find(index => index.field === 'name'));
400
+
401
+ const drop_index_result = await client.db('default').collection('test_collection').drop_index('name');
402
+
403
+ t.is(drop_index_result.ok, 1);
404
+ t.truthy(drop_index_result.message);
405
+ });
406
+
407
+ test('client can perform admin operations', async (t) => {
408
+ const test_port = get_next_port();
409
+ server = await create_server();
410
+
411
+ await new Promise((resolve) => {
412
+ server.listen(test_port, () => {
413
+ setTimeout(resolve, 100);
414
+ });
415
+ });
416
+
417
+ const setup_client = joystickdb.client({
418
+ port: test_port,
419
+ reconnect: false
420
+ });
421
+
422
+ await new Promise((resolve) => {
423
+ setup_client.on('connect', resolve);
424
+ });
425
+
426
+ const setup_result = await setup_client.setup();
427
+ setup_client.disconnect();
428
+
429
+ client = joystickdb.client({
430
+ port: test_port,
431
+ reconnect: false,
432
+ password: setup_result.password
433
+ });
434
+
435
+ await new Promise((resolve) => {
436
+ client.on('authenticated', resolve);
437
+ });
438
+
439
+ await client.db('default').collection('admin_test').insert_one({ test: 'data' });
440
+
441
+ const database = client.db('default');
442
+
443
+ const stats_result = await database.get_stats();
444
+ t.is(stats_result.ok, 1);
445
+ t.truthy(stats_result.database);
446
+
447
+ const collections_result = await database.list_collections();
448
+ t.is(collections_result.ok, 1);
449
+ t.truthy(collections_result.collections.some(col => col.name === 'admin_test'));
450
+
451
+ const documents_result = await database.list_documents('admin_test');
452
+ t.is(documents_result.ok, 1);
453
+ t.is(documents_result.documents.length, 1);
454
+
455
+ const document_id = documents_result.documents[0]._id;
456
+ const get_doc_result = await database.get_document('admin_test', document_id);
457
+ t.is(get_doc_result.ok, 1);
458
+ t.is(get_doc_result.document.test, 'data');
459
+
460
+ const query_result = await database.query_documents('admin_test', { test: 'data' });
461
+ t.is(query_result.ok, 1);
462
+ t.is(query_result.documents.length, 1);
463
+
464
+ const insert_doc_result = await database.insert_document('admin_test', { admin: 'insert' });
465
+ t.is(insert_doc_result.ok, 1);
466
+
467
+ const update_doc_result = await database.update_document('admin_test', document_id, { $set: { updated: true } });
468
+ t.is(update_doc_result.ok, 1);
469
+
470
+ const delete_doc_result = await database.delete_document('admin_test', document_id);
471
+ t.is(delete_doc_result.ok, 1);
472
+ });
473
+
474
+ test('client handles request timeout', async (t) => {
475
+ const test_port = get_next_port();
476
+ server = await create_server();
477
+
478
+ await new Promise((resolve) => {
479
+ server.listen(test_port, () => {
480
+ setTimeout(resolve, 100);
481
+ });
482
+ });
483
+
484
+ client = joystickdb.client({
485
+ port: test_port,
486
+ timeout: 100,
487
+ reconnect: false
488
+ });
489
+
490
+ await new Promise((resolve) => {
491
+ client.on('connect', resolve);
492
+ });
493
+
494
+ client.socket.write = () => {};
495
+
496
+ const error = await t.throwsAsync(async () => {
497
+ await client.ping();
498
+ });
499
+
500
+ t.is(error.message, 'Request timeout');
501
+ });
502
+
503
+ test('client queues requests when not authenticated', async (t) => {
504
+ const test_port = get_next_port();
505
+ server = await create_server();
506
+
507
+ await new Promise((resolve) => {
508
+ server.listen(test_port, () => {
509
+ setTimeout(resolve, 100);
510
+ });
511
+ });
512
+
513
+ const setup_client = joystickdb.client({
514
+ port: test_port,
515
+ reconnect: false
516
+ });
517
+
518
+ await new Promise((resolve) => {
519
+ setup_client.on('connect', resolve);
520
+ });
521
+
522
+ const setup_result = await setup_client.setup();
523
+ setup_client.disconnect();
524
+
525
+ client = joystickdb.client({
526
+ port: test_port,
527
+ reconnect: false,
528
+ password: setup_result.password
529
+ });
530
+
531
+ const insert_promise = client.db('default').collection('queue_test').insert_one({ queued: true });
532
+
533
+ t.is(client.request_queue.length, 1);
534
+
535
+ await new Promise((resolve) => {
536
+ client.on('authenticated', resolve);
537
+ });
538
+
539
+ const result = await insert_promise;
540
+
541
+ t.is(result.ok, 1);
542
+ t.is(client.request_queue.length, 0);
543
+ });
544
+
545
+ test('client handles connection errors gracefully', async (t) => {
546
+ client = joystickdb.client({
547
+ port: 9999,
548
+ reconnect: false,
549
+ timeout: 1000
550
+ });
551
+
552
+ const error = await new Promise((resolve) => {
553
+ client.on('error', resolve);
554
+ });
555
+
556
+ t.truthy(error);
557
+ });
558
+
559
+ test('client can reload server configuration', async (t) => {
560
+ const test_port = get_next_port();
561
+ server = await create_server();
562
+
563
+ await new Promise((resolve) => {
564
+ server.listen(test_port, () => {
565
+ setTimeout(resolve, 100);
566
+ });
567
+ });
568
+
569
+ const setup_client = joystickdb.client({
570
+ port: test_port,
571
+ reconnect: false
572
+ });
573
+
574
+ await new Promise((resolve) => {
575
+ setup_client.on('connect', resolve);
576
+ });
577
+
578
+ const setup_result = await setup_client.setup();
579
+ setup_client.disconnect();
580
+
581
+ client = joystickdb.client({
582
+ port: test_port,
583
+ reconnect: false,
584
+ password: setup_result.password
585
+ });
586
+
587
+ await new Promise((resolve) => {
588
+ client.on('authenticated', resolve);
589
+ });
590
+
591
+ const reload_result = await client.reload();
592
+
593
+ t.is(reload_result.ok, 1);
594
+ t.truthy(reload_result.message);
595
+ });
596
+
597
+ // NOTE: Collection chaining API tests.
598
+ test('client.collection() returns a collection interface', async (t) => {
599
+ const test_port = get_next_port();
600
+ server = await create_server();
601
+
602
+ await new Promise((resolve) => {
603
+ server.listen(test_port, () => {
604
+ setTimeout(resolve, 100);
605
+ });
606
+ });
607
+
608
+ const setup_client = joystickdb.client({
609
+ port: test_port,
610
+ reconnect: false
611
+ });
612
+
613
+ await new Promise((resolve) => {
614
+ setup_client.on('connect', resolve);
615
+ });
616
+
617
+ const setup_result = await setup_client.setup();
618
+ setup_client.disconnect();
619
+
620
+ client = joystickdb.client({
621
+ port: test_port,
622
+ reconnect: false,
623
+ password: setup_result.password
624
+ });
625
+
626
+ await new Promise((resolve) => {
627
+ client.on('authenticated', resolve);
628
+ });
629
+
630
+ const collection = client.db('default').collection('test_collection');
631
+
632
+ t.truthy(collection);
633
+ t.is(collection.collection_name, 'test_collection');
634
+ t.is(collection.client, client);
635
+ t.is(typeof collection.insert_one, 'function');
636
+ t.is(typeof collection.find_one, 'function');
637
+ t.is(typeof collection.find, 'function');
638
+ t.is(typeof collection.update_one, 'function');
639
+ t.is(typeof collection.delete_one, 'function');
640
+ t.is(typeof collection.bulk_write, 'function');
641
+ t.is(typeof collection.create_index, 'function');
642
+ t.is(typeof collection.drop_index, 'function');
643
+ t.is(typeof collection.get_indexes, 'function');
644
+ });
645
+
646
+ test('collection chaining API can perform CRUD operations', async (t) => {
647
+ const test_port = get_next_port();
648
+ server = await create_server();
649
+
650
+ await new Promise((resolve) => {
651
+ server.listen(test_port, () => {
652
+ setTimeout(resolve, 100);
653
+ });
654
+ });
655
+
656
+ const setup_client = joystickdb.client({
657
+ port: test_port,
658
+ reconnect: false
659
+ });
660
+
661
+ await new Promise((resolve) => {
662
+ setup_client.on('connect', resolve);
663
+ });
664
+
665
+ const setup_result = await setup_client.setup();
666
+ setup_client.disconnect();
667
+
668
+ client = joystickdb.client({
669
+ port: test_port,
670
+ reconnect: false,
671
+ password: setup_result.password
672
+ });
673
+
674
+ await new Promise((resolve) => {
675
+ client.on('authenticated', resolve);
676
+ });
677
+
678
+ const collection = client.db('default').collection('chain_test');
679
+
680
+ const insert_result = await collection.insert_one({
681
+ name: 'Jane Doe',
682
+ age: 25
683
+ });
684
+
685
+ t.is(insert_result.ok, 1);
686
+ t.truthy(insert_result.inserted_id);
687
+
688
+ const find_result = await collection.find_one({
689
+ name: 'Jane Doe'
690
+ });
691
+
692
+ t.is(find_result.name, 'Jane Doe');
693
+ t.is(find_result.age, 25);
694
+
695
+ const update_result = await collection.update_one({
696
+ name: 'Jane Doe'
697
+ }, {
698
+ $set: { age: 26 }
699
+ });
700
+
701
+ t.is(update_result.ok, 1);
702
+ t.is(update_result.modified_count, 1);
703
+
704
+ const updated_doc = await collection.find_one({
705
+ name: 'Jane Doe'
706
+ });
707
+
708
+ t.is(updated_doc.age, 26);
709
+
710
+ const delete_result = await collection.delete_one({
711
+ name: 'Jane Doe'
712
+ });
713
+
714
+ t.is(delete_result.ok, 1);
715
+ t.is(delete_result.deleted_count, 1);
716
+
717
+ const deleted_doc = await collection.find_one({
718
+ name: 'Jane Doe'
719
+ });
720
+
721
+ t.is(deleted_doc, null);
722
+ });
723
+
724
+ test('collection chaining API can perform find operation with multiple results', async (t) => {
725
+ const test_port = get_next_port();
726
+ server = await create_server();
727
+
728
+ await new Promise((resolve) => {
729
+ server.listen(test_port, () => {
730
+ setTimeout(resolve, 100);
731
+ });
732
+ });
733
+
734
+ const setup_client = joystickdb.client({
735
+ port: test_port,
736
+ reconnect: false
737
+ });
738
+
739
+ await new Promise((resolve) => {
740
+ setup_client.on('connect', resolve);
741
+ });
742
+
743
+ const setup_result = await setup_client.setup();
744
+ setup_client.disconnect();
745
+
746
+ client = joystickdb.client({
747
+ port: test_port,
748
+ reconnect: false,
749
+ password: setup_result.password
750
+ });
751
+
752
+ await new Promise((resolve) => {
753
+ client.on('authenticated', resolve);
754
+ });
755
+
756
+ const collection = client.db('default').collection('chain_find_test');
757
+
758
+ await collection.insert_one({ category: 'chain_test', value: 10 });
759
+ await collection.insert_one({ category: 'chain_test', value: 20 });
760
+ await collection.insert_one({ category: 'chain_test', value: 30 });
761
+
762
+ const find_result = await collection.find({
763
+ category: 'chain_test'
764
+ });
765
+
766
+ t.truthy(find_result);
767
+ t.is(find_result.length, 3);
768
+ t.truthy(find_result.find(doc => doc.value === 10));
769
+ t.truthy(find_result.find(doc => doc.value === 20));
770
+ t.truthy(find_result.find(doc => doc.value === 30));
771
+ });
772
+
773
+ test('collection chaining API can perform bulk write operations', async (t) => {
774
+ const test_port = get_next_port();
775
+ server = await create_server();
776
+
777
+ await new Promise((resolve) => {
778
+ server.listen(test_port, () => {
779
+ setTimeout(resolve, 100);
780
+ });
781
+ });
782
+
783
+ const setup_client = joystickdb.client({
784
+ port: test_port,
785
+ reconnect: false
786
+ });
787
+
788
+ await new Promise((resolve) => {
789
+ setup_client.on('connect', resolve);
790
+ });
791
+
792
+ const setup_result = await setup_client.setup();
793
+ setup_client.disconnect();
794
+
795
+ client = joystickdb.client({
796
+ port: test_port,
797
+ reconnect: false,
798
+ password: setup_result.password
799
+ });
800
+
801
+ await new Promise((resolve) => {
802
+ client.on('authenticated', resolve);
803
+ });
804
+
805
+ const collection = client.db('default').collection('chain_bulk_test');
806
+
807
+ const bulk_operations = [
808
+ { insert_one: { document: { name: 'Alice', age: 25 } } },
809
+ { insert_one: { document: { name: 'Bob', age: 30 } } },
810
+ { insert_one: { document: { name: 'Charlie', age: 35 } } }
811
+ ];
812
+
813
+ const bulk_result = await collection.bulk_write(bulk_operations);
814
+
815
+ t.is(bulk_result.ok, 1);
816
+ t.is(bulk_result.inserted_count, 3);
817
+
818
+ const find_result = await collection.find({});
819
+ t.truthy(find_result);
820
+ t.is(find_result.length, 3);
821
+ });
822
+
823
+ test('collection chaining API can perform index operations', async (t) => {
824
+ const test_port = get_next_port();
825
+ server = await create_server();
826
+
827
+ await new Promise((resolve) => {
828
+ server.listen(test_port, () => {
829
+ setTimeout(resolve, 100);
830
+ });
831
+ });
832
+
833
+ const setup_client = joystickdb.client({
834
+ port: test_port,
835
+ reconnect: false
836
+ });
837
+
838
+ await new Promise((resolve) => {
839
+ setup_client.on('connect', resolve);
840
+ });
841
+
842
+ const setup_result = await setup_client.setup();
843
+ setup_client.disconnect();
844
+
845
+ client = joystickdb.client({
846
+ port: test_port,
847
+ reconnect: false,
848
+ password: setup_result.password
849
+ });
850
+
851
+ await new Promise((resolve) => {
852
+ client.on('authenticated', resolve);
853
+ });
854
+
855
+ const collection = client.db('default').collection('chain_index_test');
856
+
857
+ const create_index_result = await collection.create_index('email');
858
+
859
+ t.is(create_index_result.ok, 1);
860
+ t.truthy(create_index_result.message);
861
+
862
+ const get_indexes_result = await collection.get_indexes();
863
+
864
+ t.is(get_indexes_result.ok, 1);
865
+ t.truthy(get_indexes_result.indexes.find(index => index.field === 'email'));
866
+
867
+ const drop_index_result = await collection.drop_index('email');
868
+
869
+ t.is(drop_index_result.ok, 1);
870
+ t.truthy(drop_index_result.message);
871
+ });
872
+
873
+ test('client can perform upsert_index operations', async (t) => {
874
+ const test_port = get_next_port();
875
+ server = await create_server();
876
+
877
+ await new Promise((resolve) => {
878
+ server.listen(test_port, () => {
879
+ setTimeout(resolve, 100);
880
+ });
881
+ });
882
+
883
+ const setup_client = joystickdb.client({
884
+ port: test_port,
885
+ reconnect: false
886
+ });
887
+
888
+ await new Promise((resolve) => {
889
+ setup_client.on('connect', resolve);
890
+ });
891
+
892
+ const setup_result = await setup_client.setup();
893
+ setup_client.disconnect();
894
+
895
+ client = joystickdb.client({
896
+ port: test_port,
897
+ reconnect: false,
898
+ password: setup_result.password
899
+ });
900
+
901
+ await new Promise((resolve) => {
902
+ client.on('authenticated', resolve);
903
+ });
904
+
905
+ // Test upsert creating new index
906
+ const create_result = await client.db('default').collection('upsert_test').upsert_index('email');
907
+
908
+ t.is(create_result.ok, 1);
909
+ t.is(create_result.operation_type, 'created');
910
+ t.truthy(create_result.message.includes('created'));
911
+
912
+ // Test upsert with same options (should be unchanged)
913
+ const unchanged_result = await client.db('default').collection('upsert_test').upsert_index('email');
914
+
915
+ t.is(unchanged_result.ok, 1);
916
+ t.is(unchanged_result.operation_type, 'unchanged');
917
+ t.truthy(unchanged_result.message.includes('already exists'));
918
+
919
+ // Test upsert with different options (should update)
920
+ const update_result = await client.db('default').collection('upsert_test').upsert_index('email', { unique: true });
921
+
922
+ t.is(update_result.ok, 1);
923
+ t.is(update_result.operation_type, 'updated');
924
+ t.truthy(update_result.message.includes('updated'));
925
+
926
+ // Verify the index was updated
927
+ const get_indexes_result = await client.db('default').collection('upsert_test').get_indexes();
928
+
929
+ t.is(get_indexes_result.ok, 1);
930
+ const email_index = get_indexes_result.indexes.find(index => index.field === 'email');
931
+ t.truthy(email_index);
932
+ t.is(email_index.unique, true);
933
+ });
934
+
935
+ test('collection chaining API can perform upsert_index operations', async (t) => {
936
+ const test_port = get_next_port();
937
+ server = await create_server();
938
+
939
+ await new Promise((resolve) => {
940
+ server.listen(test_port, () => {
941
+ setTimeout(resolve, 100);
942
+ });
943
+ });
944
+
945
+ const setup_client = joystickdb.client({
946
+ port: test_port,
947
+ reconnect: false
948
+ });
949
+
950
+ await new Promise((resolve) => {
951
+ setup_client.on('connect', resolve);
952
+ });
953
+
954
+ const setup_result = await setup_client.setup();
955
+ setup_client.disconnect();
956
+
957
+ client = joystickdb.client({
958
+ port: test_port,
959
+ reconnect: false,
960
+ password: setup_result.password
961
+ });
962
+
963
+ await new Promise((resolve) => {
964
+ client.on('authenticated', resolve);
965
+ });
966
+
967
+ const collection = client.db('default').collection('chain_upsert_test');
968
+
969
+ // Test upsert creating new index
970
+ const create_result = await collection.upsert_index('name');
971
+
972
+ t.is(create_result.ok, 1);
973
+ t.is(create_result.operation_type, 'created');
974
+ t.truthy(create_result.message.includes('created'));
975
+
976
+ // Test upsert with same options (should be unchanged)
977
+ const unchanged_result = await collection.upsert_index('name');
978
+
979
+ t.is(unchanged_result.ok, 1);
980
+ t.is(unchanged_result.operation_type, 'unchanged');
981
+ t.truthy(unchanged_result.message.includes('already exists'));
982
+
983
+ // Test upsert with different options (should update)
984
+ const update_result = await collection.upsert_index('name', { sparse: true });
985
+
986
+ t.is(update_result.ok, 1);
987
+ t.is(update_result.operation_type, 'updated');
988
+ t.truthy(update_result.message.includes('updated'));
989
+
990
+ // Verify the index was updated
991
+ const get_indexes_result = await collection.get_indexes();
992
+
993
+ t.is(get_indexes_result.ok, 1);
994
+ const name_index = get_indexes_result.indexes.find(index => index.field === 'name');
995
+ t.truthy(name_index);
996
+ t.is(name_index.sparse, true);
997
+ });
998
+
999
+ test('upsert_index handles unique constraint violations properly', async (t) => {
1000
+ const test_port = get_next_port();
1001
+ server = await create_server();
1002
+
1003
+ await new Promise((resolve) => {
1004
+ server.listen(test_port, () => {
1005
+ setTimeout(resolve, 100);
1006
+ });
1007
+ });
1008
+
1009
+ const setup_client = joystickdb.client({
1010
+ port: test_port,
1011
+ reconnect: false
1012
+ });
1013
+
1014
+ await new Promise((resolve) => {
1015
+ setup_client.on('connect', resolve);
1016
+ });
1017
+
1018
+ const setup_result = await setup_client.setup();
1019
+ setup_client.disconnect();
1020
+
1021
+ client = joystickdb.client({
1022
+ port: test_port,
1023
+ reconnect: false,
1024
+ password: setup_result.password
1025
+ });
1026
+
1027
+ await new Promise((resolve) => {
1028
+ client.on('authenticated', resolve);
1029
+ });
1030
+
1031
+ const collection = client.db('default').collection('unique_constraint_test');
1032
+
1033
+ // Insert documents with duplicate values
1034
+ await collection.insert_one({ email: 'duplicate@example.com', name: 'User 1' });
1035
+ await collection.insert_one({ email: 'duplicate@example.com', name: 'User 2' });
1036
+
1037
+ // Create non-unique index first
1038
+ const create_result = await collection.upsert_index('email', { unique: false });
1039
+ t.is(create_result.ok, 1);
1040
+ t.is(create_result.operation_type, 'created');
1041
+
1042
+ // Try to update to unique index - should fail due to duplicates
1043
+ const error = await t.throwsAsync(async () => {
1044
+ await collection.upsert_index('email', { unique: true });
1045
+ });
1046
+
1047
+ t.truthy(error.message.includes('Duplicate value for unique index'));
1048
+ });
1049
+
1050
+ test('collection chaining API works with options', async (t) => {
1051
+ const test_port = get_next_port();
1052
+ server = await create_server();
1053
+
1054
+ await new Promise((resolve) => {
1055
+ server.listen(test_port, () => {
1056
+ setTimeout(resolve, 100);
1057
+ });
1058
+ });
1059
+
1060
+ const setup_client = joystickdb.client({
1061
+ port: test_port,
1062
+ reconnect: false
1063
+ });
1064
+
1065
+ await new Promise((resolve) => {
1066
+ setup_client.on('connect', resolve);
1067
+ });
1068
+
1069
+ const setup_result = await setup_client.setup();
1070
+ setup_client.disconnect();
1071
+
1072
+ client = joystickdb.client({
1073
+ port: test_port,
1074
+ reconnect: false,
1075
+ password: setup_result.password
1076
+ });
1077
+
1078
+ await new Promise((resolve) => {
1079
+ client.on('authenticated', resolve);
1080
+ });
1081
+
1082
+ const collection = client.db('default').collection('chain_options_test');
1083
+
1084
+ const insert_result = await collection.insert_one({
1085
+ name: 'Test User',
1086
+ email: 'test@example.com'
1087
+ }, {
1088
+ validate: true
1089
+ });
1090
+
1091
+ t.is(insert_result.ok, 1);
1092
+ t.truthy(insert_result.inserted_id);
1093
+
1094
+ const find_result = await collection.find_one({
1095
+ name: 'Test User'
1096
+ }, {
1097
+ projection: { name: 1 }
1098
+ });
1099
+
1100
+ t.is(find_result.name, 'Test User');
1101
+
1102
+ const update_result = await collection.update_one({
1103
+ name: 'Test User'
1104
+ }, {
1105
+ $set: { updated: true }
1106
+ }, {
1107
+ upsert: false
1108
+ });
1109
+
1110
+ t.is(update_result.ok, 1);
1111
+ t.is(update_result.modified_count, 1);
1112
+
1113
+ const delete_result = await collection.delete_one({
1114
+ name: 'Test User'
1115
+ }, {
1116
+ limit: 1
1117
+ });
1118
+
1119
+ t.is(delete_result.ok, 1);
1120
+ t.is(delete_result.deleted_count, 1);
1121
+ });
1122
+
1123
+ test('both old and new API styles work simultaneously', async (t) => {
1124
+ const test_port = get_next_port();
1125
+ server = await create_server();
1126
+
1127
+ await new Promise((resolve) => {
1128
+ server.listen(test_port, () => {
1129
+ setTimeout(resolve, 100);
1130
+ });
1131
+ });
1132
+
1133
+ const setup_client = joystickdb.client({
1134
+ port: test_port,
1135
+ reconnect: false
1136
+ });
1137
+
1138
+ await new Promise((resolve) => {
1139
+ setup_client.on('connect', resolve);
1140
+ });
1141
+
1142
+ const setup_result = await setup_client.setup();
1143
+ setup_client.disconnect();
1144
+
1145
+ client = joystickdb.client({
1146
+ port: test_port,
1147
+ reconnect: false,
1148
+ password: setup_result.password
1149
+ });
1150
+
1151
+ await new Promise((resolve) => {
1152
+ client.on('authenticated', resolve);
1153
+ });
1154
+
1155
+ // Use database API style
1156
+ const db_insert_result = await client.db('default').collection('mixed_test').insert_one({
1157
+ method: 'db_api',
1158
+ value: 100
1159
+ });
1160
+
1161
+ t.is(db_insert_result.ok, 1);
1162
+ t.truthy(db_insert_result.inserted_id);
1163
+
1164
+ // Use collection chaining API style
1165
+ const collection = client.db('default').collection('mixed_test');
1166
+ const collection_insert_result = await collection.insert_one({
1167
+ method: 'collection_api',
1168
+ value: 200
1169
+ });
1170
+
1171
+ t.is(collection_insert_result.ok, 1);
1172
+ t.truthy(collection_insert_result.inserted_id);
1173
+
1174
+ // Verify both documents exist using database API
1175
+ const db_find_result = await client.db('default').collection('mixed_test').find({});
1176
+ t.truthy(db_find_result);
1177
+ t.is(db_find_result.length, 2);
1178
+
1179
+ // Verify both documents exist using collection API
1180
+ const collection_find_result = await collection.find({});
1181
+ t.truthy(collection_find_result);
1182
+ t.is(collection_find_result.length, 2);
1183
+
1184
+ // Check specific documents
1185
+ const db_doc = await client.db('default').collection('mixed_test').find_one({ method: 'db_api' });
1186
+ const collection_doc = await collection.find_one({ method: 'collection_api' });
1187
+
1188
+ t.is(db_doc.value, 100);
1189
+ t.is(collection_doc.value, 200);
1190
+ });
1191
+
1192
+ test('collection chaining API handles errors properly', async (t) => {
1193
+ const test_port = get_next_port();
1194
+ server = await create_server();
1195
+
1196
+ await new Promise((resolve) => {
1197
+ server.listen(test_port, () => {
1198
+ setTimeout(resolve, 100);
1199
+ });
1200
+ });
1201
+
1202
+ const setup_client = joystickdb.client({
1203
+ port: test_port,
1204
+ reconnect: false
1205
+ });
1206
+
1207
+ await new Promise((resolve) => {
1208
+ setup_client.on('connect', resolve);
1209
+ });
1210
+
1211
+ const setup_result = await setup_client.setup();
1212
+ setup_client.disconnect();
1213
+
1214
+ client = joystickdb.client({
1215
+ port: test_port,
1216
+ reconnect: false,
1217
+ password: setup_result.password
1218
+ });
1219
+
1220
+ await new Promise((resolve) => {
1221
+ client.on('authenticated', resolve);
1222
+ });
1223
+
1224
+ const collection = client.db('default').collection('error_test');
1225
+
1226
+ // Test error handling by trying to update with invalid filter
1227
+ const error = await t.throwsAsync(async () => {
1228
+ await collection.update_one(null, { $set: { test: true } });
1229
+ });
1230
+
1231
+ t.truthy(error);
1232
+ });