@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,128 @@
1
+ import test from 'ava';
2
+ import sinon from 'sinon';
3
+ import bulk_write from '../../../../src/server/lib/operations/bulk_write.js';
4
+ import insert_one from '../../../../src/server/lib/operations/insert_one.js';
5
+ import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
6
+
7
+ test.beforeEach(() => {
8
+ initialize_database('./test_data');
9
+ const db = get_database();
10
+ db.clearSync();
11
+ });
12
+
13
+ test.afterEach(async () => {
14
+ await cleanup_database();
15
+ });
16
+
17
+ test('bulk_write - should perform insert, update, and delete in one transaction', async (t) => {
18
+ const { inserted_id } = await insert_one('default', 'users', { name: 'Alice', age: 20 });
19
+ const operations = [
20
+ { insert_one: { document: { name: 'Bob', age: 30 } } },
21
+ { update_one: { filter: { _id: inserted_id }, update: { $set: { age: 21 } } } },
22
+ { delete_one: { filter: { name: 'Bob' } } }
23
+ ];
24
+ const result = await bulk_write('default', 'users', operations);
25
+ t.true(result.acknowledged);
26
+ t.is(result.inserted_count, 1);
27
+ t.is(result.matched_count, 1);
28
+ t.is(result.modified_count, 1);
29
+ t.is(result.deleted_count, 1);
30
+ t.truthy(result.inserted_ids[0]);
31
+ const db = get_database();
32
+ const alice_data = db.get(`default:users:${inserted_id}`);
33
+ const alice = JSON.parse(alice_data);
34
+ t.is(alice.age, 21);
35
+ const bob_id = result.inserted_ids[0];
36
+ const bob_data = db.get(`default:users:${bob_id}`);
37
+ t.falsy(bob_data);
38
+ });
39
+
40
+ test('bulk_write - should upsert with update_one', async (t) => {
41
+ const operations = [
42
+ { update_one: { filter: { name: 'Carol' }, update: { $set: { age: 40 } }, upsert: true } }
43
+ ];
44
+ const result = await bulk_write('default', 'users', operations);
45
+ t.is(result.upserted_count, 1);
46
+ t.truthy(result.upserted_ids[0]);
47
+ const db = get_database();
48
+ const doc_data = db.get(`default:users:${result.upserted_ids[0]}`);
49
+ if (doc_data) {
50
+ const doc = JSON.parse(doc_data);
51
+ t.is(doc.name, 'Carol');
52
+ t.is(doc.age, 40);
53
+ } else {
54
+ t.fail('Document not found');
55
+ }
56
+ });
57
+
58
+ test('bulk_write - should throw if collection name is missing', async (t) => {
59
+ await t.throwsAsync(() => bulk_write('default', '', [{ insert_one: { document: { name: 'X' } } }]), { message: 'Collection name is required' });
60
+ });
61
+
62
+ test('bulk_write - should throw if operations is not a non-empty array', async (t) => {
63
+ await t.throwsAsync(() => bulk_write('default', 'users', null), { message: 'Operations must be a non-empty array' });
64
+ await t.throwsAsync(() => bulk_write('default', 'users', []), { message: 'Operations must be a non-empty array' });
65
+ });
66
+
67
+ test('bulk_write - should throw on unsupported operation type', async (t) => {
68
+ const operations = [{ fooBar: { document: { name: 'Y' } } }];
69
+ await t.throwsAsync(() => bulk_write('default', 'users', operations), { message: 'Unsupported bulk operation: fooBar' });
70
+ });
71
+
72
+ test('bulk_write - should rollback all operations if one fails', async (t) => {
73
+ const { inserted_id } = await insert_one('default', 'users', { _id: 'fixed_id', name: 'Zed' });
74
+ const operations = [
75
+ { insert_one: { document: { _id: 'fixed_id', name: 'Duplicate' } } },
76
+ { insert_one: { document: { name: 'ShouldNotInsert' } } }
77
+ ];
78
+ await t.throwsAsync(() => bulk_write('default', 'users', operations));
79
+ const db = get_database();
80
+ const range_data = Array.from(db.getRange({ start: 'default:users:', end: 'default:users:\xFF' }));
81
+ const should_not_insert = range_data.find(({ value }) => {
82
+ const doc = JSON.parse(value);
83
+ return doc.name === 'ShouldNotInsert';
84
+ });
85
+ t.falsy(should_not_insert);
86
+ });
87
+
88
+ test('bulk_write - should support backward compatibility with camelCase operations', async (t) => {
89
+ const { inserted_id } = await insert_one('default', 'users', { name: 'Alice', age: 20 });
90
+ const operations = [
91
+ { insertOne: { document: { name: 'Bob', age: 30 } } },
92
+ { updateOne: { filter: { _id: inserted_id }, update: { $set: { age: 21 } } } },
93
+ { deleteOne: { filter: { name: 'Bob' } } }
94
+ ];
95
+ const result = await bulk_write('default', 'users', operations);
96
+ t.true(result.acknowledged);
97
+ t.is(result.inserted_count, 1);
98
+ t.is(result.matched_count, 1);
99
+ t.is(result.modified_count, 1);
100
+ t.is(result.deleted_count, 1);
101
+ t.truthy(result.inserted_ids[0]);
102
+ const db = get_database();
103
+ const alice_data = db.get(`default:users:${inserted_id}`);
104
+ const alice = JSON.parse(alice_data);
105
+ t.is(alice.age, 21);
106
+ const bob_id = result.inserted_ids[0];
107
+ const bob_data = db.get(`default:users:${bob_id}`);
108
+ t.falsy(bob_data);
109
+ });
110
+
111
+ test('bulk_write - should support mixed snake_case and camelCase operations', async (t) => {
112
+ const operations = [
113
+ { insert_one: { document: { name: 'Snake', type: 'snake_case' } } },
114
+ { insertOne: { document: { name: 'Camel', type: 'camelCase' } } }
115
+ ];
116
+ const result = await bulk_write('default', 'users', operations);
117
+ t.true(result.acknowledged);
118
+ t.is(result.inserted_count, 2);
119
+ t.truthy(result.inserted_ids[0]);
120
+ t.truthy(result.inserted_ids[1]);
121
+ const db = get_database();
122
+ const snake_data = db.get(`default:users:${result.inserted_ids[0]}`);
123
+ const camel_data = db.get(`default:users:${result.inserted_ids[1]}`);
124
+ const snake_doc = JSON.parse(snake_data);
125
+ const camel_doc = JSON.parse(camel_data);
126
+ t.is(snake_doc.name, 'Snake');
127
+ t.is(camel_doc.name, 'Camel');
128
+ });
@@ -0,0 +1,138 @@
1
+ import test from 'ava';
2
+ import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
3
+ import { initialize_index_database, get_index_database } from '../../../../src/server/lib/index_manager.js';
4
+ import create_index_operation from '../../../../src/server/lib/operations/create_index.js';
5
+
6
+ test.beforeEach(() => {
7
+ initialize_database('./test_data');
8
+ initialize_index_database();
9
+
10
+ const main_db = get_database();
11
+ const index_db = get_index_database();
12
+
13
+ try {
14
+ const main_range = main_db.getRange();
15
+ for (const { key } of main_range) {
16
+ try {
17
+ main_db.remove(key);
18
+ } catch (error) {
19
+ // Ignore errors during cleanup
20
+ }
21
+ }
22
+ } catch (error) {
23
+ // Ignore errors during cleanup
24
+ }
25
+
26
+ try {
27
+ const index_range = index_db.getRange();
28
+ for (const { key } of index_range) {
29
+ try {
30
+ index_db.remove(key);
31
+ } catch (error) {
32
+ // Ignore errors during cleanup
33
+ }
34
+ }
35
+ } catch (error) {
36
+ // Ignore errors during cleanup
37
+ }
38
+ });
39
+
40
+ test.afterEach(async () => {
41
+ await cleanup_database();
42
+ });
43
+
44
+ test('create_index_operation - should create an index successfully', async (t) => {
45
+ const result = await create_index_operation('default', 'users', 'email');
46
+
47
+ t.is(result.acknowledged, true);
48
+ });
49
+
50
+ test('create_index_operation - should create a unique index', async (t) => {
51
+ const result = await create_index_operation('default', 'users', 'email', { unique: true });
52
+
53
+ t.is(result.acknowledged, true);
54
+ });
55
+
56
+ test('create_index_operation - should create a sparse index', async (t) => {
57
+ const result = await create_index_operation('default', 'users', 'email', { sparse: true });
58
+
59
+ t.is(result.acknowledged, true);
60
+ });
61
+
62
+ test('create_index_operation - should throw if collection name is missing', async (t) => {
63
+ await t.throwsAsync(
64
+ () => create_index_operation('default', '', 'email'),
65
+ { message: 'Collection name is required' }
66
+ );
67
+ });
68
+
69
+ test('create_index_operation - should throw if field name is missing', async (t) => {
70
+ await t.throwsAsync(
71
+ () => create_index_operation('default', 'users', ''),
72
+ { message: 'Field name is required' }
73
+ );
74
+ });
75
+
76
+ test('create_index_operation - should throw if index already exists', async (t) => {
77
+ await create_index_operation('default', 'users', 'email');
78
+
79
+ await t.throwsAsync(
80
+ () => create_index_operation('default', 'users', 'email'),
81
+ { message: 'Index on default.users.email already exists' }
82
+ );
83
+ });
84
+
85
+ test('create_index_operation - should create index with upsert when index does not exist', async (t) => {
86
+ const result = await create_index_operation('default', 'users', 'email', { upsert: true });
87
+
88
+ t.is(result.acknowledged, true);
89
+ t.is(result.operation_type, 'created');
90
+ t.is(result.message, 'Index created on default.users.email');
91
+ });
92
+
93
+ test('create_index_operation - should not change index with upsert when index exists with same options', async (t) => {
94
+ await create_index_operation('default', 'users', 'email', { unique: true });
95
+
96
+ const result = await create_index_operation('default', 'users', 'email', { unique: true, upsert: true });
97
+
98
+ t.is(result.acknowledged, true);
99
+ t.is(result.operation_type, 'unchanged');
100
+ t.is(result.message, 'Index already exists on default.users.email');
101
+ });
102
+
103
+ test('create_index_operation - should update index with upsert when index exists with different options', async (t) => {
104
+ await create_index_operation('default', 'users', 'email', { unique: false });
105
+
106
+ const result = await create_index_operation('default', 'users', 'email', { unique: true, upsert: true });
107
+
108
+ t.is(result.acknowledged, true);
109
+ t.is(result.operation_type, 'updated');
110
+ t.is(result.message, 'Index updated on default.users.email');
111
+ });
112
+
113
+ test('create_index_operation - should update sparse index option with upsert', async (t) => {
114
+ await create_index_operation('default', 'users', 'email', { sparse: false });
115
+
116
+ const result = await create_index_operation('default', 'users', 'email', { sparse: true, upsert: true });
117
+
118
+ t.is(result.acknowledged, true);
119
+ t.is(result.operation_type, 'updated');
120
+ t.is(result.message, 'Index updated on default.users.email');
121
+ });
122
+
123
+ test('create_index_operation - should handle upsert with existing data and unique constraint', async (t) => {
124
+ const main_db = get_database();
125
+
126
+ // Insert test documents with duplicate values
127
+ main_db.put('default:users:1', JSON.stringify({ _id: '1', email: 'test@example.com' }));
128
+ main_db.put('default:users:2', JSON.stringify({ _id: '2', email: 'test@example.com' }));
129
+
130
+ // Create non-unique index first
131
+ await create_index_operation('default', 'users', 'email', { unique: false });
132
+
133
+ // Try to update to unique index - should fail due to duplicates
134
+ await t.throwsAsync(
135
+ () => create_index_operation('default', 'users', 'email', { unique: true, upsert: true }),
136
+ { message: /Duplicate value for unique index/ }
137
+ );
138
+ });
@@ -0,0 +1,52 @@
1
+ import test from 'ava';
2
+ import sinon from 'sinon';
3
+ import delete_one from '../../../../src/server/lib/operations/delete_one.js';
4
+ import insert_one from '../../../../src/server/lib/operations/insert_one.js';
5
+ import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
6
+
7
+ test.beforeEach(() => {
8
+ initialize_database('./test_data');
9
+ const db = get_database();
10
+ db.clearSync();
11
+ });
12
+
13
+ test.afterEach(async () => {
14
+ await cleanup_database();
15
+ });
16
+
17
+ test('delete_one - should delete a document by filter', async (t) => {
18
+ const { inserted_id } = await insert_one('default', 'users', { name: 'Alice' });
19
+ const result = await delete_one('default', 'users', { _id: inserted_id });
20
+ t.true(result.acknowledged);
21
+ t.is(result.deleted_count, 1);
22
+ const db = get_database();
23
+ const doc = db.get(`default:users:${inserted_id}`);
24
+ t.falsy(doc);
25
+ });
26
+
27
+ test('delete_one - should only delete one matching document', async (t) => {
28
+ await insert_one('default', 'users', { name: 'Bob', group: 'g1' });
29
+ const { inserted_id } = await insert_one('default', 'users', { name: 'Carol', group: 'g1' });
30
+ const result = await delete_one('default', 'users', { group: 'g1' });
31
+ t.true(result.acknowledged);
32
+ t.is(result.deleted_count, 1);
33
+ const db = get_database();
34
+ const doc1 = db.get(`default:users:${inserted_id}`);
35
+ t.truthy(doc1);
36
+ });
37
+
38
+ test('delete_one - should return deleted_count 0 if no match', async (t) => {
39
+ await insert_one('default', 'users', { name: 'Dan' });
40
+ const result = await delete_one('default', 'users', { name: 'Nonexistent' });
41
+ t.true(result.acknowledged);
42
+ t.is(result.deleted_count, 0);
43
+ });
44
+
45
+ test('delete_one - should throw if collection name is missing', async (t) => {
46
+ await t.throwsAsync(() => delete_one('default', '', { name: 'Eve' }), { message: 'Collection name is required' });
47
+ });
48
+
49
+ test('delete_one - should throw if filter is not an object', async (t) => {
50
+ await t.throwsAsync(() => delete_one('default', 'users', null), { message: 'Filter must be a valid object' });
51
+ await t.throwsAsync(() => delete_one('default', 'users', 'not-an-object'), { message: 'Filter must be a valid object' });
52
+ });
@@ -0,0 +1,72 @@
1
+ import test from 'ava';
2
+ import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
3
+ import { initialize_index_database, get_index_database, create_index, cleanup_index_database } from '../../../../src/server/lib/index_manager.js';
4
+ import drop_index_operation from '../../../../src/server/lib/operations/drop_index.js';
5
+
6
+ test.beforeEach(() => {
7
+ initialize_database('./test_data');
8
+ initialize_index_database();
9
+
10
+ const main_db = get_database();
11
+ const index_db = get_index_database();
12
+
13
+ try {
14
+ const main_range = main_db.getRange();
15
+ for (const { key } of main_range) {
16
+ try {
17
+ main_db.remove(key);
18
+ } catch (error) {
19
+ // Ignore errors during cleanup
20
+ }
21
+ }
22
+ } catch (error) {
23
+ // Ignore errors during cleanup
24
+ }
25
+
26
+ try {
27
+ const index_range = index_db.getRange();
28
+ for (const { key } of index_range) {
29
+ try {
30
+ index_db.remove(key);
31
+ } catch (error) {
32
+ // Ignore errors during cleanup
33
+ }
34
+ }
35
+ } catch (error) {
36
+ // Ignore errors during cleanup
37
+ }
38
+ });
39
+
40
+ test.afterEach(async (t) => {
41
+ cleanup_index_database();
42
+ await cleanup_database();
43
+ });
44
+
45
+ test('drop_index_operation - should drop an index successfully', async (t) => {
46
+ await create_index('default', 'users', 'email');
47
+
48
+ const result = await drop_index_operation('default', 'users', 'email');
49
+
50
+ t.is(result.acknowledged, true);
51
+ });
52
+
53
+ test('drop_index_operation - should throw if collection name is missing', async (t) => {
54
+ await t.throwsAsync(
55
+ () => drop_index_operation('default', '', 'email'),
56
+ { message: 'Collection name is required' }
57
+ );
58
+ });
59
+
60
+ test('drop_index_operation - should throw if field name is missing', async (t) => {
61
+ await t.throwsAsync(
62
+ () => drop_index_operation('default', 'users', ''),
63
+ { message: 'Field name is required' }
64
+ );
65
+ });
66
+
67
+ test('drop_index_operation - should throw if index does not exist', async (t) => {
68
+ await t.throwsAsync(
69
+ () => drop_index_operation('default', 'users', 'email'),
70
+ { message: 'Index on default.users.email does not exist' }
71
+ );
72
+ });
@@ -0,0 +1,93 @@
1
+ import test from 'ava';
2
+ import sinon from 'sinon';
3
+ import find from '../../../../src/server/lib/operations/find.js';
4
+ import insert_one from '../../../../src/server/lib/operations/insert_one.js';
5
+ import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
6
+
7
+ test.beforeEach(() => {
8
+ initialize_database('./test_data');
9
+ const db = get_database();
10
+ db.clearSync();
11
+ });
12
+
13
+ test.afterEach(async (t) => {
14
+ await cleanup_database();
15
+ });
16
+
17
+ test('find - should find multiple documents by filter', async (t) => {
18
+ await insert_one('default', 'users', { name: 'Alice', age: 25 });
19
+ await insert_one('default', 'users', { name: 'Bob', age: 25 });
20
+ await insert_one('default', 'users', { name: 'Carol', age: 30 });
21
+ const docs = await find('default', 'users', { age: 25 });
22
+ t.is(docs.length, 2);
23
+ t.true(docs.some(doc => doc.name === 'Alice'));
24
+ t.true(docs.some(doc => doc.name === 'Bob'));
25
+ });
26
+
27
+ test('find - should return empty array if no match', async (t) => {
28
+ await insert_one('default', 'users', { name: 'Dan' });
29
+ const docs = await find('default', 'users', { name: 'Nonexistent' });
30
+ t.deepEqual(docs, []);
31
+ });
32
+
33
+ test('find - should support all query operators', async (t) => {
34
+ await insert_one('default', 'users', { name: 'Eve', age: 20, tags: ['a'], email: 'eve@example.com' });
35
+ await insert_one('default', 'users', { name: 'Frank', age: 30, tags: ['b'], email: 'frank@example.com' });
36
+ t.is((await find('default', 'users', { age: { $eq: 20 } })).length, 1);
37
+ t.is((await find('default', 'users', { age: { $ne: 20 } })).length, 1);
38
+ t.is((await find('default', 'users', { age: { $gt: 25 } })).length, 1);
39
+ t.is((await find('default', 'users', { age: { $gte: 30 } })).length, 1);
40
+ t.is((await find('default', 'users', { age: { $lt: 25 } })).length, 1);
41
+ t.is((await find('default', 'users', { age: { $lte: 20 } })).length, 1);
42
+ t.is((await find('default', 'users', { name: { $in: ['Eve', 'X'] } })).length, 1);
43
+ t.is((await find('default', 'users', { name: { $nin: ['Frank'] } })).length, 1);
44
+ t.is((await find('default', 'users', { email: { $exists: true } })).length, 2);
45
+ t.is((await find('default', 'users', { phone: { $exists: false } })).length, 2);
46
+ t.is((await find('default', 'users', { email: { $regex: '^eve@' } })).length, 1);
47
+ });
48
+
49
+ test('find - should apply projection inclusion', async (t) => {
50
+ await insert_one('default', 'users', { name: 'Grace', age: 40, email: 'g@example.com' });
51
+ const docs = await find('default', 'users', { name: 'Grace' }, { projection: { name: 1, _id: 1 } });
52
+ t.is(docs.length, 1);
53
+ t.truthy(docs[0].name);
54
+ t.truthy(docs[0]._id);
55
+ t.falsy(docs[0].age);
56
+ t.falsy(docs[0].email);
57
+ });
58
+
59
+ test('find - should apply projection exclusion', async (t) => {
60
+ await insert_one('default', 'users', { name: 'Heidi', age: 50, email: 'h@example.com' });
61
+ const docs = await find('default', 'users', { name: 'Heidi' }, { projection: { age: 0, email: 0 } });
62
+ t.is(docs.length, 1);
63
+ t.truthy(docs[0].name);
64
+ t.truthy(docs[0]._id);
65
+ t.falsy(docs[0].age);
66
+ t.falsy(docs[0].email);
67
+ });
68
+
69
+ test('find - should sort documents ascending and descending', async (t) => {
70
+ await insert_one('default', 'users', { name: 'Ivan', age: 10 });
71
+ await insert_one('default', 'users', { name: 'Judy', age: 20 });
72
+ await insert_one('default', 'users', { name: 'Karl', age: 30 });
73
+ const asc = await find('default', 'users', {}, { sort: { age: 1 } });
74
+ t.deepEqual(asc.map(doc => doc.name), ['Ivan', 'Judy', 'Karl']);
75
+ const desc = await find('default', 'users', {}, { sort: { age: -1 } });
76
+ t.deepEqual(desc.map(doc => doc.name), ['Karl', 'Judy', 'Ivan']);
77
+ });
78
+
79
+ test('find - should limit and skip results', async (t) => {
80
+ await insert_one('default', 'users', { name: 'Liam', age: 1 });
81
+ await insert_one('default', 'users', { name: 'Mona', age: 2 });
82
+ await insert_one('default', 'users', { name: 'Nina', age: 3 });
83
+ const limited = await find('default', 'users', {}, { sort: { age: 1 }, limit: 2 });
84
+ t.is(limited.length, 2);
85
+ t.deepEqual(limited.map(doc => doc.name), ['Liam', 'Mona']);
86
+ const skipped = await find('default', 'users', {}, { sort: { age: 1 }, skip: 1 });
87
+ t.is(skipped.length, 2);
88
+ t.deepEqual(skipped.map(doc => doc.name), ['Mona', 'Nina']);
89
+ });
90
+
91
+ test('find - should throw if collection name is missing', async (t) => {
92
+ await t.throwsAsync(() => find('default', '', {}), { message: 'Collection name is required' });
93
+ });
@@ -0,0 +1,91 @@
1
+ import test from 'ava';
2
+ import sinon from 'sinon';
3
+ import find_one from '../../../../src/server/lib/operations/find_one.js';
4
+ import insert_one from '../../../../src/server/lib/operations/insert_one.js';
5
+ import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
6
+
7
+ test.beforeEach(() => {
8
+ initialize_database('./test_data');
9
+ const db = get_database();
10
+ db.clearSync();
11
+ });
12
+
13
+ test.afterEach(async (t) => {
14
+ await cleanup_database();
15
+ });
16
+
17
+ test('find_one - should find a document by filter', async (t) => {
18
+ const { inserted_id } = await insert_one('default', 'users', { name: 'Alice', age: 25 });
19
+ const doc = await find_one('default', 'users', { _id: inserted_id });
20
+ t.truthy(doc);
21
+ t.is(doc.name, 'Alice');
22
+ });
23
+
24
+ test('find_one - should return null if no match', async (t) => {
25
+ await insert_one('default', 'users', { name: 'Bob' });
26
+ const doc = await find_one('default', 'users', { name: 'Nonexistent' });
27
+ t.is(doc, null);
28
+ });
29
+
30
+ test('find_one - should support $eq operator', async (t) => {
31
+ const { inserted_id } = await insert_one('default', 'users', { age: 30 });
32
+ const doc = await find_one('default', 'users', { age: { $eq: 30 } });
33
+ t.truthy(doc);
34
+ t.is(doc._id, inserted_id);
35
+ });
36
+
37
+ test('find_one - should support $ne operator', async (t) => {
38
+ await insert_one('default', 'users', { name: 'Carol', age: 40 });
39
+ const doc = await find_one('default', 'users', { age: { $ne: 50 } });
40
+ t.truthy(doc);
41
+ t.is(doc.name, 'Carol');
42
+ });
43
+
44
+ test('find_one - should support $gt, $gte, $lt, $lte operators', async (t) => {
45
+ await insert_one('default', 'users', { name: 'Dan', age: 20 });
46
+ await insert_one('default', 'users', { name: 'Eve', age: 30 });
47
+ t.truthy(await find_one('default', 'users', { age: { $gt: 25 } }));
48
+ t.truthy(await find_one('default', 'users', { age: { $gte: 30 } }));
49
+ t.truthy(await find_one('default', 'users', { age: { $lt: 25 } }));
50
+ t.truthy(await find_one('default', 'users', { age: { $lte: 20 } }));
51
+ });
52
+
53
+ test('find_one - should support $in and $nin operators', async (t) => {
54
+ await insert_one('default', 'users', { name: 'Frank', role: 'admin' });
55
+ await insert_one('default', 'users', { name: 'Grace', role: 'user' });
56
+ t.truthy(await find_one('default', 'users', { role: { $in: ['admin', 'super'] } }));
57
+ t.truthy(await find_one('default', 'users', { role: { $nin: ['banned'] } }));
58
+ });
59
+
60
+ test('find_one - should support $exists operator', async (t) => {
61
+ await insert_one('default', 'users', { name: 'Heidi', email: 'h@example.com' });
62
+ t.truthy(await find_one('default', 'users', { email: { $exists: true } }));
63
+ t.truthy(await find_one('default', 'users', { phone: { $exists: false } }));
64
+ });
65
+
66
+ test('find_one - should support $regex operator', async (t) => {
67
+ await insert_one('default', 'users', { name: 'Ivan', email: 'ivan@example.com' });
68
+ t.truthy(await find_one('default', 'users', { email: { $regex: '^ivan@' } }));
69
+ });
70
+
71
+ test('find_one - should apply projection inclusion', async (t) => {
72
+ const { inserted_id } = await insert_one('default', 'users', { name: 'Judy', age: 50, email: 'j@example.com' });
73
+ const doc = await find_one('default', 'users', { _id: inserted_id }, { projection: { name: 1, _id: 1 } });
74
+ t.truthy(doc.name);
75
+ t.truthy(doc._id);
76
+ t.falsy(doc.age);
77
+ t.falsy(doc.email);
78
+ });
79
+
80
+ test('find_one - should apply projection exclusion', async (t) => {
81
+ const { inserted_id } = await insert_one('default', 'users', { name: 'Karl', age: 60, email: 'k@example.com' });
82
+ const doc = await find_one('default', 'users', { _id: inserted_id }, { projection: { age: 0, email: 0 } });
83
+ t.truthy(doc.name);
84
+ t.truthy(doc._id);
85
+ t.falsy(doc.age);
86
+ t.falsy(doc.email);
87
+ });
88
+
89
+ test('find_one - should throw if collection name is missing', async (t) => {
90
+ await t.throwsAsync(() => find_one('default', '', { name: 'Liam' }), { message: 'Collection name is required' });
91
+ });
@@ -0,0 +1,87 @@
1
+ import test from 'ava';
2
+ import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
3
+ import { initialize_index_database, get_index_database, create_index, cleanup_index_database } from '../../../../src/server/lib/index_manager.js';
4
+ import get_indexes_operation from '../../../../src/server/lib/operations/get_indexes.js';
5
+
6
+ test.beforeEach(() => {
7
+ initialize_database('./test_data');
8
+ initialize_index_database();
9
+
10
+ const main_db = get_database();
11
+ const index_db = get_index_database();
12
+
13
+ try {
14
+ const main_range = main_db.getRange();
15
+ for (const { key } of main_range) {
16
+ try {
17
+ main_db.remove(key);
18
+ } catch (error) {
19
+ // Ignore errors during cleanup
20
+ }
21
+ }
22
+ } catch (error) {
23
+ // Ignore errors during cleanup
24
+ }
25
+
26
+ try {
27
+ const index_range = index_db.getRange();
28
+ for (const { key } of index_range) {
29
+ try {
30
+ index_db.remove(key);
31
+ } catch (error) {
32
+ // Ignore errors during cleanup
33
+ }
34
+ }
35
+ } catch (error) {
36
+ // Ignore errors during cleanup
37
+ }
38
+ });
39
+
40
+ test.afterEach(async (t) => {
41
+ cleanup_index_database();
42
+ await cleanup_database();
43
+ });
44
+
45
+ test('get_indexes_operation - should return empty array for collection with no indexes', async (t) => {
46
+ const result = await get_indexes_operation('default', 'users');
47
+
48
+ t.is(result.acknowledged, true);
49
+ t.is(result.indexes.length, 0);
50
+ });
51
+
52
+ test('get_indexes_operation - should return all indexes for a collection', async (t) => {
53
+ await create_index('default', 'users', 'email');
54
+ await create_index('default', 'users', 'name');
55
+
56
+ const result = await get_indexes_operation('default', 'users');
57
+
58
+ t.is(result.acknowledged, true);
59
+ t.is(result.indexes.length, 2);
60
+
61
+ const fields = result.indexes.map(index => index.field).sort();
62
+ t.deepEqual(fields, ['email', 'name']);
63
+ });
64
+
65
+ test('get_indexes_operation - should throw if collection name is missing', async (t) => {
66
+ await t.throwsAsync(
67
+ () => get_indexes_operation('default', ''),
68
+ { message: 'Collection name is required' }
69
+ );
70
+ });
71
+
72
+ test('get_indexes_operation - should return index metadata', async (t) => {
73
+ await create_index('default', 'users', 'email', { unique: true, sparse: false });
74
+
75
+ const result = await get_indexes_operation('default', 'users');
76
+
77
+ t.is(result.acknowledged, true);
78
+ t.is(result.indexes.length, 1);
79
+
80
+ const index = result.indexes[0];
81
+ t.is(index.database, 'default');
82
+ t.is(index.collection, 'users');
83
+ t.is(index.field, 'email');
84
+ t.is(index.unique, true);
85
+ t.is(index.sparse, false);
86
+ t.truthy(index.created_at);
87
+ });