@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,389 @@
1
+ import test from 'ava';
2
+ import http from 'http';
3
+ import { URL } from 'url';
4
+ import fs from 'fs';
5
+ import {
6
+ start_http_server,
7
+ stop_http_server,
8
+ get_setup_info,
9
+ is_setup_required
10
+ } from '../../../src/server/lib/http_server.js';
11
+ import { reset_auth_state, setup_authentication } from '../../../src/server/lib/auth_manager.js';
12
+
13
+ // NOTE: Simple port allocation using a fixed range to avoid conflicts.
14
+ let current_port = 9000;
15
+ const get_next_port = () => {
16
+ return ++current_port;
17
+ };
18
+
19
+ const make_http_request = (url, method = 'GET', data = null) => {
20
+ return new Promise((resolve, reject) => {
21
+ const parsed_url = new URL(url);
22
+ const options = {
23
+ hostname: parsed_url.hostname,
24
+ port: parsed_url.port,
25
+ path: parsed_url.pathname + parsed_url.search,
26
+ method: method,
27
+ headers: {
28
+ 'Content-Type': 'application/x-www-form-urlencoded'
29
+ }
30
+ };
31
+
32
+ const req = http.request(options, (res) => {
33
+ let body = '';
34
+ res.on('data', (chunk) => {
35
+ body += chunk;
36
+ });
37
+ res.on('end', () => {
38
+ resolve({
39
+ status_code: res.statusCode,
40
+ headers: res.headers,
41
+ body: body
42
+ });
43
+ });
44
+ });
45
+
46
+ req.on('error', (error) => {
47
+ reject(error);
48
+ });
49
+
50
+ if (data && method === 'POST') {
51
+ req.write(data);
52
+ }
53
+
54
+ req.end();
55
+ });
56
+ };
57
+
58
+ test.beforeEach(() => {
59
+ reset_auth_state();
60
+ });
61
+
62
+ test.afterEach(async () => {
63
+ // NOTE: Clean up HTTP server.
64
+ await stop_http_server();
65
+
66
+ reset_auth_state();
67
+ });
68
+
69
+ test('is_setup_required returns true when authentication not configured', (t) => {
70
+ const setup_required = is_setup_required();
71
+ t.true(setup_required);
72
+ });
73
+
74
+ test('is_setup_required returns false when authentication is configured', (t) => {
75
+ setup_authentication();
76
+ const setup_required = is_setup_required();
77
+ t.false(setup_required);
78
+ });
79
+
80
+ test('start_http_server starts server even when setup not required (for recovery)', async (t) => {
81
+ setup_authentication();
82
+ const port = get_next_port();
83
+ const server = await start_http_server(port);
84
+ t.truthy(server);
85
+
86
+ const setup_info = get_setup_info();
87
+ t.false(setup_info.setup_required);
88
+ t.is(setup_info.setup_token, null);
89
+ t.false(setup_info.setup_completed);
90
+ t.true(setup_info.http_server_running);
91
+ });
92
+
93
+ test('start_http_server starts server when setup is required', async (t) => {
94
+ const port = get_next_port();
95
+ const server = await start_http_server(port);
96
+ t.truthy(server);
97
+
98
+ const setup_info = get_setup_info();
99
+ t.true(setup_info.setup_required);
100
+ t.truthy(setup_info.setup_token);
101
+ t.false(setup_info.setup_completed);
102
+ t.true(setup_info.http_server_running);
103
+ });
104
+
105
+ test('HTTP server serves 404 for unknown paths', async (t) => {
106
+ const port = get_next_port();
107
+ await start_http_server(port);
108
+
109
+ const response = await make_http_request(`http://localhost:${port}/unknown`);
110
+ t.is(response.status_code, 404);
111
+ t.true(response.body.includes('404 Not Found'));
112
+ });
113
+
114
+ test('setup endpoint requires valid token', async (t) => {
115
+ const port = get_next_port();
116
+ await start_http_server(port);
117
+
118
+ const response = await make_http_request(`http://localhost:${port}/setup?token=invalid`);
119
+ t.is(response.status_code, 403);
120
+ t.true(response.body.includes('Invalid or missing setup token'));
121
+ });
122
+
123
+ test('setup endpoint serves form with valid token', async (t) => {
124
+ const port = get_next_port();
125
+ await start_http_server(port);
126
+ const setup_info = get_setup_info();
127
+
128
+ const response = await make_http_request(`http://localhost:${port}/setup?token=${setup_info.setup_token}`);
129
+ t.is(response.status_code, 200);
130
+ t.true(response.body.includes('JoystickDB Setup'));
131
+ t.true(response.body.includes('Setup JoystickDB'));
132
+ t.true(response.body.includes('form method="POST"'));
133
+ });
134
+
135
+ test('setup endpoint handles POST request with valid token', async (t) => {
136
+ const port = get_next_port();
137
+ await start_http_server(port);
138
+ const setup_info = get_setup_info();
139
+
140
+ const response = await make_http_request(
141
+ `http://localhost:${port}/setup?token=${setup_info.setup_token}`,
142
+ 'POST',
143
+ ''
144
+ );
145
+
146
+ t.is(response.status_code, 200);
147
+ t.true(response.body.includes('Setup Completed Successfully'));
148
+ t.true(response.body.includes('Generated Password'));
149
+ t.true(response.body.includes('JOYSTICKDB_PASSWORD='));
150
+
151
+ // NOTE: Verify authentication was configured in environment variable.
152
+ t.truthy(process.env.JOYSTICK_DB_SETTINGS);
153
+ });
154
+
155
+ test('setup endpoint rejects POST with invalid token', async (t) => {
156
+ const port = get_next_port();
157
+ await start_http_server(port);
158
+
159
+ const response = await make_http_request(
160
+ `http://localhost:${port}/setup?token=invalid`,
161
+ 'POST',
162
+ ''
163
+ );
164
+
165
+ t.is(response.status_code, 403);
166
+ t.true(response.body.includes('Invalid or missing setup token'));
167
+ });
168
+
169
+ test('setup endpoint rejects setup when already configured', async (t) => {
170
+ setup_authentication();
171
+ const port = get_next_port();
172
+ const server = await start_http_server(port);
173
+
174
+ // NOTE: Server should start even when authentication is configured (for recovery operations).
175
+ t.truthy(server);
176
+
177
+ // NOTE: Verify that is_setup_required returns false.
178
+ t.false(is_setup_required());
179
+
180
+ // NOTE: But setup endpoint should reject setup attempts.
181
+ const response = await make_http_request(`http://localhost:${port}/setup?token=any`);
182
+ t.is(response.status_code, 400);
183
+ t.true(response.body.includes('Setup has already been completed'));
184
+ });
185
+
186
+ test('setup endpoint handles missing token parameter', async (t) => {
187
+ const port = get_next_port();
188
+ await start_http_server(port);
189
+
190
+ const response = await make_http_request(`http://localhost:${port}/setup`);
191
+ t.is(response.status_code, 403);
192
+ t.true(response.body.includes('Invalid or missing setup token'));
193
+ });
194
+
195
+ test('setup endpoint handles unsupported HTTP methods', async (t) => {
196
+ const port = get_next_port();
197
+ await start_http_server(port);
198
+ const setup_info = get_setup_info();
199
+
200
+ const response = await make_http_request(
201
+ `http://localhost:${port}/setup?token=${setup_info.setup_token}`,
202
+ 'PUT',
203
+ ''
204
+ );
205
+
206
+ t.is(response.status_code, 405);
207
+ t.true(response.body.includes('Method not allowed'));
208
+ });
209
+
210
+ test('rate limiting prevents excessive setup attempts', async (t) => {
211
+ const port = get_next_port();
212
+ await start_http_server(port);
213
+
214
+ // NOTE: Make 11 failed attempts (exceeds limit of 10).
215
+ for (let i = 0; i < 11; i++) {
216
+ await make_http_request(`http://localhost:${port}/setup?token=invalid`);
217
+ }
218
+
219
+ const response = await make_http_request(`http://localhost:${port}/setup?token=invalid`);
220
+ t.is(response.status_code, 429);
221
+ t.true(response.body.includes('Too many setup attempts'));
222
+ });
223
+
224
+ test('stop_http_server stops running server', async (t) => {
225
+ const port = get_next_port();
226
+ const server = await start_http_server(port);
227
+ t.truthy(server);
228
+
229
+ await stop_http_server();
230
+
231
+ const setup_info = get_setup_info();
232
+ t.false(setup_info.http_server_running);
233
+
234
+ // NOTE: Verify server is no longer accessible.
235
+ try {
236
+ await make_http_request(`http://localhost:${port}/setup?token=any`);
237
+ t.fail('Server should not be accessible after stop');
238
+ } catch (error) {
239
+ t.true(error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND');
240
+ }
241
+ });
242
+
243
+ test('stop_http_server handles no running server gracefully', async (t) => {
244
+ await t.notThrowsAsync(async () => {
245
+ await stop_http_server();
246
+ });
247
+ });
248
+
249
+ test('get_setup_info returns correct information', async (t) => {
250
+ let setup_info = get_setup_info();
251
+ t.true(setup_info.setup_required);
252
+ t.is(setup_info.setup_token, null);
253
+ t.false(setup_info.setup_completed);
254
+ t.false(setup_info.http_server_running);
255
+
256
+ const port = get_next_port();
257
+ await start_http_server(port);
258
+
259
+ setup_info = get_setup_info();
260
+ t.true(setup_info.setup_required);
261
+ t.truthy(setup_info.setup_token);
262
+ t.false(setup_info.setup_completed);
263
+ t.true(setup_info.http_server_running);
264
+ });
265
+
266
+ test('setup completion updates setup info', async (t) => {
267
+ const port = get_next_port();
268
+ await start_http_server(port);
269
+ const setup_info = get_setup_info();
270
+
271
+ await make_http_request(
272
+ `http://localhost:${port}/setup?token=${setup_info.setup_token}`,
273
+ 'POST',
274
+ ''
275
+ );
276
+
277
+ const updated_info = get_setup_info();
278
+ t.false(updated_info.setup_required);
279
+ t.is(updated_info.setup_token, null);
280
+ t.true(updated_info.setup_completed);
281
+ });
282
+
283
+ test('HTTP server handles port conflicts gracefully', async (t) => {
284
+ const test_port = get_next_port();
285
+
286
+ // NOTE: Start first server.
287
+ const server1 = await start_http_server(test_port);
288
+ t.truthy(server1);
289
+
290
+ // NOTE: Wait a moment to ensure the first server is fully established.
291
+ await new Promise(resolve => setTimeout(resolve, 100));
292
+
293
+ // NOTE: Try to start second server on same port - this should fail.
294
+ let error_caught = false;
295
+ try {
296
+ await start_http_server(test_port);
297
+ t.fail('Should throw error for port conflict');
298
+ } catch (error) {
299
+ error_caught = true;
300
+ t.true(error.message.includes('EADDRINUSE') || error.code === 'EADDRINUSE');
301
+ }
302
+
303
+ // NOTE: Ensure we caught the expected error.
304
+ t.true(error_caught, 'Expected EADDRINUSE error was not caught');
305
+
306
+ // NOTE: Verify first server is still running and accessible.
307
+ const setup_info = get_setup_info();
308
+ t.true(setup_info.http_server_running);
309
+
310
+ // NOTE: Clean up the first server.
311
+ await stop_http_server();
312
+ });
313
+
314
+ test('setup form includes security information', async (t) => {
315
+ const port = get_next_port();
316
+ await start_http_server(port);
317
+ const setup_info = get_setup_info();
318
+
319
+ const response = await make_http_request(`http://localhost:${port}/setup?token=${setup_info.setup_token}`);
320
+
321
+ t.true(response.body.includes('What happens during setup?'));
322
+ t.true(response.body.includes('secure random password'));
323
+ t.true(response.body.includes('settings.db.json'));
324
+ t.true(response.body.includes('After setup:'));
325
+ t.true(response.body.includes('JOYSTICKDB_PASSWORD='));
326
+ t.true(response.body.includes('TCP port 1983'));
327
+ });
328
+
329
+ test('success page includes complete instructions', async (t) => {
330
+ const port = get_next_port();
331
+ await start_http_server(port);
332
+ const setup_info = get_setup_info();
333
+
334
+ const response = await make_http_request(
335
+ `http://localhost:${port}/setup?token=${setup_info.setup_token}`,
336
+ 'POST',
337
+ ''
338
+ );
339
+
340
+ t.true(response.body.includes('Setup Completed Successfully'));
341
+ t.true(response.body.includes('Save this password immediately'));
342
+ t.true(response.body.includes('Generated Password:'));
343
+ t.true(response.body.includes('Next Steps:'));
344
+ t.true(response.body.includes('Set environment variable'));
345
+ t.true(response.body.includes('Restart your application'));
346
+ t.true(response.body.includes('Connect using the TCP client'));
347
+ t.true(response.body.includes('Client Connection Example:'));
348
+ t.true(response.body.includes('import joystickdb'));
349
+ t.true(response.body.includes('Configuration Details:'));
350
+ });
351
+
352
+ test('error pages include troubleshooting information', async (t) => {
353
+ const port = get_next_port();
354
+ await start_http_server(port);
355
+
356
+ const response = await make_http_request(`http://localhost:${port}/setup?token=invalid`);
357
+
358
+ t.true(response.body.includes('Setup Error'));
359
+ t.true(response.body.includes('Troubleshooting:'));
360
+ t.true(response.body.includes('authentication'));
361
+ t.true(response.body.includes('settings.db.json'));
362
+ t.true(response.body.includes('write permissions'));
363
+ t.true(response.body.includes('server logs'));
364
+ t.true(response.body.includes('Go Back'));
365
+ });
366
+
367
+ test('HTTP server uses configured port from settings', async (t) => {
368
+ const custom_port = get_next_port();
369
+ const server = await start_http_server(custom_port);
370
+ t.truthy(server);
371
+
372
+ const setup_info = get_setup_info();
373
+ const response = await make_http_request(`http://localhost:${custom_port}/setup?token=${setup_info.setup_token}`);
374
+ t.is(response.status_code, 200);
375
+ });
376
+
377
+ test('setup token is unique across server restarts', async (t) => {
378
+ const port1 = get_next_port();
379
+ await start_http_server(port1);
380
+ const first_token = get_setup_info().setup_token;
381
+
382
+ await stop_http_server();
383
+
384
+ const port2 = get_next_port();
385
+ await start_http_server(port2);
386
+ const second_token = get_setup_info().setup_token;
387
+
388
+ t.not(first_token, second_token);
389
+ });
@@ -0,0 +1,301 @@
1
+ import test from 'ava';
2
+ import { initialize_database, get_database, cleanup_database } from '../../../src/server/lib/query_engine.js';
3
+ import {
4
+ initialize_index_database,
5
+ get_index_database,
6
+ create_index,
7
+ drop_index,
8
+ get_indexes,
9
+ update_indexes_on_insert,
10
+ update_indexes_on_update,
11
+ update_indexes_on_delete,
12
+ find_documents_by_index,
13
+ can_use_index
14
+ } from '../../../src/server/lib/index_manager.js';
15
+ import insert_one from '../../../src/server/lib/operations/insert_one.js';
16
+
17
+ test.beforeEach(() => {
18
+ initialize_database('./test_data');
19
+ initialize_index_database();
20
+
21
+ const main_db = get_database();
22
+ const index_db = get_index_database();
23
+
24
+ // Clear both databases
25
+ main_db.clearSync();
26
+ index_db.clearSync();
27
+ });
28
+
29
+ test.afterEach(async () => {
30
+ try {
31
+ await cleanup_database();
32
+ } catch (error) {
33
+ // Ignore cleanup errors in tests
34
+ }
35
+ });
36
+
37
+ test('create_index - should create an index successfully', async (t) => {
38
+ const result = await create_index('default', 'users', 'email');
39
+
40
+ t.is(result.acknowledged, true);
41
+
42
+ const indexes = get_indexes('default', 'users');
43
+ t.is(indexes.length, 1);
44
+ t.is(indexes[0].field, 'email');
45
+ t.is(indexes[0].collection, 'users');
46
+ });
47
+
48
+ test('create_index - should create a unique index', async (t) => {
49
+ const result = await create_index('default', 'users', 'email', { unique: true });
50
+
51
+ t.is(result.acknowledged, true);
52
+
53
+ const indexes = get_indexes('default', 'users');
54
+ t.is(indexes.length, 1);
55
+ t.is(indexes[0].unique, true);
56
+ });
57
+
58
+ test('create_index - should create a sparse index', async (t) => {
59
+ const result = await create_index('default', 'users', 'email', { sparse: true });
60
+
61
+ t.is(result.acknowledged, true);
62
+
63
+ const indexes = get_indexes('default', 'users');
64
+ t.is(indexes.length, 1);
65
+ t.is(indexes[0].sparse, true);
66
+ });
67
+
68
+ test('create_index - should throw if index already exists', async (t) => {
69
+ await create_index('default', 'users', 'email');
70
+
71
+ await t.throwsAsync(
72
+ () => create_index('default', 'users', 'email'),
73
+ { message: 'Index on default.users.email already exists' }
74
+ );
75
+ });
76
+
77
+ test('create_index - should throw if collection name is missing', async (t) => {
78
+ await t.throwsAsync(
79
+ () => create_index('default', '', 'email'),
80
+ { message: 'Collection name is required' }
81
+ );
82
+ });
83
+
84
+ test('create_index - should throw if field name is missing', async (t) => {
85
+ await t.throwsAsync(
86
+ () => create_index('default', 'users', ''),
87
+ { message: 'Field name is required' }
88
+ );
89
+ });
90
+
91
+ test('drop_index - should drop an index successfully', async (t) => {
92
+ await create_index('default', 'users', 'email');
93
+
94
+ const result = await drop_index('default', 'users', 'email');
95
+ t.is(result.acknowledged, true);
96
+
97
+ const indexes = get_indexes('default', 'users');
98
+ t.is(indexes.length, 0);
99
+ });
100
+
101
+ test('drop_index - should throw if index does not exist', async (t) => {
102
+ await t.throwsAsync(
103
+ () => drop_index('default', 'users', 'email'),
104
+ { message: 'Index on default.users.email does not exist' }
105
+ );
106
+ });
107
+
108
+ test('get_indexes - should return empty array for collection with no indexes', (t) => {
109
+ const indexes = get_indexes('default', 'users');
110
+ t.is(indexes.length, 0);
111
+ });
112
+
113
+ test('get_indexes - should return all indexes for a collection', async (t) => {
114
+ await create_index('default', 'users', 'email');
115
+ await create_index('default', 'users', 'name');
116
+
117
+ const indexes = get_indexes('default', 'users');
118
+ t.is(indexes.length, 2);
119
+
120
+ const fields = indexes.map(index => index.field).sort();
121
+ t.deepEqual(fields, ['email', 'name']);
122
+ });
123
+
124
+ test('update_indexes_on_insert - should update indexes when document is inserted', async (t) => {
125
+ await create_index('default', 'users', 'email');
126
+
127
+ const document = { _id: 'test_id', email: 'test@example.com', name: 'Test User' };
128
+ await update_indexes_on_insert('default', 'users', document);
129
+
130
+ const document_ids = find_documents_by_index('default', 'users', 'email', 'eq', 'test@example.com');
131
+ t.truthy(document_ids);
132
+ t.is(document_ids.length, 1);
133
+ t.is(document_ids[0], 'test_id');
134
+ });
135
+
136
+ test('update_indexes_on_insert - should handle null values with sparse index', async (t) => {
137
+ await create_index('default', 'users', 'email', { sparse: true });
138
+
139
+ const document = { _id: 'test_id', name: 'Test User' };
140
+ await update_indexes_on_insert('default', 'users', document);
141
+
142
+ const document_ids = find_documents_by_index('default', 'users', 'email', 'eq', null);
143
+ t.is(document_ids, null);
144
+ });
145
+
146
+ test('update_indexes_on_insert - should handle null values with non-sparse index', async (t) => {
147
+ await create_index('default', 'users', 'email');
148
+
149
+ const document = { _id: 'test_id', name: 'Test User' };
150
+ await update_indexes_on_insert('default', 'users', document);
151
+
152
+ const document_ids = find_documents_by_index('default', 'users', 'email', 'eq', null);
153
+ t.truthy(document_ids);
154
+ t.is(document_ids.length, 1);
155
+ t.is(document_ids[0], 'test_id');
156
+ });
157
+
158
+ test('update_indexes_on_insert - should enforce unique constraint', async (t) => {
159
+ await create_index('default', 'users', 'email', { unique: true });
160
+
161
+ const document1 = { _id: 'test_id_1', email: 'test@example.com' };
162
+ await update_indexes_on_insert('default', 'users', document1);
163
+
164
+ const document2 = { _id: 'test_id_2', email: 'test@example.com' };
165
+ await t.throwsAsync(
166
+ () => update_indexes_on_insert('default', 'users', document2),
167
+ { message: 'Duplicate value for unique index on default.users.email: test@example.com' }
168
+ );
169
+ });
170
+
171
+ test('update_indexes_on_update - should update indexes when document is updated', async (t) => {
172
+ await create_index('default', 'users', 'email');
173
+
174
+ const old_document = { _id: 'test_id', email: 'old@example.com' };
175
+ const new_document = { _id: 'test_id', email: 'new@example.com' };
176
+
177
+ await update_indexes_on_insert('default', 'users', old_document);
178
+ await update_indexes_on_update('default', 'users', old_document, new_document);
179
+
180
+ const old_ids = find_documents_by_index('default', 'users', 'email', 'eq', 'old@example.com');
181
+ t.is(old_ids, null);
182
+
183
+ const new_ids = find_documents_by_index('default', 'users', 'email', 'eq', 'new@example.com');
184
+ t.truthy(new_ids);
185
+ t.is(new_ids.length, 1);
186
+ t.is(new_ids[0], 'test_id');
187
+ });
188
+
189
+ test('update_indexes_on_delete - should update indexes when document is deleted', async (t) => {
190
+ await create_index('default', 'users', 'email');
191
+
192
+ const document = { _id: 'test_id', email: 'test@example.com' };
193
+ await update_indexes_on_insert('default', 'users', document);
194
+ await update_indexes_on_delete('default', 'users', document);
195
+
196
+ const document_ids = find_documents_by_index('default', 'users', 'email', 'eq', 'test@example.com');
197
+ t.is(document_ids, null);
198
+ });
199
+
200
+ test('find_documents_by_index - should find documents by exact match', async (t) => {
201
+ await create_index('default', 'users', 'email');
202
+
203
+ const document = { _id: 'test_id', email: 'test@example.com' };
204
+ await update_indexes_on_insert('default', 'users', document);
205
+
206
+ const document_ids = find_documents_by_index('default', 'users', 'email', '$eq', 'test@example.com');
207
+ t.truthy(document_ids);
208
+ t.is(document_ids.length, 1);
209
+ t.is(document_ids[0], 'test_id');
210
+ });
211
+
212
+ test('find_documents_by_index - should find documents by $in operator', async (t) => {
213
+ await create_index('default', 'users', 'status');
214
+
215
+ const doc1 = { _id: 'id1', status: 'active' };
216
+ const doc2 = { _id: 'id2', status: 'inactive' };
217
+ const doc3 = { _id: 'id3', status: 'pending' };
218
+
219
+ await update_indexes_on_insert('default', 'users', doc1);
220
+ await update_indexes_on_insert('default', 'users', doc2);
221
+ await update_indexes_on_insert('default', 'users', doc3);
222
+
223
+ const document_ids = find_documents_by_index('default', 'users', 'status', '$in', ['active', 'pending']);
224
+ t.truthy(document_ids);
225
+ t.is(document_ids.length, 2);
226
+ t.true(document_ids.includes('id1'));
227
+ t.true(document_ids.includes('id3'));
228
+ });
229
+
230
+ test('find_documents_by_index - should find documents by $exists operator', async (t) => {
231
+ await create_index('default', 'users', 'email');
232
+
233
+ const doc1 = { _id: 'id1', email: 'test@example.com' };
234
+ const doc2 = { _id: 'id2', name: 'Test User' };
235
+
236
+ await update_indexes_on_insert('default', 'users', doc1);
237
+ await update_indexes_on_insert('default', 'users', doc2);
238
+
239
+ const with_email = find_documents_by_index('default', 'users', 'email', '$exists', true);
240
+ t.truthy(with_email);
241
+ t.is(with_email.length, 1);
242
+ t.is(with_email[0], 'id1');
243
+
244
+ const without_email = find_documents_by_index('default', 'users', 'email', '$exists', false);
245
+ t.truthy(without_email);
246
+ t.is(without_email.length, 1);
247
+ t.is(without_email[0], 'id2');
248
+ });
249
+
250
+ test('can_use_index - should detect when index can be used', async (t) => {
251
+ await create_index('default', 'users', 'email');
252
+
253
+ const filter1 = { email: 'test@example.com' };
254
+ const result1 = can_use_index('default', 'users', filter1);
255
+ t.truthy(result1);
256
+ t.is(result1.field, 'email');
257
+ t.deepEqual(result1.operators, ['eq']);
258
+
259
+ const filter2 = { email: { $eq: 'test@example.com' } };
260
+ const result2 = can_use_index('default', 'users', filter2);
261
+ t.truthy(result2);
262
+ t.is(result2.field, 'email');
263
+ t.deepEqual(result2.operators, ['$eq']);
264
+
265
+ const filter3 = { email: { $in: ['test1@example.com', 'test2@example.com'] } };
266
+ const result3 = can_use_index('default', 'users', filter3);
267
+ t.truthy(result3);
268
+ t.is(result3.field, 'email');
269
+ t.deepEqual(result3.operators, ['$in']);
270
+ });
271
+
272
+ test('can_use_index - should return null when no index can be used', async (t) => {
273
+ await create_index('default', 'users', 'email');
274
+
275
+ const filter1 = { name: 'Test User' };
276
+ const result1 = can_use_index('default', 'users', filter1);
277
+ t.is(result1, null);
278
+
279
+ const filter2 = { email: { $regex: 'test.*' } };
280
+ const result2 = can_use_index('default', 'users', filter2);
281
+ t.is(result2, null);
282
+ });
283
+
284
+ test('nested field indexing - should handle nested object fields', async (t) => {
285
+ await create_index('default', 'users', 'profile.email');
286
+
287
+ const document = {
288
+ _id: 'test_id',
289
+ profile: {
290
+ email: 'test@example.com',
291
+ name: 'Test User'
292
+ }
293
+ };
294
+
295
+ await update_indexes_on_insert('default', 'users', document);
296
+
297
+ const document_ids = find_documents_by_index('default', 'users', 'profile.email', 'eq', 'test@example.com');
298
+ t.truthy(document_ids);
299
+ t.is(document_ids.length, 1);
300
+ t.is(document_ids[0], 'test_id');
301
+ });