mongo.do 0.1.0

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 (707) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +454 -0
  3. package/dist/agentfs/adapters/anthropic.d.ts +176 -0
  4. package/dist/agentfs/adapters/anthropic.d.ts.map +1 -0
  5. package/dist/agentfs/adapters/anthropic.js +629 -0
  6. package/dist/agentfs/adapters/anthropic.js.map +1 -0
  7. package/dist/agentfs/adapters/index.d.ts +21 -0
  8. package/dist/agentfs/adapters/index.d.ts.map +1 -0
  9. package/dist/agentfs/adapters/index.js +23 -0
  10. package/dist/agentfs/adapters/index.js.map +1 -0
  11. package/dist/agentfs/adapters/vercel.d.ts +260 -0
  12. package/dist/agentfs/adapters/vercel.d.ts.map +1 -0
  13. package/dist/agentfs/adapters/vercel.js +288 -0
  14. package/dist/agentfs/adapters/vercel.js.map +1 -0
  15. package/dist/agentfs/glob.d.ts +116 -0
  16. package/dist/agentfs/glob.d.ts.map +1 -0
  17. package/dist/agentfs/glob.js +270 -0
  18. package/dist/agentfs/glob.js.map +1 -0
  19. package/dist/agentfs/grep.d.ts +83 -0
  20. package/dist/agentfs/grep.d.ts.map +1 -0
  21. package/dist/agentfs/grep.js +193 -0
  22. package/dist/agentfs/grep.js.map +1 -0
  23. package/dist/agentfs/index.d.ts +22 -0
  24. package/dist/agentfs/index.d.ts.map +1 -0
  25. package/dist/agentfs/index.js +18 -0
  26. package/dist/agentfs/index.js.map +1 -0
  27. package/dist/agentfs/kv-store.d.ts +128 -0
  28. package/dist/agentfs/kv-store.d.ts.map +1 -0
  29. package/dist/agentfs/kv-store.js +227 -0
  30. package/dist/agentfs/kv-store.js.map +1 -0
  31. package/dist/agentfs/mondo-agent.d.ts +255 -0
  32. package/dist/agentfs/mondo-agent.d.ts.map +1 -0
  33. package/dist/agentfs/mondo-agent.js +879 -0
  34. package/dist/agentfs/mondo-agent.js.map +1 -0
  35. package/dist/agentfs/toolcalls.d.ts +130 -0
  36. package/dist/agentfs/toolcalls.d.ts.map +1 -0
  37. package/dist/agentfs/toolcalls.js +178 -0
  38. package/dist/agentfs/toolcalls.js.map +1 -0
  39. package/dist/agentfs/types.d.ts +171 -0
  40. package/dist/agentfs/types.d.ts.map +1 -0
  41. package/dist/agentfs/types.js +7 -0
  42. package/dist/agentfs/types.js.map +1 -0
  43. package/dist/agentfs/vfs.d.ts +249 -0
  44. package/dist/agentfs/vfs.d.ts.map +1 -0
  45. package/dist/agentfs/vfs.js +469 -0
  46. package/dist/agentfs/vfs.js.map +1 -0
  47. package/dist/cli/index.d.ts +15 -0
  48. package/dist/cli/index.d.ts.map +1 -0
  49. package/dist/cli/index.js +219 -0
  50. package/dist/cli/index.js.map +1 -0
  51. package/dist/cli/mcp.d.ts +119 -0
  52. package/dist/cli/mcp.d.ts.map +1 -0
  53. package/dist/cli/mcp.js +418 -0
  54. package/dist/cli/mcp.js.map +1 -0
  55. package/dist/cli/server.d.ts +179 -0
  56. package/dist/cli/server.d.ts.map +1 -0
  57. package/dist/cli/server.js +441 -0
  58. package/dist/cli/server.js.map +1 -0
  59. package/dist/client/Collection.d.ts +199 -0
  60. package/dist/client/Collection.d.ts.map +1 -0
  61. package/dist/client/Collection.js +256 -0
  62. package/dist/client/Collection.js.map +1 -0
  63. package/dist/client/Database.d.ts +68 -0
  64. package/dist/client/Database.d.ts.map +1 -0
  65. package/dist/client/Database.js +105 -0
  66. package/dist/client/Database.js.map +1 -0
  67. package/dist/client/MongoClient.d.ts +165 -0
  68. package/dist/client/MongoClient.d.ts.map +1 -0
  69. package/dist/client/MongoClient.js +307 -0
  70. package/dist/client/MongoClient.js.map +1 -0
  71. package/dist/client/aggregation-cursor.d.ts +210 -0
  72. package/dist/client/aggregation-cursor.d.ts.map +1 -0
  73. package/dist/client/aggregation-cursor.js +509 -0
  74. package/dist/client/aggregation-cursor.js.map +1 -0
  75. package/dist/client/bulk-write.d.ts +216 -0
  76. package/dist/client/bulk-write.d.ts.map +1 -0
  77. package/dist/client/bulk-write.js +63 -0
  78. package/dist/client/bulk-write.js.map +1 -0
  79. package/dist/client/change-stream.d.ts +245 -0
  80. package/dist/client/change-stream.d.ts.map +1 -0
  81. package/dist/client/change-stream.js +429 -0
  82. package/dist/client/change-stream.js.map +1 -0
  83. package/dist/client/cursor.d.ts +85 -0
  84. package/dist/client/cursor.d.ts.map +1 -0
  85. package/dist/client/cursor.js +156 -0
  86. package/dist/client/cursor.js.map +1 -0
  87. package/dist/client/http-cursor.d.ts +233 -0
  88. package/dist/client/http-cursor.d.ts.map +1 -0
  89. package/dist/client/http-cursor.js +496 -0
  90. package/dist/client/http-cursor.js.map +1 -0
  91. package/dist/client/index.d.ts +18 -0
  92. package/dist/client/index.d.ts.map +1 -0
  93. package/dist/client/index.js +24 -0
  94. package/dist/client/index.js.map +1 -0
  95. package/dist/client/mongo-client.d.ts +60 -0
  96. package/dist/client/mongo-client.d.ts.map +1 -0
  97. package/dist/client/mongo-client.js +190 -0
  98. package/dist/client/mongo-client.js.map +1 -0
  99. package/dist/client/mongo-collection.d.ts +359 -0
  100. package/dist/client/mongo-collection.d.ts.map +1 -0
  101. package/dist/client/mongo-collection.js +1641 -0
  102. package/dist/client/mongo-collection.js.map +1 -0
  103. package/dist/client/mongo-cursor.d.ts +257 -0
  104. package/dist/client/mongo-cursor.d.ts.map +1 -0
  105. package/dist/client/mongo-cursor.js +621 -0
  106. package/dist/client/mongo-cursor.js.map +1 -0
  107. package/dist/client/mongo-database.d.ts +88 -0
  108. package/dist/client/mongo-database.d.ts.map +1 -0
  109. package/dist/client/mongo-database.js +139 -0
  110. package/dist/client/mongo-database.js.map +1 -0
  111. package/dist/client/session.d.ts +210 -0
  112. package/dist/client/session.d.ts.map +1 -0
  113. package/dist/client/session.js +326 -0
  114. package/dist/client/session.js.map +1 -0
  115. package/dist/durable-object/index-manager.d.ts +173 -0
  116. package/dist/durable-object/index-manager.d.ts.map +1 -0
  117. package/dist/durable-object/index-manager.js +764 -0
  118. package/dist/durable-object/index-manager.js.map +1 -0
  119. package/dist/durable-object/index.d.ts +12 -0
  120. package/dist/durable-object/index.d.ts.map +1 -0
  121. package/dist/durable-object/index.js +8 -0
  122. package/dist/durable-object/index.js.map +1 -0
  123. package/dist/durable-object/mcp-handler.d.ts +52 -0
  124. package/dist/durable-object/mcp-handler.d.ts.map +1 -0
  125. package/dist/durable-object/mcp-handler.js +186 -0
  126. package/dist/durable-object/mcp-handler.js.map +1 -0
  127. package/dist/durable-object/migrations.d.ts +40 -0
  128. package/dist/durable-object/migrations.d.ts.map +1 -0
  129. package/dist/durable-object/migrations.js +121 -0
  130. package/dist/durable-object/migrations.js.map +1 -0
  131. package/dist/durable-object/mondo-database.d.ts +148 -0
  132. package/dist/durable-object/mondo-database.d.ts.map +1 -0
  133. package/dist/durable-object/mondo-database.js +621 -0
  134. package/dist/durable-object/mondo-database.js.map +1 -0
  135. package/dist/durable-object/schema.d.ts +192 -0
  136. package/dist/durable-object/schema.d.ts.map +1 -0
  137. package/dist/durable-object/schema.js +186 -0
  138. package/dist/durable-object/schema.js.map +1 -0
  139. package/dist/embedding/document-serializer.d.ts +118 -0
  140. package/dist/embedding/document-serializer.d.ts.map +1 -0
  141. package/dist/embedding/document-serializer.js +339 -0
  142. package/dist/embedding/document-serializer.js.map +1 -0
  143. package/dist/embedding/embedding-manager.d.ts +136 -0
  144. package/dist/embedding/embedding-manager.d.ts.map +1 -0
  145. package/dist/embedding/embedding-manager.js +176 -0
  146. package/dist/embedding/embedding-manager.js.map +1 -0
  147. package/dist/embedding/index.d.ts +9 -0
  148. package/dist/embedding/index.d.ts.map +1 -0
  149. package/dist/embedding/index.js +9 -0
  150. package/dist/embedding/index.js.map +1 -0
  151. package/dist/executor/aggregation-executor.d.ts +93 -0
  152. package/dist/executor/aggregation-executor.d.ts.map +1 -0
  153. package/dist/executor/aggregation-executor.js +275 -0
  154. package/dist/executor/aggregation-executor.js.map +1 -0
  155. package/dist/executor/function-executor.d.ts +39 -0
  156. package/dist/executor/function-executor.d.ts.map +1 -0
  157. package/dist/executor/function-executor.js +168 -0
  158. package/dist/executor/function-executor.js.map +1 -0
  159. package/dist/executor/index.d.ts +4 -0
  160. package/dist/executor/index.d.ts.map +1 -0
  161. package/dist/executor/index.js +4 -0
  162. package/dist/executor/index.js.map +1 -0
  163. package/dist/executor/vector-search-executor.d.ts +71 -0
  164. package/dist/executor/vector-search-executor.d.ts.map +1 -0
  165. package/dist/executor/vector-search-executor.js +113 -0
  166. package/dist/executor/vector-search-executor.js.map +1 -0
  167. package/dist/index.d.ts +21 -0
  168. package/dist/index.d.ts.map +1 -0
  169. package/dist/index.js +28 -0
  170. package/dist/index.js.map +1 -0
  171. package/dist/mcp/adapters/anthropic-adapter.d.ts +256 -0
  172. package/dist/mcp/adapters/anthropic-adapter.d.ts.map +1 -0
  173. package/dist/mcp/adapters/anthropic-adapter.js +409 -0
  174. package/dist/mcp/adapters/anthropic-adapter.js.map +1 -0
  175. package/dist/mcp/adapters/base-adapter.d.ts +164 -0
  176. package/dist/mcp/adapters/base-adapter.d.ts.map +1 -0
  177. package/dist/mcp/adapters/base-adapter.js +277 -0
  178. package/dist/mcp/adapters/base-adapter.js.map +1 -0
  179. package/dist/mcp/adapters/errors.d.ts +173 -0
  180. package/dist/mcp/adapters/errors.d.ts.map +1 -0
  181. package/dist/mcp/adapters/errors.js +305 -0
  182. package/dist/mcp/adapters/errors.js.map +1 -0
  183. package/dist/mcp/adapters/index.d.ts +65 -0
  184. package/dist/mcp/adapters/index.d.ts.map +1 -0
  185. package/dist/mcp/adapters/index.js +92 -0
  186. package/dist/mcp/adapters/index.js.map +1 -0
  187. package/dist/mcp/adapters/streaming.d.ts +200 -0
  188. package/dist/mcp/adapters/streaming.d.ts.map +1 -0
  189. package/dist/mcp/adapters/streaming.js +381 -0
  190. package/dist/mcp/adapters/streaming.js.map +1 -0
  191. package/dist/mcp/adapters/vercel-adapter.d.ts +321 -0
  192. package/dist/mcp/adapters/vercel-adapter.d.ts.map +1 -0
  193. package/dist/mcp/adapters/vercel-adapter.js +487 -0
  194. package/dist/mcp/adapters/vercel-adapter.js.map +1 -0
  195. package/dist/mcp/agent.d.ts +192 -0
  196. package/dist/mcp/agent.d.ts.map +1 -0
  197. package/dist/mcp/agent.js +338 -0
  198. package/dist/mcp/agent.js.map +1 -0
  199. package/dist/mcp/cli.d.ts +71 -0
  200. package/dist/mcp/cli.d.ts.map +1 -0
  201. package/dist/mcp/cli.js +218 -0
  202. package/dist/mcp/cli.js.map +1 -0
  203. package/dist/mcp/index.d.ts +15 -0
  204. package/dist/mcp/index.d.ts.map +1 -0
  205. package/dist/mcp/index.js +20 -0
  206. package/dist/mcp/index.js.map +1 -0
  207. package/dist/mcp/sandbox/database-proxy.d.ts +118 -0
  208. package/dist/mcp/sandbox/database-proxy.d.ts.map +1 -0
  209. package/dist/mcp/sandbox/database-proxy.js +154 -0
  210. package/dist/mcp/sandbox/database-proxy.js.map +1 -0
  211. package/dist/mcp/sandbox/index.d.ts +8 -0
  212. package/dist/mcp/sandbox/index.d.ts.map +1 -0
  213. package/dist/mcp/sandbox/index.js +7 -0
  214. package/dist/mcp/sandbox/index.js.map +1 -0
  215. package/dist/mcp/sandbox/miniflare-evaluator.d.ts +72 -0
  216. package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +1 -0
  217. package/dist/mcp/sandbox/miniflare-evaluator.js +379 -0
  218. package/dist/mcp/sandbox/miniflare-evaluator.js.map +1 -0
  219. package/dist/mcp/sandbox/template.d.ts +48 -0
  220. package/dist/mcp/sandbox/template.d.ts.map +1 -0
  221. package/dist/mcp/sandbox/template.js +147 -0
  222. package/dist/mcp/sandbox/template.js.map +1 -0
  223. package/dist/mcp/sandbox/worker-evaluator.d.ts +160 -0
  224. package/dist/mcp/sandbox/worker-evaluator.d.ts.map +1 -0
  225. package/dist/mcp/sandbox/worker-evaluator.js +217 -0
  226. package/dist/mcp/sandbox/worker-evaluator.js.map +1 -0
  227. package/dist/mcp/server.d.ts +75 -0
  228. package/dist/mcp/server.d.ts.map +1 -0
  229. package/dist/mcp/server.js +278 -0
  230. package/dist/mcp/server.js.map +1 -0
  231. package/dist/mcp/tool-call-auditor.d.ts +188 -0
  232. package/dist/mcp/tool-call-auditor.d.ts.map +1 -0
  233. package/dist/mcp/tool-call-auditor.js +198 -0
  234. package/dist/mcp/tool-call-auditor.js.map +1 -0
  235. package/dist/mcp/tools/do.d.ts +51 -0
  236. package/dist/mcp/tools/do.d.ts.map +1 -0
  237. package/dist/mcp/tools/do.js +113 -0
  238. package/dist/mcp/tools/do.js.map +1 -0
  239. package/dist/mcp/tools/fetch.d.ts +43 -0
  240. package/dist/mcp/tools/fetch.d.ts.map +1 -0
  241. package/dist/mcp/tools/fetch.js +127 -0
  242. package/dist/mcp/tools/fetch.js.map +1 -0
  243. package/dist/mcp/tools/index.d.ts +9 -0
  244. package/dist/mcp/tools/index.d.ts.map +1 -0
  245. package/dist/mcp/tools/index.js +9 -0
  246. package/dist/mcp/tools/index.js.map +1 -0
  247. package/dist/mcp/tools/search.d.ts +60 -0
  248. package/dist/mcp/tools/search.d.ts.map +1 -0
  249. package/dist/mcp/tools/search.js +278 -0
  250. package/dist/mcp/tools/search.js.map +1 -0
  251. package/dist/mcp/transport/http.d.ts +144 -0
  252. package/dist/mcp/transport/http.d.ts.map +1 -0
  253. package/dist/mcp/transport/http.js +545 -0
  254. package/dist/mcp/transport/http.js.map +1 -0
  255. package/dist/mcp/transport/index.d.ts +10 -0
  256. package/dist/mcp/transport/index.d.ts.map +1 -0
  257. package/dist/mcp/transport/index.js +12 -0
  258. package/dist/mcp/transport/index.js.map +1 -0
  259. package/dist/mcp/transport/stdio.d.ts +132 -0
  260. package/dist/mcp/transport/stdio.d.ts.map +1 -0
  261. package/dist/mcp/transport/stdio.js +466 -0
  262. package/dist/mcp/transport/stdio.js.map +1 -0
  263. package/dist/mcp/types.d.ts +476 -0
  264. package/dist/mcp/types.d.ts.map +1 -0
  265. package/dist/mcp/types.js +178 -0
  266. package/dist/mcp/types.js.map +1 -0
  267. package/dist/olap/cdc/cdc-buffer.d.ts +92 -0
  268. package/dist/olap/cdc/cdc-buffer.d.ts.map +1 -0
  269. package/dist/olap/cdc/cdc-buffer.js +146 -0
  270. package/dist/olap/cdc/cdc-buffer.js.map +1 -0
  271. package/dist/olap/cdc/cdc-emitter.d.ts +118 -0
  272. package/dist/olap/cdc/cdc-emitter.d.ts.map +1 -0
  273. package/dist/olap/cdc/cdc-emitter.js +217 -0
  274. package/dist/olap/cdc/cdc-emitter.js.map +1 -0
  275. package/dist/olap/cdc/cdc-schema.d.ts +119 -0
  276. package/dist/olap/cdc/cdc-schema.d.ts.map +1 -0
  277. package/dist/olap/cdc/cdc-schema.js +253 -0
  278. package/dist/olap/cdc/cdc-schema.js.map +1 -0
  279. package/dist/olap/cdc/index.d.ts +10 -0
  280. package/dist/olap/cdc/index.d.ts.map +1 -0
  281. package/dist/olap/cdc/index.js +10 -0
  282. package/dist/olap/cdc/index.js.map +1 -0
  283. package/dist/olap/clickhouse/iceberg.d.ts +164 -0
  284. package/dist/olap/clickhouse/iceberg.d.ts.map +1 -0
  285. package/dist/olap/clickhouse/iceberg.js +138 -0
  286. package/dist/olap/clickhouse/iceberg.js.map +1 -0
  287. package/dist/olap/clickhouse/index.d.ts +14 -0
  288. package/dist/olap/clickhouse/index.d.ts.map +1 -0
  289. package/dist/olap/clickhouse/index.js +14 -0
  290. package/dist/olap/clickhouse/index.js.map +1 -0
  291. package/dist/olap/clickhouse/mapper.d.ts +170 -0
  292. package/dist/olap/clickhouse/mapper.d.ts.map +1 -0
  293. package/dist/olap/clickhouse/mapper.js +654 -0
  294. package/dist/olap/clickhouse/mapper.js.map +1 -0
  295. package/dist/olap/clickhouse/olap-backend.d.ts +181 -0
  296. package/dist/olap/clickhouse/olap-backend.d.ts.map +1 -0
  297. package/dist/olap/clickhouse/olap-backend.js +1083 -0
  298. package/dist/olap/clickhouse/olap-backend.js.map +1 -0
  299. package/dist/olap/clickhouse/query-executor.d.ts +163 -0
  300. package/dist/olap/clickhouse/query-executor.d.ts.map +1 -0
  301. package/dist/olap/clickhouse/query-executor.js +560 -0
  302. package/dist/olap/clickhouse/query-executor.js.map +1 -0
  303. package/dist/olap/clickhouse/query.d.ts +134 -0
  304. package/dist/olap/clickhouse/query.d.ts.map +1 -0
  305. package/dist/olap/clickhouse/query.js +512 -0
  306. package/dist/olap/clickhouse/query.js.map +1 -0
  307. package/dist/olap/stage/index.d.ts +6 -0
  308. package/dist/olap/stage/index.d.ts.map +1 -0
  309. package/dist/olap/stage/index.js +6 -0
  310. package/dist/olap/stage/index.js.map +1 -0
  311. package/dist/olap/stage/parser.d.ts +68 -0
  312. package/dist/olap/stage/parser.d.ts.map +1 -0
  313. package/dist/olap/stage/parser.js +293 -0
  314. package/dist/olap/stage/parser.js.map +1 -0
  315. package/dist/olap/stage/router.d.ts +94 -0
  316. package/dist/olap/stage/router.d.ts.map +1 -0
  317. package/dist/olap/stage/router.js +390 -0
  318. package/dist/olap/stage/router.js.map +1 -0
  319. package/dist/rpc/endpoint.d.ts +52 -0
  320. package/dist/rpc/endpoint.d.ts.map +1 -0
  321. package/dist/rpc/endpoint.js +734 -0
  322. package/dist/rpc/endpoint.js.map +1 -0
  323. package/dist/rpc/index.d.ts +34 -0
  324. package/dist/rpc/index.d.ts.map +1 -0
  325. package/dist/rpc/index.js +45 -0
  326. package/dist/rpc/index.js.map +1 -0
  327. package/dist/rpc/rpc-client.d.ts +275 -0
  328. package/dist/rpc/rpc-client.d.ts.map +1 -0
  329. package/dist/rpc/rpc-client.js +735 -0
  330. package/dist/rpc/rpc-client.js.map +1 -0
  331. package/dist/rpc/rpc-target.d.ts +220 -0
  332. package/dist/rpc/rpc-target.d.ts.map +1 -0
  333. package/dist/rpc/rpc-target.js +500 -0
  334. package/dist/rpc/rpc-target.js.map +1 -0
  335. package/dist/rpc/worker-entrypoint.d.ts +159 -0
  336. package/dist/rpc/worker-entrypoint.d.ts.map +1 -0
  337. package/dist/rpc/worker-entrypoint.js +212 -0
  338. package/dist/rpc/worker-entrypoint.js.map +1 -0
  339. package/dist/server.d.ts +18 -0
  340. package/dist/server.d.ts.map +1 -0
  341. package/dist/server.js +129 -0
  342. package/dist/server.js.map +1 -0
  343. package/dist/studio/components/browser/CollectionItem.d.ts +26 -0
  344. package/dist/studio/components/browser/CollectionItem.d.ts.map +1 -0
  345. package/dist/studio/components/browser/CollectionItem.js +143 -0
  346. package/dist/studio/components/browser/CollectionItem.js.map +1 -0
  347. package/dist/studio/components/browser/CollectionTree.d.ts +45 -0
  348. package/dist/studio/components/browser/CollectionTree.d.ts.map +1 -0
  349. package/dist/studio/components/browser/CollectionTree.js +207 -0
  350. package/dist/studio/components/browser/CollectionTree.js.map +1 -0
  351. package/dist/studio/components/browser/ConnectedDatabaseBrowser.d.ts +51 -0
  352. package/dist/studio/components/browser/ConnectedDatabaseBrowser.d.ts.map +1 -0
  353. package/dist/studio/components/browser/ConnectedDatabaseBrowser.js +185 -0
  354. package/dist/studio/components/browser/ConnectedDatabaseBrowser.js.map +1 -0
  355. package/dist/studio/components/browser/DatabaseBrowser.d.ts +46 -0
  356. package/dist/studio/components/browser/DatabaseBrowser.d.ts.map +1 -0
  357. package/dist/studio/components/browser/DatabaseBrowser.js +304 -0
  358. package/dist/studio/components/browser/DatabaseBrowser.js.map +1 -0
  359. package/dist/studio/components/browser/__tests__/CollectionItem.test.d.ts +5 -0
  360. package/dist/studio/components/browser/__tests__/CollectionItem.test.d.ts.map +1 -0
  361. package/dist/studio/components/browser/__tests__/CollectionItem.test.js +169 -0
  362. package/dist/studio/components/browser/__tests__/CollectionItem.test.js.map +1 -0
  363. package/dist/studio/components/browser/__tests__/CollectionTree.test.d.ts +5 -0
  364. package/dist/studio/components/browser/__tests__/CollectionTree.test.d.ts.map +1 -0
  365. package/dist/studio/components/browser/__tests__/CollectionTree.test.js +203 -0
  366. package/dist/studio/components/browser/__tests__/CollectionTree.test.js.map +1 -0
  367. package/dist/studio/components/browser/__tests__/DatabaseBrowser.e2e.test.d.ts +8 -0
  368. package/dist/studio/components/browser/__tests__/DatabaseBrowser.e2e.test.d.ts.map +1 -0
  369. package/dist/studio/components/browser/__tests__/DatabaseBrowser.e2e.test.js +522 -0
  370. package/dist/studio/components/browser/__tests__/DatabaseBrowser.e2e.test.js.map +1 -0
  371. package/dist/studio/components/browser/__tests__/DatabaseBrowser.test.d.ts +5 -0
  372. package/dist/studio/components/browser/__tests__/DatabaseBrowser.test.d.ts.map +1 -0
  373. package/dist/studio/components/browser/__tests__/DatabaseBrowser.test.js +518 -0
  374. package/dist/studio/components/browser/__tests__/DatabaseBrowser.test.js.map +1 -0
  375. package/dist/studio/components/browser/__tests__/setup.d.ts +5 -0
  376. package/dist/studio/components/browser/__tests__/setup.d.ts.map +1 -0
  377. package/dist/studio/components/browser/__tests__/setup.js +22 -0
  378. package/dist/studio/components/browser/__tests__/setup.js.map +1 -0
  379. package/dist/studio/components/browser/index.d.ts +15 -0
  380. package/dist/studio/components/browser/index.d.ts.map +1 -0
  381. package/dist/studio/components/browser/index.js +10 -0
  382. package/dist/studio/components/browser/index.js.map +1 -0
  383. package/dist/studio/components/browser/types.d.ts +33 -0
  384. package/dist/studio/components/browser/types.d.ts.map +1 -0
  385. package/dist/studio/components/browser/types.js +5 -0
  386. package/dist/studio/components/browser/types.js.map +1 -0
  387. package/dist/studio/components/connection/ConnectionForm.d.ts +59 -0
  388. package/dist/studio/components/connection/ConnectionForm.d.ts.map +1 -0
  389. package/dist/studio/components/connection/ConnectionForm.js +274 -0
  390. package/dist/studio/components/connection/ConnectionForm.js.map +1 -0
  391. package/dist/studio/components/connection/ConnectionList.d.ts +59 -0
  392. package/dist/studio/components/connection/ConnectionList.d.ts.map +1 -0
  393. package/dist/studio/components/connection/ConnectionList.js +286 -0
  394. package/dist/studio/components/connection/ConnectionList.js.map +1 -0
  395. package/dist/studio/components/connection/ConnectionPanel.d.ts +132 -0
  396. package/dist/studio/components/connection/ConnectionPanel.d.ts.map +1 -0
  397. package/dist/studio/components/connection/ConnectionPanel.js +293 -0
  398. package/dist/studio/components/connection/ConnectionPanel.js.map +1 -0
  399. package/dist/studio/components/connection/__tests__/ConnectionPanel.test.d.ts +8 -0
  400. package/dist/studio/components/connection/__tests__/ConnectionPanel.test.d.ts.map +1 -0
  401. package/dist/studio/components/connection/__tests__/ConnectionPanel.test.js +632 -0
  402. package/dist/studio/components/connection/__tests__/ConnectionPanel.test.js.map +1 -0
  403. package/dist/studio/components/connection/__tests__/setup.d.ts +5 -0
  404. package/dist/studio/components/connection/__tests__/setup.d.ts.map +1 -0
  405. package/dist/studio/components/connection/__tests__/setup.js +11 -0
  406. package/dist/studio/components/connection/__tests__/setup.js.map +1 -0
  407. package/dist/studio/components/connection/index.d.ts +10 -0
  408. package/dist/studio/components/connection/index.d.ts.map +1 -0
  409. package/dist/studio/components/connection/index.js +7 -0
  410. package/dist/studio/components/connection/index.js.map +1 -0
  411. package/dist/studio/components/crud/DeleteDocumentDialog.d.ts +91 -0
  412. package/dist/studio/components/crud/DeleteDocumentDialog.d.ts.map +1 -0
  413. package/dist/studio/components/crud/DeleteDocumentDialog.js +273 -0
  414. package/dist/studio/components/crud/DeleteDocumentDialog.js.map +1 -0
  415. package/dist/studio/components/crud/DocumentEditor.d.ts +32 -0
  416. package/dist/studio/components/crud/DocumentEditor.d.ts.map +1 -0
  417. package/dist/studio/components/crud/DocumentEditor.js +546 -0
  418. package/dist/studio/components/crud/DocumentEditor.js.map +1 -0
  419. package/dist/studio/components/crud/InsertDocumentDialog.d.ts +78 -0
  420. package/dist/studio/components/crud/InsertDocumentDialog.d.ts.map +1 -0
  421. package/dist/studio/components/crud/InsertDocumentDialog.js +323 -0
  422. package/dist/studio/components/crud/InsertDocumentDialog.js.map +1 -0
  423. package/dist/studio/components/crud/__tests__/DeleteDocumentDialog.test.d.ts +5 -0
  424. package/dist/studio/components/crud/__tests__/DeleteDocumentDialog.test.d.ts.map +1 -0
  425. package/dist/studio/components/crud/__tests__/DeleteDocumentDialog.test.js +298 -0
  426. package/dist/studio/components/crud/__tests__/DeleteDocumentDialog.test.js.map +1 -0
  427. package/dist/studio/components/crud/__tests__/DocumentEditor.test.d.ts +8 -0
  428. package/dist/studio/components/crud/__tests__/DocumentEditor.test.d.ts.map +1 -0
  429. package/dist/studio/components/crud/__tests__/DocumentEditor.test.js +368 -0
  430. package/dist/studio/components/crud/__tests__/DocumentEditor.test.js.map +1 -0
  431. package/dist/studio/components/crud/__tests__/InsertDocumentDialog.test.d.ts +2 -0
  432. package/dist/studio/components/crud/__tests__/InsertDocumentDialog.test.d.ts.map +1 -0
  433. package/dist/studio/components/crud/__tests__/InsertDocumentDialog.test.js +352 -0
  434. package/dist/studio/components/crud/__tests__/InsertDocumentDialog.test.js.map +1 -0
  435. package/dist/studio/components/crud/__tests__/setup.d.ts +5 -0
  436. package/dist/studio/components/crud/__tests__/setup.d.ts.map +1 -0
  437. package/dist/studio/components/crud/__tests__/setup.js +22 -0
  438. package/dist/studio/components/crud/__tests__/setup.js.map +1 -0
  439. package/dist/studio/components/crud/index.d.ts +12 -0
  440. package/dist/studio/components/crud/index.d.ts.map +1 -0
  441. package/dist/studio/components/crud/index.js +11 -0
  442. package/dist/studio/components/crud/index.js.map +1 -0
  443. package/dist/studio/hooks/useConnection.d.ts +127 -0
  444. package/dist/studio/hooks/useConnection.d.ts.map +1 -0
  445. package/dist/studio/hooks/useConnection.js +414 -0
  446. package/dist/studio/hooks/useConnection.js.map +1 -0
  447. package/dist/studio/hooks/useDatabaseBrowser.d.ts +107 -0
  448. package/dist/studio/hooks/useDatabaseBrowser.d.ts.map +1 -0
  449. package/dist/studio/hooks/useDatabaseBrowser.js +294 -0
  450. package/dist/studio/hooks/useDatabaseBrowser.js.map +1 -0
  451. package/dist/studio/index.d.ts +16 -0
  452. package/dist/studio/index.d.ts.map +1 -0
  453. package/dist/studio/index.js +14 -0
  454. package/dist/studio/index.js.map +1 -0
  455. package/dist/studio/types/connection.d.ts +266 -0
  456. package/dist/studio/types/connection.d.ts.map +1 -0
  457. package/dist/studio/types/connection.js +159 -0
  458. package/dist/studio/types/connection.js.map +1 -0
  459. package/dist/studio/vitest.config.d.ts +3 -0
  460. package/dist/studio/vitest.config.d.ts.map +1 -0
  461. package/dist/studio/vitest.config.js +33 -0
  462. package/dist/studio/vitest.config.js.map +1 -0
  463. package/dist/translator/aggregation-translator.d.ts +51 -0
  464. package/dist/translator/aggregation-translator.d.ts.map +1 -0
  465. package/dist/translator/aggregation-translator.js +324 -0
  466. package/dist/translator/aggregation-translator.js.map +1 -0
  467. package/dist/translator/dialect.d.ts +131 -0
  468. package/dist/translator/dialect.d.ts.map +1 -0
  469. package/dist/translator/dialect.js +276 -0
  470. package/dist/translator/dialect.js.map +1 -0
  471. package/dist/translator/geo-translator.d.ts +91 -0
  472. package/dist/translator/geo-translator.d.ts.map +1 -0
  473. package/dist/translator/geo-translator.js +587 -0
  474. package/dist/translator/geo-translator.js.map +1 -0
  475. package/dist/translator/hybrid-translator.d.ts +70 -0
  476. package/dist/translator/hybrid-translator.d.ts.map +1 -0
  477. package/dist/translator/hybrid-translator.js +193 -0
  478. package/dist/translator/hybrid-translator.js.map +1 -0
  479. package/dist/translator/index.d.ts +13 -0
  480. package/dist/translator/index.d.ts.map +1 -0
  481. package/dist/translator/index.js +11 -0
  482. package/dist/translator/index.js.map +1 -0
  483. package/dist/translator/query-translator.d.ts +211 -0
  484. package/dist/translator/query-translator.d.ts.map +1 -0
  485. package/dist/translator/query-translator.js +1276 -0
  486. package/dist/translator/query-translator.js.map +1 -0
  487. package/dist/translator/search-highlight.d.ts +83 -0
  488. package/dist/translator/search-highlight.d.ts.map +1 -0
  489. package/dist/translator/search-highlight.js +83 -0
  490. package/dist/translator/search-highlight.js.map +1 -0
  491. package/dist/translator/search-translator.d.ts +155 -0
  492. package/dist/translator/search-translator.d.ts.map +1 -0
  493. package/dist/translator/search-translator.js +241 -0
  494. package/dist/translator/search-translator.js.map +1 -0
  495. package/dist/translator/stages/add-fields-stage.d.ts +7 -0
  496. package/dist/translator/stages/add-fields-stage.d.ts.map +1 -0
  497. package/dist/translator/stages/add-fields-stage.js +72 -0
  498. package/dist/translator/stages/add-fields-stage.js.map +1 -0
  499. package/dist/translator/stages/bucket-stage.d.ts +7 -0
  500. package/dist/translator/stages/bucket-stage.d.ts.map +1 -0
  501. package/dist/translator/stages/bucket-stage.js +87 -0
  502. package/dist/translator/stages/bucket-stage.js.map +1 -0
  503. package/dist/translator/stages/count-stage.d.ts +7 -0
  504. package/dist/translator/stages/count-stage.d.ts.map +1 -0
  505. package/dist/translator/stages/count-stage.js +12 -0
  506. package/dist/translator/stages/count-stage.js.map +1 -0
  507. package/dist/translator/stages/expression-translator.d.ts +68 -0
  508. package/dist/translator/stages/expression-translator.d.ts.map +1 -0
  509. package/dist/translator/stages/expression-translator.js +467 -0
  510. package/dist/translator/stages/expression-translator.js.map +1 -0
  511. package/dist/translator/stages/facet-stage.d.ts +13 -0
  512. package/dist/translator/stages/facet-stage.d.ts.map +1 -0
  513. package/dist/translator/stages/facet-stage.js +26 -0
  514. package/dist/translator/stages/facet-stage.js.map +1 -0
  515. package/dist/translator/stages/fusion-stages.d.ts +118 -0
  516. package/dist/translator/stages/fusion-stages.d.ts.map +1 -0
  517. package/dist/translator/stages/fusion-stages.js +201 -0
  518. package/dist/translator/stages/fusion-stages.js.map +1 -0
  519. package/dist/translator/stages/group-stage.d.ts +8 -0
  520. package/dist/translator/stages/group-stage.d.ts.map +1 -0
  521. package/dist/translator/stages/group-stage.js +123 -0
  522. package/dist/translator/stages/group-stage.js.map +1 -0
  523. package/dist/translator/stages/index.d.ts +24 -0
  524. package/dist/translator/stages/index.d.ts.map +1 -0
  525. package/dist/translator/stages/index.js +24 -0
  526. package/dist/translator/stages/index.js.map +1 -0
  527. package/dist/translator/stages/join-optimizer.d.ts +37 -0
  528. package/dist/translator/stages/join-optimizer.d.ts.map +1 -0
  529. package/dist/translator/stages/join-optimizer.js +93 -0
  530. package/dist/translator/stages/join-optimizer.js.map +1 -0
  531. package/dist/translator/stages/limit-stage.d.ts +7 -0
  532. package/dist/translator/stages/limit-stage.d.ts.map +1 -0
  533. package/dist/translator/stages/limit-stage.js +11 -0
  534. package/dist/translator/stages/limit-stage.js.map +1 -0
  535. package/dist/translator/stages/lookup-stage.d.ts +7 -0
  536. package/dist/translator/stages/lookup-stage.d.ts.map +1 -0
  537. package/dist/translator/stages/lookup-stage.js +73 -0
  538. package/dist/translator/stages/lookup-stage.js.map +1 -0
  539. package/dist/translator/stages/match-stage.d.ts +7 -0
  540. package/dist/translator/stages/match-stage.d.ts.map +1 -0
  541. package/dist/translator/stages/match-stage.js +14 -0
  542. package/dist/translator/stages/match-stage.js.map +1 -0
  543. package/dist/translator/stages/optimizer.d.ts +15 -0
  544. package/dist/translator/stages/optimizer.d.ts.map +1 -0
  545. package/dist/translator/stages/optimizer.js +249 -0
  546. package/dist/translator/stages/optimizer.js.map +1 -0
  547. package/dist/translator/stages/parallel-facet.d.ts +47 -0
  548. package/dist/translator/stages/parallel-facet.d.ts.map +1 -0
  549. package/dist/translator/stages/parallel-facet.js +57 -0
  550. package/dist/translator/stages/parallel-facet.js.map +1 -0
  551. package/dist/translator/stages/project-stage.d.ts +8 -0
  552. package/dist/translator/stages/project-stage.d.ts.map +1 -0
  553. package/dist/translator/stages/project-stage.js +145 -0
  554. package/dist/translator/stages/project-stage.js.map +1 -0
  555. package/dist/translator/stages/search-stage.d.ts +60 -0
  556. package/dist/translator/stages/search-stage.d.ts.map +1 -0
  557. package/dist/translator/stages/search-stage.js +89 -0
  558. package/dist/translator/stages/search-stage.js.map +1 -0
  559. package/dist/translator/stages/skip-stage.d.ts +7 -0
  560. package/dist/translator/stages/skip-stage.d.ts.map +1 -0
  561. package/dist/translator/stages/skip-stage.js +11 -0
  562. package/dist/translator/stages/skip-stage.js.map +1 -0
  563. package/dist/translator/stages/sort-stage.d.ts +7 -0
  564. package/dist/translator/stages/sort-stage.d.ts.map +1 -0
  565. package/dist/translator/stages/sort-stage.js +21 -0
  566. package/dist/translator/stages/sort-stage.js.map +1 -0
  567. package/dist/translator/stages/types.d.ts +136 -0
  568. package/dist/translator/stages/types.d.ts.map +1 -0
  569. package/dist/translator/stages/types.js +5 -0
  570. package/dist/translator/stages/types.js.map +1 -0
  571. package/dist/translator/stages/unwind-stage.d.ts +7 -0
  572. package/dist/translator/stages/unwind-stage.d.ts.map +1 -0
  573. package/dist/translator/stages/unwind-stage.js +61 -0
  574. package/dist/translator/stages/unwind-stage.js.map +1 -0
  575. package/dist/translator/stages/vector-search-stage.d.ts +53 -0
  576. package/dist/translator/stages/vector-search-stage.d.ts.map +1 -0
  577. package/dist/translator/stages/vector-search-stage.js +62 -0
  578. package/dist/translator/stages/vector-search-stage.js.map +1 -0
  579. package/dist/translator/update-translator.d.ts +148 -0
  580. package/dist/translator/update-translator.d.ts.map +1 -0
  581. package/dist/translator/update-translator.js +819 -0
  582. package/dist/translator/update-translator.js.map +1 -0
  583. package/dist/translator/vector-translator.d.ts +89 -0
  584. package/dist/translator/vector-translator.d.ts.map +1 -0
  585. package/dist/translator/vector-translator.js +106 -0
  586. package/dist/translator/vector-translator.js.map +1 -0
  587. package/dist/types/env.d.ts +31 -0
  588. package/dist/types/env.d.ts.map +1 -0
  589. package/dist/types/env.js +5 -0
  590. package/dist/types/env.js.map +1 -0
  591. package/dist/types/function.d.ts +65 -0
  592. package/dist/types/function.d.ts.map +1 -0
  593. package/dist/types/function.js +5 -0
  594. package/dist/types/function.js.map +1 -0
  595. package/dist/types/index.d.ts +137 -0
  596. package/dist/types/index.d.ts.map +1 -0
  597. package/dist/types/index.js +13 -0
  598. package/dist/types/index.js.map +1 -0
  599. package/dist/types/mongodb.d.ts +258 -0
  600. package/dist/types/mongodb.d.ts.map +1 -0
  601. package/dist/types/mongodb.js +5 -0
  602. package/dist/types/mongodb.js.map +1 -0
  603. package/dist/types/objectid.d.ts +130 -0
  604. package/dist/types/objectid.d.ts.map +1 -0
  605. package/dist/types/objectid.js +314 -0
  606. package/dist/types/objectid.js.map +1 -0
  607. package/dist/types/rpc.d.ts +313 -0
  608. package/dist/types/rpc.d.ts.map +1 -0
  609. package/dist/types/rpc.js +136 -0
  610. package/dist/types/rpc.js.map +1 -0
  611. package/dist/types/vectorize.d.ts +136 -0
  612. package/dist/types/vectorize.d.ts.map +1 -0
  613. package/dist/types/vectorize.js +8 -0
  614. package/dist/types/vectorize.js.map +1 -0
  615. package/dist/utils/sql-safety.d.ts +64 -0
  616. package/dist/utils/sql-safety.d.ts.map +1 -0
  617. package/dist/utils/sql-safety.js +112 -0
  618. package/dist/utils/sql-safety.js.map +1 -0
  619. package/dist/validation/document-validator.d.ts +195 -0
  620. package/dist/validation/document-validator.d.ts.map +1 -0
  621. package/dist/validation/document-validator.js +529 -0
  622. package/dist/validation/document-validator.js.map +1 -0
  623. package/dist/vectorize/document-serializer.d.ts +119 -0
  624. package/dist/vectorize/document-serializer.d.ts.map +1 -0
  625. package/dist/vectorize/document-serializer.js +320 -0
  626. package/dist/vectorize/document-serializer.js.map +1 -0
  627. package/dist/wire/auth/index.d.ts +5 -0
  628. package/dist/wire/auth/index.d.ts.map +1 -0
  629. package/dist/wire/auth/index.js +5 -0
  630. package/dist/wire/auth/index.js.map +1 -0
  631. package/dist/wire/auth/scram.d.ts +160 -0
  632. package/dist/wire/auth/scram.d.ts.map +1 -0
  633. package/dist/wire/auth/scram.js +425 -0
  634. package/dist/wire/auth/scram.js.map +1 -0
  635. package/dist/wire/backend/interface.d.ts +168 -0
  636. package/dist/wire/backend/interface.d.ts.map +1 -0
  637. package/dist/wire/backend/interface.js +10 -0
  638. package/dist/wire/backend/interface.js.map +1 -0
  639. package/dist/wire/backend/local-sqlite.d.ts +89 -0
  640. package/dist/wire/backend/local-sqlite.d.ts.map +1 -0
  641. package/dist/wire/backend/local-sqlite.js +1002 -0
  642. package/dist/wire/backend/local-sqlite.js.map +1 -0
  643. package/dist/wire/backend/query-router.d.ts +197 -0
  644. package/dist/wire/backend/query-router.d.ts.map +1 -0
  645. package/dist/wire/backend/query-router.js +590 -0
  646. package/dist/wire/backend/query-router.js.map +1 -0
  647. package/dist/wire/backend/validation.d.ts +26 -0
  648. package/dist/wire/backend/validation.d.ts.map +1 -0
  649. package/dist/wire/backend/validation.js +79 -0
  650. package/dist/wire/backend/validation.js.map +1 -0
  651. package/dist/wire/backend/workers-proxy.d.ts +95 -0
  652. package/dist/wire/backend/workers-proxy.d.ts.map +1 -0
  653. package/dist/wire/backend/workers-proxy.js +429 -0
  654. package/dist/wire/backend/workers-proxy.js.map +1 -0
  655. package/dist/wire/commands/admin.d.ts +49 -0
  656. package/dist/wire/commands/admin.d.ts.map +1 -0
  657. package/dist/wire/commands/admin.js +272 -0
  658. package/dist/wire/commands/admin.js.map +1 -0
  659. package/dist/wire/commands/aggregate.d.ts +15 -0
  660. package/dist/wire/commands/aggregate.d.ts.map +1 -0
  661. package/dist/wire/commands/aggregate.js +98 -0
  662. package/dist/wire/commands/aggregate.js.map +1 -0
  663. package/dist/wire/commands/auth.d.ts +58 -0
  664. package/dist/wire/commands/auth.d.ts.map +1 -0
  665. package/dist/wire/commands/auth.js +158 -0
  666. package/dist/wire/commands/auth.js.map +1 -0
  667. package/dist/wire/commands/crud.d.ts +49 -0
  668. package/dist/wire/commands/crud.d.ts.map +1 -0
  669. package/dist/wire/commands/crud.js +336 -0
  670. package/dist/wire/commands/crud.js.map +1 -0
  671. package/dist/wire/commands/hello.d.ts +35 -0
  672. package/dist/wire/commands/hello.d.ts.map +1 -0
  673. package/dist/wire/commands/hello.js +204 -0
  674. package/dist/wire/commands/hello.js.map +1 -0
  675. package/dist/wire/commands/index.d.ts +24 -0
  676. package/dist/wire/commands/index.d.ts.map +1 -0
  677. package/dist/wire/commands/index.js +145 -0
  678. package/dist/wire/commands/index.js.map +1 -0
  679. package/dist/wire/commands/router.d.ts +46 -0
  680. package/dist/wire/commands/router.d.ts.map +1 -0
  681. package/dist/wire/commands/router.js +151 -0
  682. package/dist/wire/commands/router.js.map +1 -0
  683. package/dist/wire/commands/types.d.ts +51 -0
  684. package/dist/wire/commands/types.d.ts.map +1 -0
  685. package/dist/wire/commands/types.js +15 -0
  686. package/dist/wire/commands/types.js.map +1 -0
  687. package/dist/wire/index.d.ts +15 -0
  688. package/dist/wire/index.d.ts.map +1 -0
  689. package/dist/wire/index.js +19 -0
  690. package/dist/wire/index.js.map +1 -0
  691. package/dist/wire/message.d.ts +49 -0
  692. package/dist/wire/message.d.ts.map +1 -0
  693. package/dist/wire/message.js +299 -0
  694. package/dist/wire/message.js.map +1 -0
  695. package/dist/wire/server.d.ts +145 -0
  696. package/dist/wire/server.d.ts.map +1 -0
  697. package/dist/wire/server.js +284 -0
  698. package/dist/wire/server.js.map +1 -0
  699. package/dist/wire/types.d.ts +140 -0
  700. package/dist/wire/types.d.ts.map +1 -0
  701. package/dist/wire/types.js +64 -0
  702. package/dist/wire/types.js.map +1 -0
  703. package/dist/worker.d.ts +17 -0
  704. package/dist/worker.d.ts.map +1 -0
  705. package/dist/worker.js +22 -0
  706. package/dist/worker.js.map +1 -0
  707. package/package.json +82 -0
@@ -0,0 +1,1276 @@
1
+ /**
2
+ * QueryTranslator - Translates MongoDB-style queries to SQL
3
+ * using json_extract for field access on JSON documents.
4
+ *
5
+ * Features:
6
+ * - Operator registry pattern for extensibility
7
+ * - Automatic flattening of nested $and/$or for SQL optimization
8
+ * - CTE-based optimization for multiple array operations
9
+ * - Parameterized queries for SQL injection prevention
10
+ * - $text operator for full-text search with FTS5
11
+ * - Multi-dialect support (SQLite, ClickHouse)
12
+ */
13
+ import { validateFieldPath } from '../utils/sql-safety.js';
14
+ import { validateDialect, jsonExtract as dialectJsonExtract, jsonType as dialectJsonType, jsonTypeWithPath as dialectJsonTypeWithPath, jsonArrayLength as dialectJsonArrayLength, regexMatch as dialectRegexMatch, } from './dialect.js';
15
+ import { translateExpression } from './stages/expression-translator.js';
16
+ /**
17
+ * MongoDB type to SQLite json_type mapping
18
+ */
19
+ const MONGO_TYPE_TO_SQLITE = {
20
+ string: 'text',
21
+ number: ['integer', 'real'],
22
+ bool: ['true', 'false'],
23
+ boolean: ['true', 'false'],
24
+ array: 'array',
25
+ object: 'object',
26
+ null: 'null',
27
+ };
28
+ const DEFAULT_OPTIONS = {
29
+ useCTE: true,
30
+ flattenLogical: true,
31
+ dialect: 'sqlite',
32
+ };
33
+ /**
34
+ * QueryTranslator - Converts MongoDB query syntax to SQL with json_extract
35
+ */
36
+ export class QueryTranslator {
37
+ options;
38
+ dialect;
39
+ constructor(options = {}) {
40
+ // Validate dialect before merging options
41
+ const dialect = validateDialect(options.dialect);
42
+ this.options = { ...DEFAULT_OPTIONS, ...options, dialect };
43
+ this.dialect = dialect;
44
+ }
45
+ /**
46
+ * Registry of comparison operators and their SQL translations
47
+ */
48
+ comparisonOperators = {
49
+ $eq: (path, value, params) => {
50
+ if (value === null) {
51
+ return `${this.jsonExtract(path)} IS NULL`;
52
+ }
53
+ // SQLite's json_extract returns 1/0 for booleans, so convert JS booleans
54
+ const sqlValue = typeof value === 'boolean' ? (value ? 1 : 0) : value;
55
+ params.push(sqlValue);
56
+ return `${this.jsonExtract(path)} = ?`;
57
+ },
58
+ $ne: (path, value, params) => {
59
+ if (value === null) {
60
+ return `${this.jsonExtract(path)} IS NOT NULL`;
61
+ }
62
+ // SQLite's json_extract returns 1/0 for booleans, so convert JS booleans
63
+ const sqlValue = typeof value === 'boolean' ? (value ? 1 : 0) : value;
64
+ params.push(sqlValue);
65
+ return `${this.jsonExtract(path)} != ?`;
66
+ },
67
+ $gt: (path, value, params) => {
68
+ params.push(value);
69
+ return `${this.jsonExtract(path)} > ?`;
70
+ },
71
+ $gte: (path, value, params) => {
72
+ params.push(value);
73
+ return `${this.jsonExtract(path)} >= ?`;
74
+ },
75
+ $lt: (path, value, params) => {
76
+ params.push(value);
77
+ return `${this.jsonExtract(path)} < ?`;
78
+ },
79
+ $lte: (path, value, params) => {
80
+ params.push(value);
81
+ return `${this.jsonExtract(path)} <= ?`;
82
+ },
83
+ $in: (path, value, params) => {
84
+ const arr = value;
85
+ if (arr.length === 0) {
86
+ return '0 = 1';
87
+ }
88
+ params.push(...arr);
89
+ const placeholders = arr.map(() => '?').join(', ');
90
+ return `${this.jsonExtract(path)} IN (${placeholders})`;
91
+ },
92
+ $nin: (path, value, params) => {
93
+ const arr = value;
94
+ if (arr.length === 0) {
95
+ return '1 = 1';
96
+ }
97
+ params.push(...arr);
98
+ const placeholders = arr.map(() => '?').join(', ');
99
+ return `${this.jsonExtract(path)} NOT IN (${placeholders})`;
100
+ },
101
+ $regex: (path, value, params) => {
102
+ // Handle both { $regex: "pattern" } and { $regex: "pattern", $options: "i" }
103
+ // Also handle direct { field: { $regex: /pattern/i } } form
104
+ // Also support $regexType: 'glob' for SQLite GLOB syntax
105
+ let pattern;
106
+ let options = '';
107
+ let regexType = 'like'; // default to LIKE pattern matching
108
+ if (typeof value === 'string') {
109
+ pattern = value;
110
+ }
111
+ else if (value instanceof RegExp) {
112
+ pattern = value.source;
113
+ options = value.flags;
114
+ }
115
+ else if (value && typeof value === 'object') {
116
+ const regexObj = value;
117
+ pattern = regexObj.$regex || '';
118
+ options = regexObj.$options || '';
119
+ regexType = regexObj.$regexType || 'like';
120
+ }
121
+ else {
122
+ pattern = String(value);
123
+ }
124
+ const fieldExpr = this.jsonExtract(path);
125
+ const caseInsensitive = options.includes('i');
126
+ // Handle GLOB type for SQLite
127
+ if (regexType === 'glob' && this.dialect === 'sqlite') {
128
+ params.push(pattern);
129
+ const typeCheck = `json_type(${fieldExpr}) = 'text'`;
130
+ return `(${typeCheck} AND ${fieldExpr} GLOB ?)`;
131
+ }
132
+ // Convert regex pattern to LIKE pattern
133
+ // This is a simplified conversion that handles common cases
134
+ const likePattern = this.regexToLike(pattern, options);
135
+ params.push(likePattern);
136
+ // First ensure the field is a string type (regex only works on strings)
137
+ if (this.dialect === 'clickhouse') {
138
+ const typeCheck = `JSONType(data, ${path.replace(/^\$\.?/, '').split('.').map(p => `'${p}'`).join(', ')}) = 'String'`;
139
+ const matchExpr = dialectRegexMatch(this.dialect, fieldExpr, '?', caseInsensitive);
140
+ return `(${typeCheck} AND ${matchExpr})`;
141
+ }
142
+ const typeCheck = `json_type(${fieldExpr}) = 'text'`;
143
+ const matchExpr = dialectRegexMatch(this.dialect, fieldExpr, '?', caseInsensitive);
144
+ return `(${typeCheck} AND ${matchExpr})`;
145
+ },
146
+ $mod: (path, value, params) => {
147
+ // $mod: [divisor, remainder] - matches if field % divisor == remainder
148
+ const [divisor, remainder] = value;
149
+ params.push(divisor, remainder);
150
+ // Check that the field is numeric and apply modulo
151
+ // Use CAST to handle float truncation like MongoDB
152
+ return `(json_type(${this.jsonExtract(path)}) IN ('integer', 'real') AND CAST(${this.jsonExtract(path)} AS INTEGER) % ? = ?)`;
153
+ },
154
+ // Bitwise operators
155
+ $bitsAllSet: (path, value, params) => {
156
+ // $bitsAllSet: [bit positions] or bitmask number
157
+ // Matches if all specified bits are set (1)
158
+ const mask = this.resolveBitmask(value);
159
+ params.push(mask, mask);
160
+ // (field & mask) == mask means all bits in mask are set
161
+ return `(json_type(${this.jsonExtract(path)}) IN ('integer', 'real') AND (CAST(${this.jsonExtract(path)} AS INTEGER) & ?) = ?)`;
162
+ },
163
+ $bitsAnyClear: (path, value, params) => {
164
+ // $bitsAnyClear: [bit positions] or bitmask number
165
+ // Matches if any of the specified bits are clear (0)
166
+ const mask = this.resolveBitmask(value);
167
+ params.push(mask, mask);
168
+ // (field & mask) != mask means at least one bit in mask is clear
169
+ return `(json_type(${this.jsonExtract(path)}) IN ('integer', 'real') AND (CAST(${this.jsonExtract(path)} AS INTEGER) & ?) != ?)`;
170
+ },
171
+ $bitsAllClear: (path, value, params) => {
172
+ // $bitsAllClear: [bit positions] or bitmask number
173
+ // Matches if all specified bits are clear (0)
174
+ const mask = this.resolveBitmask(value);
175
+ params.push(mask);
176
+ // (field & mask) == 0 means all bits in mask are clear
177
+ return `(json_type(${this.jsonExtract(path)}) IN ('integer', 'real') AND (CAST(${this.jsonExtract(path)} AS INTEGER) & ?) = 0)`;
178
+ },
179
+ $bitsAnySet: (path, value, params) => {
180
+ // $bitsAnySet: [bit positions] or bitmask number
181
+ // Matches if any of the specified bits are set (1)
182
+ const mask = this.resolveBitmask(value);
183
+ params.push(mask);
184
+ // (field & mask) != 0 means at least one bit in mask is set
185
+ return `(json_type(${this.jsonExtract(path)}) IN ('integer', 'real') AND (CAST(${this.jsonExtract(path)} AS INTEGER) & ?) != 0)`;
186
+ },
187
+ };
188
+ /**
189
+ * Registry of element operators
190
+ */
191
+ elementOperators = {
192
+ $exists: (path, value, _params) => {
193
+ // MongoDB $exists distinguishes between missing fields and null values:
194
+ // - $exists: true -> field exists (including explicit null values)
195
+ // - $exists: false -> field is completely missing from the document
196
+ //
197
+ // SQLite's json_extract returns NULL for both missing fields AND null values,
198
+ // but json_type returns 'null' for explicit nulls and NULL for missing fields.
199
+ // So we use json_type with path directly to properly detect field existence.
200
+ if (path.startsWith('$')) {
201
+ // JSON path - use json_type(data, path) directly for existence checks
202
+ const typeExpr = dialectJsonTypeWithPath(this.dialect, 'data', path);
203
+ if (value) {
204
+ return `${typeExpr} IS NOT NULL`;
205
+ }
206
+ return `${typeExpr} IS NULL`;
207
+ }
208
+ // Direct reference (for elemMatch context) - use json_type
209
+ if (this.dialect === 'clickhouse') {
210
+ if (value) {
211
+ return `JSONType(${path}) IS NOT NULL`;
212
+ }
213
+ return `JSONType(${path}) IS NULL`;
214
+ }
215
+ if (value) {
216
+ return `json_type(${path}) IS NOT NULL`;
217
+ }
218
+ return `json_type(${path}) IS NULL`;
219
+ },
220
+ $type: (path, value, _params) => {
221
+ const mongoType = value;
222
+ const sqliteType = MONGO_TYPE_TO_SQLITE[mongoType];
223
+ const typeExpr = dialectJsonType(this.dialect, 'data', path);
224
+ if (Array.isArray(sqliteType)) {
225
+ if (mongoType === 'number') {
226
+ if (this.dialect === 'clickhouse') {
227
+ return `${typeExpr} IN ('Int64', 'Float64', 'UInt64')`;
228
+ }
229
+ return `${typeExpr} IN ('integer', 'real')`;
230
+ }
231
+ // bool type checks for true/false values
232
+ if (this.dialect === 'clickhouse') {
233
+ return `${typeExpr} = 'Bool'`;
234
+ }
235
+ return `${typeExpr} IN ('true', 'false')`;
236
+ }
237
+ if (this.dialect === 'clickhouse') {
238
+ const chType = mongoType === 'string' ? 'String' : mongoType === 'array' ? 'Array' : mongoType === 'object' ? 'Object' : sqliteType;
239
+ return `${typeExpr} = '${chType}'`;
240
+ }
241
+ return `${typeExpr} = '${sqliteType}'`;
242
+ },
243
+ };
244
+ /**
245
+ * Registry of array operators
246
+ */
247
+ arrayOperators = {
248
+ $size: (path, value, params) => {
249
+ params.push(value);
250
+ const lenExpr = dialectJsonArrayLength(this.dialect, 'data', path);
251
+ return `${lenExpr} = ?`;
252
+ },
253
+ $all: (path, value, params) => {
254
+ const arr = value;
255
+ if (arr.length === 0) {
256
+ return '1 = 1';
257
+ }
258
+ // Each value must exist in the array using EXISTS with json_each
259
+ const conditions = arr.map((v) => {
260
+ params.push(v);
261
+ return `EXISTS (SELECT 1 FROM json_each(${this.jsonExtract(path)}) WHERE value = ?)`;
262
+ });
263
+ return conditions.length === 1
264
+ ? conditions[0]
265
+ : `(${conditions.join(' AND ')})`;
266
+ },
267
+ $elemMatch: (path, value, params) => {
268
+ const conditions = value;
269
+ // Generate subquery for array element matching
270
+ const innerConditions = this.translateElemMatchConditions(conditions, params);
271
+ return `EXISTS (SELECT 1 FROM json_each(${this.jsonExtract(path)}) WHERE ${innerConditions})`;
272
+ },
273
+ };
274
+ /**
275
+ * Main entry point - translate a MongoDB query to SQL
276
+ */
277
+ translate(query) {
278
+ const params = [];
279
+ if (Object.keys(query).length === 0) {
280
+ return { sql: '1 = 1', params: [] };
281
+ }
282
+ // Pre-process to flatten nested logical operators if enabled
283
+ const processedQuery = this.options.flattenLogical
284
+ ? this.flattenLogicalOperators(query)
285
+ : query;
286
+ const sql = this.translateDocument(processedQuery, params);
287
+ return { sql, params };
288
+ }
289
+ /**
290
+ * Flatten nested logical operators of the same type
291
+ * E.g., $and: [{ $and: [a, b] }, c] -> $and: [a, b, c]
292
+ */
293
+ flattenLogicalOperators(query) {
294
+ const result = {};
295
+ for (const [key, value] of Object.entries(query)) {
296
+ if (key === '$and' || key === '$or') {
297
+ const conditions = value;
298
+ const flattened = [];
299
+ for (const condition of conditions) {
300
+ // Recursively flatten nested conditions
301
+ const flatCondition = this.flattenLogicalOperators(condition);
302
+ // If the nested condition is the same logical operator, merge it
303
+ if (Object.keys(flatCondition).length === 1 && flatCondition[key]) {
304
+ const nestedConditions = flatCondition[key];
305
+ flattened.push(...nestedConditions);
306
+ }
307
+ else {
308
+ flattened.push(flatCondition);
309
+ }
310
+ }
311
+ result[key] = flattened;
312
+ }
313
+ else if (key === '$nor') {
314
+ // $nor cannot be flattened the same way, but we still process nested conditions
315
+ const conditions = value;
316
+ result[key] = conditions.map(c => this.flattenLogicalOperators(c));
317
+ }
318
+ else if (key.startsWith('$')) {
319
+ // Other operators, just copy
320
+ result[key] = value;
321
+ }
322
+ else {
323
+ // Field condition - recursively process if it's an object
324
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
325
+ const operators = value;
326
+ const processedOps = {};
327
+ for (const [op, opValue] of Object.entries(operators)) {
328
+ if (op === '$not' && opValue && typeof opValue === 'object') {
329
+ processedOps[op] = this.flattenLogicalOperators(opValue);
330
+ }
331
+ else if (op === '$elemMatch' && opValue && typeof opValue === 'object') {
332
+ processedOps[op] = this.flattenLogicalOperators(opValue);
333
+ }
334
+ else {
335
+ processedOps[op] = opValue;
336
+ }
337
+ }
338
+ result[key] = processedOps;
339
+ }
340
+ else {
341
+ result[key] = value;
342
+ }
343
+ }
344
+ }
345
+ return result;
346
+ }
347
+ /**
348
+ * Translate a query document (top-level or nested)
349
+ */
350
+ translateDocument(query, params) {
351
+ const conditions = [];
352
+ for (const [key, value] of Object.entries(query)) {
353
+ if (key.startsWith('$')) {
354
+ // Logical operator at top level
355
+ const sql = this.translateLogicalOperator(key, value, params);
356
+ conditions.push(sql);
357
+ }
358
+ else {
359
+ // Field condition
360
+ const sql = this.translateField(key, value, params);
361
+ conditions.push(sql);
362
+ }
363
+ }
364
+ if (conditions.length === 0) {
365
+ return '1 = 1';
366
+ }
367
+ if (conditions.length === 1) {
368
+ return conditions[0];
369
+ }
370
+ return `(${conditions.join(' AND ')})`;
371
+ }
372
+ /**
373
+ * Translate a field condition
374
+ */
375
+ translateField(field, value, params) {
376
+ const path = this.fieldToJsonPath(field);
377
+ // Direct value comparison (implicit $eq)
378
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
379
+ return this.comparisonOperators.$eq(path, value, params);
380
+ }
381
+ // Object with operators
382
+ const operators = value;
383
+ const operatorKeys = Object.keys(operators);
384
+ // Check if it's an object with operators
385
+ if (operatorKeys.length > 0 && operatorKeys.every(k => k.startsWith('$'))) {
386
+ return this.translateFieldConditions(path, operators, params, false);
387
+ }
388
+ // Plain object equality (implicit $eq)
389
+ return this.comparisonOperators.$eq(path, value, params);
390
+ }
391
+ /**
392
+ * Translate conditions on a single field
393
+ */
394
+ translateFieldConditions(path, conditions, params, isElemMatch) {
395
+ const sqlParts = [];
396
+ // Check for $regex with sibling $options or $regexType
397
+ const hasRegexWithOptions = '$regex' in conditions && ('$options' in conditions || '$regexType' in conditions);
398
+ for (const [op, value] of Object.entries(conditions)) {
399
+ // Skip $options and $regexType when they're siblings of $regex (handled together with $regex)
400
+ if ((op === '$options' || op === '$regexType') && hasRegexWithOptions) {
401
+ continue;
402
+ }
403
+ let sql;
404
+ if (op === '$not') {
405
+ // $not wraps another operator
406
+ const innerConditions = value;
407
+ const innerSql = this.translateFieldConditions(path, innerConditions, params, isElemMatch);
408
+ sql = `NOT (${innerSql})`;
409
+ }
410
+ else if (op === '$regex' && hasRegexWithOptions) {
411
+ // Handle $regex with sibling $options or $regexType
412
+ const actualPath = isElemMatch ? this.elemMatchFieldPath(path, '') : path;
413
+ const regexValue = {
414
+ $regex: value,
415
+ $options: conditions.$options,
416
+ $regexType: conditions.$regexType
417
+ };
418
+ sql = this.comparisonOperators[op](actualPath, regexValue, params);
419
+ }
420
+ else if (this.comparisonOperators[op]) {
421
+ const actualPath = isElemMatch ? this.elemMatchFieldPath(path, '') : path;
422
+ sql = this.comparisonOperators[op](actualPath, value, params);
423
+ }
424
+ else if (this.elementOperators[op]) {
425
+ const actualPath = isElemMatch ? this.elemMatchFieldPath(path, '') : path;
426
+ sql = this.elementOperators[op](actualPath, value, params);
427
+ }
428
+ else if (this.arrayOperators[op]) {
429
+ const actualPath = isElemMatch ? this.elemMatchFieldPath(path, '') : path;
430
+ sql = this.arrayOperators[op](actualPath, value, params);
431
+ }
432
+ else {
433
+ // Unknown operator - treat as nested field in elemMatch context
434
+ if (isElemMatch) {
435
+ const nestedPath = this.elemMatchFieldPath(path, op.replace('$', ''));
436
+ sql = this.translateFieldConditions(nestedPath, { $eq: value }, params, true);
437
+ }
438
+ else {
439
+ throw new Error(`Unknown operator: ${op}`);
440
+ }
441
+ }
442
+ sqlParts.push(sql);
443
+ }
444
+ if (sqlParts.length === 0) {
445
+ return '1 = 1';
446
+ }
447
+ if (sqlParts.length === 1) {
448
+ return sqlParts[0];
449
+ }
450
+ return `(${sqlParts.join(' AND ')})`;
451
+ }
452
+ /**
453
+ * Translate logical operators ($and, $or, $not, $nor, $text)
454
+ */
455
+ translateLogicalOperator(op, value, params) {
456
+ switch (op) {
457
+ case '$and': {
458
+ const conditions = value;
459
+ if (conditions.length === 0) {
460
+ return '1 = 1';
461
+ }
462
+ const parts = conditions.map(c => this.translateDocument(c, params));
463
+ if (parts.length === 1) {
464
+ return parts[0];
465
+ }
466
+ return `(${parts.join(' AND ')})`;
467
+ }
468
+ case '$or': {
469
+ const conditions = value;
470
+ if (conditions.length === 0) {
471
+ return '0 = 1';
472
+ }
473
+ const parts = conditions.map(c => this.translateDocument(c, params));
474
+ if (parts.length === 1) {
475
+ return parts[0];
476
+ }
477
+ return `(${parts.join(' OR ')})`;
478
+ }
479
+ case '$nor': {
480
+ const conditions = value;
481
+ if (conditions.length === 0) {
482
+ return '1 = 1';
483
+ }
484
+ const parts = conditions.map(c => this.translateDocument(c, params));
485
+ return `NOT (${parts.join(' OR ')})`;
486
+ }
487
+ case '$not': {
488
+ // $not at top level wraps a condition
489
+ const innerSql = this.translateDocument(value, params);
490
+ return `NOT (${innerSql})`;
491
+ }
492
+ case '$text': {
493
+ // $text operator for full-text search
494
+ const textOp = value;
495
+ const { sql } = this.translateTextOperator(textOp, params);
496
+ return sql;
497
+ }
498
+ case '$expr': {
499
+ // $expr allows use of aggregation expressions in the query language
500
+ // It enables comparing fields within the same document
501
+ const exprResult = translateExpression(value, params, this.dialect);
502
+ return exprResult;
503
+ }
504
+ case '$jsonSchema': {
505
+ // $jsonSchema validates documents against a JSON Schema
506
+ const schema = value;
507
+ return this.translateJsonSchema(schema, '$', params);
508
+ }
509
+ case '$where': {
510
+ // $where executes JavaScript expressions - NOT SUPPORTED due to security risks
511
+ // MongoDB's $where allows arbitrary JavaScript execution which is a security risk
512
+ // In a SQL context, we cannot safely execute JavaScript
513
+ throw new Error('$where operator is not supported due to security risks. ' +
514
+ 'Use $expr with aggregation expressions instead for field comparisons.');
515
+ }
516
+ default:
517
+ throw new Error(`Unknown logical operator: ${op}`);
518
+ }
519
+ }
520
+ /**
521
+ * Convert a field name to a JSON path
522
+ * e.g., "a.b.c" -> "$.a.b.c"
523
+ * e.g., "items.0.name" -> "$.items[0].name"
524
+ *
525
+ * SECURITY: Validates field name to prevent SQL injection attacks.
526
+ * @throws Error if field contains invalid characters
527
+ */
528
+ fieldToJsonPath(field) {
529
+ // Validate the entire field path to prevent SQL injection
530
+ validateFieldPath(field);
531
+ const parts = field.split('.');
532
+ let path = '$';
533
+ for (const part of parts) {
534
+ // Check if part is a numeric index
535
+ if (/^\d+$/.test(part)) {
536
+ path += `[${part}]`;
537
+ }
538
+ else {
539
+ path += `.${part}`;
540
+ }
541
+ }
542
+ return path;
543
+ }
544
+ /**
545
+ * Generate json_extract SQL for a path
546
+ */
547
+ jsonExtract(path) {
548
+ // If path starts with $, it's a JSON path
549
+ if (path.startsWith('$')) {
550
+ return dialectJsonExtract(this.dialect, 'data', path);
551
+ }
552
+ // Otherwise, it's a direct reference (for elemMatch context)
553
+ return path;
554
+ }
555
+ /**
556
+ * Convert a bitwise operator value to a bitmask
557
+ * Accepts either:
558
+ * - A number (used directly as bitmask)
559
+ * - An array of bit positions (converted to bitmask)
560
+ */
561
+ resolveBitmask(value) {
562
+ if (typeof value === 'number') {
563
+ return value;
564
+ }
565
+ if (Array.isArray(value)) {
566
+ // Convert array of bit positions to bitmask
567
+ // e.g., [0, 2, 4] -> 0b10101 = 21
568
+ return value.reduce((mask, pos) => mask | (1 << pos), 0);
569
+ }
570
+ throw new Error('Bitwise operator value must be a number or array of bit positions');
571
+ }
572
+ /**
573
+ * Generate path for elemMatch field access
574
+ * SECURITY: Validates field name to prevent SQL injection attacks.
575
+ */
576
+ elemMatchFieldPath(basePath, field) {
577
+ if (basePath === 'value') {
578
+ // Inside json_each, value is the current element
579
+ if (field === '') {
580
+ return 'value';
581
+ }
582
+ // Validate field name to prevent SQL injection
583
+ validateFieldPath(field);
584
+ return `json_extract(value, '$.${field}')`;
585
+ }
586
+ if (field === '') {
587
+ return basePath;
588
+ }
589
+ // Validate field name to prevent SQL injection
590
+ validateFieldPath(field);
591
+ return `${basePath}.${field}`;
592
+ }
593
+ /**
594
+ * Convert a regex pattern to SQLite LIKE/GLOB pattern
595
+ * This handles common regex patterns:
596
+ * - ^pattern -> pattern% (starts with)
597
+ * - pattern$ -> %pattern (ends with)
598
+ * - ^pattern$ -> pattern (exact match)
599
+ * - .* or .+ -> % (any characters)
600
+ * - . -> _ (single character)
601
+ * - [0-9] -> character class (converted to GLOB syntax)
602
+ * - [a-z] -> character class (converted to GLOB syntax)
603
+ * - Literal text -> %text% (contains, default behavior)
604
+ *
605
+ * @param pattern The regex pattern to convert
606
+ * @param options Regex options (i=case-insensitive, m=multiline)
607
+ */
608
+ regexToLike(pattern, options = '') {
609
+ // For multiline mode, we need special handling of ^ and $
610
+ // In multiline mode, ^ matches start of line (after \n) and $ matches before \n
611
+ const isMultiline = options.includes('m');
612
+ // Handle anchors
613
+ let startsWithAnchor = pattern.startsWith('^');
614
+ let endsWithAnchor = pattern.endsWith('$') && !pattern.endsWith('\\$');
615
+ // In multiline mode, anchors match line boundaries, not string boundaries
616
+ // Since LIKE can't match line boundaries, we convert to contains match
617
+ if (isMultiline && (startsWithAnchor || endsWithAnchor)) {
618
+ // For multiline, we treat ^ and $ as matching within the string
619
+ // This is an approximation - LIKE can't truly match line boundaries
620
+ // But we can check for patterns after newline or before newline
621
+ startsWithAnchor = false;
622
+ endsWithAnchor = false;
623
+ }
624
+ // Remove anchors for processing
625
+ let processed = pattern;
626
+ if (pattern.startsWith('^')) {
627
+ processed = processed.slice(1);
628
+ }
629
+ if (processed.endsWith('$') && !processed.endsWith('\\$')) {
630
+ processed = processed.slice(0, -1);
631
+ }
632
+ // Process character by character to handle escaping properly
633
+ let result = '';
634
+ let i = 0;
635
+ while (i < processed.length) {
636
+ const char = processed[i];
637
+ const nextChar = processed[i + 1];
638
+ if (char === '\\' && nextChar !== undefined) {
639
+ // Escaped character in regex - keep literal character
640
+ // But we need to escape it for LIKE if it's a special LIKE character
641
+ if (nextChar === '%' || nextChar === '_') {
642
+ result += '\\' + nextChar;
643
+ }
644
+ else {
645
+ result += nextChar;
646
+ }
647
+ i += 2;
648
+ }
649
+ else if (char === '[') {
650
+ // Character class - find the closing bracket
651
+ const endBracket = processed.indexOf(']', i + 1);
652
+ if (endBracket !== -1) {
653
+ const charClass = processed.slice(i + 1, endBracket);
654
+ // Convert common character classes to approximate LIKE patterns
655
+ // [0-9] -> _ (single digit) or % for multiple
656
+ // [a-zA-Z] -> _ (single letter)
657
+ // For now, use _ as a single character match (approximation)
658
+ if (charClass.includes('+') || processed[endBracket + 1] === '+') {
659
+ result += '%';
660
+ i = endBracket + (processed[endBracket + 1] === '+' ? 2 : 1);
661
+ }
662
+ else if (processed[endBracket + 1] === '*') {
663
+ result += '%';
664
+ i = endBracket + 2;
665
+ }
666
+ else {
667
+ result += '_';
668
+ i = endBracket + 1;
669
+ }
670
+ }
671
+ else {
672
+ // No closing bracket, treat [ as literal
673
+ result += char;
674
+ i += 1;
675
+ }
676
+ }
677
+ else if (char === '.' && nextChar === '*') {
678
+ // .* -> % (any characters)
679
+ result += '%';
680
+ i += 2;
681
+ }
682
+ else if (char === '.' && nextChar === '+') {
683
+ // .+ -> % (one or more characters, approximate with %)
684
+ result += '%';
685
+ i += 2;
686
+ }
687
+ else if (char === '.') {
688
+ // . -> _ (single character)
689
+ result += '_';
690
+ i += 1;
691
+ }
692
+ else if (char === '%') {
693
+ // Escape literal % for LIKE
694
+ result += '\\%';
695
+ i += 1;
696
+ }
697
+ else if (char === '_') {
698
+ // Escape literal _ for LIKE
699
+ result += '\\_';
700
+ i += 1;
701
+ }
702
+ else if (char === '+' || char === '*' || char === '?' || char === '|' || char === '(' || char === ')') {
703
+ // Skip regex quantifiers and grouping - not directly translatable to LIKE
704
+ // These would need more sophisticated handling
705
+ i += 1;
706
+ }
707
+ else {
708
+ // Regular character
709
+ result += char;
710
+ i += 1;
711
+ }
712
+ }
713
+ // Apply wildcards based on anchors
714
+ if (!startsWithAnchor && !endsWithAnchor) {
715
+ // No anchors: match anywhere (contains)
716
+ return `%${result}%`;
717
+ }
718
+ else if (startsWithAnchor && !endsWithAnchor) {
719
+ // Starts with anchor only
720
+ return `${result}%`;
721
+ }
722
+ else if (!startsWithAnchor && endsWithAnchor) {
723
+ // Ends with anchor only
724
+ return `%${result}`;
725
+ }
726
+ else {
727
+ // Both anchors: exact match
728
+ return result;
729
+ }
730
+ }
731
+ /**
732
+ * Translate conditions inside $elemMatch
733
+ * This handles document conditions like { field: value, field: { $op: value } }
734
+ * SECURITY: Validates field names to prevent SQL injection attacks.
735
+ */
736
+ translateElemMatchConditions(conditions, params) {
737
+ const sqlParts = [];
738
+ for (const [field, value] of Object.entries(conditions)) {
739
+ // Validate field name to prevent SQL injection
740
+ validateFieldPath(field);
741
+ // In elemMatch, 'value' refers to the current array element from json_each
742
+ // For nested fields, we use json_extract(value, '$.field')
743
+ const extractPath = `json_extract(value, '$.${field}')`;
744
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
745
+ // Direct value comparison
746
+ if (value === null) {
747
+ sqlParts.push(`${extractPath} IS NULL`);
748
+ }
749
+ else {
750
+ params.push(value);
751
+ sqlParts.push(`${extractPath} = ?`);
752
+ }
753
+ }
754
+ else {
755
+ // Object with operators
756
+ const operators = value;
757
+ const opKeys = Object.keys(operators);
758
+ if (opKeys.length > 0 && opKeys.every(k => k.startsWith('$'))) {
759
+ // It's operators like { $gte: 90 }
760
+ for (const [op, opValue] of Object.entries(operators)) {
761
+ const opSql = this.translateElemMatchOperator(extractPath, op, opValue, params);
762
+ sqlParts.push(opSql);
763
+ }
764
+ }
765
+ else {
766
+ // Plain object equality
767
+ params.push(JSON.stringify(value));
768
+ sqlParts.push(`${extractPath} = json(?)`);
769
+ }
770
+ }
771
+ }
772
+ if (sqlParts.length === 0) {
773
+ return '1 = 1';
774
+ }
775
+ return sqlParts.length === 1 ? sqlParts[0] : `(${sqlParts.join(' AND ')})`;
776
+ }
777
+ /**
778
+ * Translate a single operator for elemMatch context
779
+ */
780
+ translateElemMatchOperator(path, op, value, params) {
781
+ switch (op) {
782
+ case '$eq': {
783
+ if (value === null) {
784
+ return `${path} IS NULL`;
785
+ }
786
+ // SQLite's json_extract returns 1/0 for booleans, so convert JS booleans
787
+ const eqValue = typeof value === 'boolean' ? (value ? 1 : 0) : value;
788
+ params.push(eqValue);
789
+ return `${path} = ?`;
790
+ }
791
+ case '$ne': {
792
+ if (value === null) {
793
+ return `${path} IS NOT NULL`;
794
+ }
795
+ // SQLite's json_extract returns 1/0 for booleans, so convert JS booleans
796
+ const neValue = typeof value === 'boolean' ? (value ? 1 : 0) : value;
797
+ params.push(neValue);
798
+ return `${path} != ?`;
799
+ }
800
+ case '$gt':
801
+ params.push(value);
802
+ return `${path} > ?`;
803
+ case '$gte':
804
+ params.push(value);
805
+ return `${path} >= ?`;
806
+ case '$lt':
807
+ params.push(value);
808
+ return `${path} < ?`;
809
+ case '$lte':
810
+ params.push(value);
811
+ return `${path} <= ?`;
812
+ case '$in': {
813
+ const arr = value;
814
+ if (arr.length === 0)
815
+ return '0 = 1';
816
+ params.push(...arr);
817
+ return `${path} IN (${arr.map(() => '?').join(', ')})`;
818
+ }
819
+ case '$nin': {
820
+ const arr = value;
821
+ if (arr.length === 0)
822
+ return '1 = 1';
823
+ params.push(...arr);
824
+ return `${path} NOT IN (${arr.map(() => '?').join(', ')})`;
825
+ }
826
+ case '$exists':
827
+ // Use json_type to distinguish between null values and missing fields
828
+ return value ? `json_type(${path}) IS NOT NULL` : `json_type(${path}) IS NULL`;
829
+ case '$regex': {
830
+ // Handle $regex in elemMatch context
831
+ let pattern;
832
+ let options = '';
833
+ if (typeof value === 'string') {
834
+ pattern = value;
835
+ }
836
+ else if (value instanceof RegExp) {
837
+ pattern = value.source;
838
+ options = value.flags;
839
+ }
840
+ else if (value && typeof value === 'object') {
841
+ const regexObj = value;
842
+ pattern = regexObj.$regex || '';
843
+ options = regexObj.$options || '';
844
+ }
845
+ else {
846
+ pattern = String(value);
847
+ }
848
+ const likePattern = this.regexToLike(pattern, options);
849
+ params.push(likePattern);
850
+ // Add type check to ensure we only match string values
851
+ const typeCheck = `json_type(${path}) = 'text'`;
852
+ if (options.includes('i')) {
853
+ return `(${typeCheck} AND LOWER(${path}) LIKE LOWER(?))`;
854
+ }
855
+ return `(${typeCheck} AND ${path} LIKE ?)`;
856
+ }
857
+ case '$mod': {
858
+ // Handle $mod in elemMatch context
859
+ const [divisor, remainder] = value;
860
+ params.push(divisor, remainder);
861
+ return `(json_type(${path}) IN ('integer', 'real') AND CAST(${path} AS INTEGER) % ? = ?)`;
862
+ }
863
+ default:
864
+ throw new Error(`Unsupported operator in $elemMatch: ${op}`);
865
+ }
866
+ }
867
+ /**
868
+ * Generate optimized SQL with CTE for multiple array operations on the same field
869
+ * This is useful when you have multiple $all checks or $elemMatch on the same array
870
+ *
871
+ * Example output:
872
+ * WITH array_cte AS (
873
+ * SELECT value FROM json_each(json_extract(data, '$.tags'))
874
+ * )
875
+ * SELECT * FROM documents WHERE
876
+ * EXISTS (SELECT 1 FROM array_cte WHERE value = ?) AND
877
+ * EXISTS (SELECT 1 FROM array_cte WHERE value = ?)
878
+ */
879
+ translateWithCTE(query, tableName = 'documents') {
880
+ const params = [];
881
+ if (Object.keys(query).length === 0) {
882
+ return { sql: `SELECT * FROM ${tableName}`, params: [] };
883
+ }
884
+ // Collect all array fields that have multiple operations
885
+ const arrayFieldOps = this.collectArrayOperations(query);
886
+ const cteDefinitions = [];
887
+ const cteAliases = new Map();
888
+ let cteIndex = 0;
889
+ // Create CTEs for fields with multiple array operations
890
+ for (const [field, count] of arrayFieldOps.entries()) {
891
+ if (count > 1) {
892
+ const alias = `arr_cte_${cteIndex++}`;
893
+ const path = this.fieldToJsonPath(field);
894
+ cteDefinitions.push(`${alias} AS (SELECT value FROM json_each(json_extract(data, '${path}')))`);
895
+ cteAliases.set(field, alias);
896
+ }
897
+ }
898
+ // Translate the query, replacing repeated json_each with CTE references
899
+ const whereClause = this.translateDocumentWithCTE(query, params, cteAliases);
900
+ let sql;
901
+ if (cteDefinitions.length > 0) {
902
+ sql = `WITH ${cteDefinitions.join(', ')} SELECT * FROM ${tableName} WHERE ${whereClause}`;
903
+ }
904
+ else {
905
+ sql = `SELECT * FROM ${tableName} WHERE ${whereClause}`;
906
+ }
907
+ return { sql, params };
908
+ }
909
+ /**
910
+ * Collect array operations for CTE optimization analysis
911
+ */
912
+ collectArrayOperations(query, counts = new Map()) {
913
+ for (const [key, value] of Object.entries(query)) {
914
+ if (key === '$and' || key === '$or' || key === '$nor') {
915
+ const conditions = value;
916
+ for (const condition of conditions) {
917
+ this.collectArrayOperations(condition, counts);
918
+ }
919
+ }
920
+ else if (!key.startsWith('$') && value && typeof value === 'object') {
921
+ const operators = value;
922
+ for (const op of Object.keys(operators)) {
923
+ if (op === '$all' || op === '$elemMatch') {
924
+ counts.set(key, (counts.get(key) || 0) + 1);
925
+ }
926
+ }
927
+ }
928
+ }
929
+ return counts;
930
+ }
931
+ /**
932
+ * Translate document using CTE aliases where applicable
933
+ */
934
+ translateDocumentWithCTE(query, params, _cteAliases) {
935
+ // For now, fall back to standard translation
936
+ // CTE optimization would replace json_each references with CTE aliases
937
+ // This is a placeholder for full CTE implementation
938
+ return this.translateDocument(query, params);
939
+ }
940
+ /**
941
+ * Register a custom comparison operator
942
+ * Allows extending the translator with custom operators
943
+ */
944
+ registerOperator(name, handler) {
945
+ if (!name.startsWith('$')) {
946
+ throw new Error('Operator name must start with $');
947
+ }
948
+ this.comparisonOperators[name] = handler;
949
+ }
950
+ /**
951
+ * Register a custom element operator
952
+ */
953
+ registerElementOperator(name, handler) {
954
+ if (!name.startsWith('$')) {
955
+ throw new Error('Operator name must start with $');
956
+ }
957
+ this.elementOperators[name] = handler;
958
+ }
959
+ /**
960
+ * Register a custom array operator
961
+ */
962
+ registerArrayOperator(name, handler) {
963
+ if (!name.startsWith('$')) {
964
+ throw new Error('Operator name must start with $');
965
+ }
966
+ this.arrayOperators[name] = handler;
967
+ }
968
+ /**
969
+ * Translate a MongoDB $text query to FTS5 MATCH SQL
970
+ */
971
+ translateTextOperator(textOp, params) {
972
+ const search = textOp.$search;
973
+ const caseSensitive = textOp.$caseSensitive;
974
+ const diacriticSensitive = textOp.$diacriticSensitive;
975
+ // Handle empty search string
976
+ if (!search || search.trim() === '') {
977
+ return { sql: '0 = 1', ftsMatch: '' };
978
+ }
979
+ // Convert MongoDB text search syntax to FTS5 syntax
980
+ const ftsQuery = this.convertToFTS5Query(search, caseSensitive, diacriticSensitive);
981
+ params.push(ftsQuery);
982
+ // Generate the FTS5 MATCH condition
983
+ // This will be joined with the main documents table using rowid
984
+ const sql = `id IN (SELECT rowid FROM {{FTS_TABLE}} WHERE {{FTS_TABLE}} MATCH ?)`;
985
+ return { sql, ftsMatch: ftsQuery };
986
+ }
987
+ /**
988
+ * Convert MongoDB text search syntax to FTS5 query syntax
989
+ *
990
+ * MongoDB syntax:
991
+ * - "word" -> matches word
992
+ * - "word1 word2" -> matches word1 OR word2
993
+ * - "\"phrase\"" -> matches exact phrase
994
+ * - "-word" -> excludes word (negation)
995
+ *
996
+ * FTS5 syntax:
997
+ * - "word" -> matches word
998
+ * - "word1 OR word2" -> matches word1 or word2
999
+ * - "word1 word2" -> matches word1 AND word2
1000
+ * - "\"phrase\"" -> matches exact phrase
1001
+ * - "NOT word" -> excludes word
1002
+ */
1003
+ convertToFTS5Query(search, _caseSensitive, _diacriticSensitive) {
1004
+ // Escape special FTS5 characters except quotes and minus
1005
+ const escaped = search.replace(/[&|()^~*:]/g, (char) => {
1006
+ return '\\' + char;
1007
+ });
1008
+ const tokens = [];
1009
+ let remaining = escaped.trim();
1010
+ // Parse the search string for phrases and terms
1011
+ while (remaining.length > 0) {
1012
+ remaining = remaining.trim();
1013
+ // Check for quoted phrase
1014
+ if (remaining.startsWith('"')) {
1015
+ const endQuote = remaining.indexOf('"', 1);
1016
+ if (endQuote > 1) {
1017
+ const phrase = remaining.slice(1, endQuote);
1018
+ tokens.push(`"${phrase}"`);
1019
+ remaining = remaining.slice(endQuote + 1);
1020
+ continue;
1021
+ }
1022
+ }
1023
+ // Check for negation
1024
+ if (remaining.startsWith('-')) {
1025
+ const spaceIdx = remaining.indexOf(' ');
1026
+ const term = spaceIdx > 0 ? remaining.slice(1, spaceIdx) : remaining.slice(1);
1027
+ if (term) {
1028
+ tokens.push(`NOT ${term}`);
1029
+ }
1030
+ remaining = spaceIdx > 0 ? remaining.slice(spaceIdx + 1) : '';
1031
+ continue;
1032
+ }
1033
+ // Regular term
1034
+ const spaceIdx = remaining.indexOf(' ');
1035
+ const term = spaceIdx > 0 ? remaining.slice(0, spaceIdx) : remaining;
1036
+ if (term) {
1037
+ tokens.push(term);
1038
+ }
1039
+ remaining = spaceIdx > 0 ? remaining.slice(spaceIdx + 1) : '';
1040
+ }
1041
+ // Join tokens - MongoDB uses OR by default for multiple terms
1042
+ // FTS5 uses AND by default, so we explicitly use OR
1043
+ if (tokens.length === 0) {
1044
+ return '*'; // Match all if no valid tokens
1045
+ }
1046
+ // Separate NOT terms from regular terms
1047
+ const notTerms = tokens.filter(t => t.startsWith('NOT '));
1048
+ const regularTerms = tokens.filter(t => !t.startsWith('NOT '));
1049
+ let query = '';
1050
+ if (regularTerms.length > 0) {
1051
+ // Use OR for regular terms (MongoDB default behavior)
1052
+ query = regularTerms.join(' OR ');
1053
+ }
1054
+ // Add NOT terms with AND
1055
+ if (notTerms.length > 0) {
1056
+ if (query) {
1057
+ query = `(${query}) AND ${notTerms.join(' AND ')}`;
1058
+ }
1059
+ else {
1060
+ // Only negations - need a base to negate from
1061
+ query = `* AND ${notTerms.join(' AND ')}`;
1062
+ }
1063
+ }
1064
+ return query;
1065
+ }
1066
+ /**
1067
+ * Translate a query with $meta projection support for textScore
1068
+ *
1069
+ * @param query The MongoDB query (must contain $text for textScore)
1070
+ * @param projection The projection with potential {$meta: "textScore"} fields
1071
+ * @param sort Optional sort with potential {$meta: "textScore"} fields
1072
+ */
1073
+ translateWithMeta(query, projection, sort) {
1074
+ const params = [];
1075
+ // Check if query has $text
1076
+ const hasText = '$text' in query;
1077
+ if (!hasText) {
1078
+ // No text search, fall back to regular translation
1079
+ const baseResult = this.translate(query);
1080
+ return baseResult;
1081
+ }
1082
+ // Extract $text operator
1083
+ const textOp = query.$text;
1084
+ const { sql: textSql, ftsMatch } = this.translateTextOperator(textOp, params);
1085
+ // Process remaining query conditions
1086
+ const remainingQuery = {};
1087
+ for (const [key, value] of Object.entries(query)) {
1088
+ if (key !== '$text') {
1089
+ remainingQuery[key] = value;
1090
+ }
1091
+ }
1092
+ let whereClause = textSql;
1093
+ if (Object.keys(remainingQuery).length > 0) {
1094
+ const remainingResult = this.translateDocument(remainingQuery, params);
1095
+ whereClause = `(${textSql}) AND (${remainingResult})`;
1096
+ }
1097
+ // Build SELECT clause with textScore if projected
1098
+ let selectClause = '*';
1099
+ const hasTextScoreProjection = projection && Object.values(projection).some(v => v && typeof v === 'object' && v.$meta === 'textScore');
1100
+ if (hasTextScoreProjection) {
1101
+ // FTS5 uses bm25() for relevance ranking
1102
+ // bm25() returns negative values (more negative = more relevant)
1103
+ // We negate it to get positive scores where higher = more relevant
1104
+ selectClause = '*, -bm25({{FTS_TABLE}}) as rank';
1105
+ }
1106
+ // Build ORDER BY clause
1107
+ let orderByClause = '';
1108
+ if (sort) {
1109
+ const hasTextScoreSort = Object.values(sort).some(v => v && typeof v === 'object' && v.$meta === 'textScore');
1110
+ if (hasTextScoreSort) {
1111
+ // Sort by rank (descending by default for textScore)
1112
+ orderByClause = ' ORDER BY rank DESC';
1113
+ }
1114
+ }
1115
+ return {
1116
+ sql: `SELECT ${selectClause} WHERE ${whereClause}${orderByClause}`,
1117
+ params,
1118
+ requiresFTS: true,
1119
+ ftsMatch,
1120
+ };
1121
+ }
1122
+ /**
1123
+ * Translate a JSON Schema to SQL conditions
1124
+ * Supports common JSON Schema validation keywords
1125
+ *
1126
+ * @param schema The JSON Schema to validate against
1127
+ * @param path The current JSON path (e.g., "$.field.nested")
1128
+ * @param params The parameters array to push values to
1129
+ */
1130
+ translateJsonSchema(schema, path, params) {
1131
+ const conditions = [];
1132
+ const jsonPath = path === '$' ? 'data' : `json_extract(data, '${path}')`;
1133
+ const typeExpr = path === '$'
1134
+ ? 'json_type(data)'
1135
+ : dialectJsonType(this.dialect, 'data', path);
1136
+ // Handle type/bsonType constraint
1137
+ const schemaType = schema.type || schema.bsonType;
1138
+ if (schemaType) {
1139
+ const types = Array.isArray(schemaType) ? schemaType : [schemaType];
1140
+ const typeConditions = types.map(t => {
1141
+ const sqlType = MONGO_TYPE_TO_SQLITE[t];
1142
+ if (Array.isArray(sqlType)) {
1143
+ return `${typeExpr} IN (${sqlType.map(st => `'${st}'`).join(', ')})`;
1144
+ }
1145
+ return `${typeExpr} = '${sqlType || t}'`;
1146
+ });
1147
+ if (typeConditions.length === 1) {
1148
+ conditions.push(typeConditions[0]);
1149
+ }
1150
+ else {
1151
+ conditions.push(`(${typeConditions.join(' OR ')})`);
1152
+ }
1153
+ }
1154
+ // Handle required fields
1155
+ if (schema.required && schema.required.length > 0) {
1156
+ for (const field of schema.required) {
1157
+ validateFieldPath(field);
1158
+ const fieldPath = path === '$' ? `$.${field}` : `${path}.${field}`;
1159
+ const existsExpr = dialectJsonTypeWithPath(this.dialect, 'data', fieldPath);
1160
+ conditions.push(`${existsExpr} IS NOT NULL`);
1161
+ }
1162
+ }
1163
+ // Handle properties
1164
+ if (schema.properties) {
1165
+ for (const [field, propSchema] of Object.entries(schema.properties)) {
1166
+ validateFieldPath(field);
1167
+ const fieldPath = path === '$' ? `$.${field}` : `${path}.${field}`;
1168
+ const existsExpr = dialectJsonTypeWithPath(this.dialect, 'data', fieldPath);
1169
+ // Property validation only applies if the field exists
1170
+ const propCondition = this.translateJsonSchema(propSchema, fieldPath, params);
1171
+ if (propCondition !== '1 = 1') {
1172
+ conditions.push(`(${existsExpr} IS NULL OR ${propCondition})`);
1173
+ }
1174
+ }
1175
+ }
1176
+ // Handle enum constraint
1177
+ if (schema.enum && schema.enum.length > 0) {
1178
+ const enumValues = schema.enum.map(v => {
1179
+ if (v === null)
1180
+ return 'NULL';
1181
+ params.push(typeof v === 'object' ? JSON.stringify(v) : v);
1182
+ return '?';
1183
+ });
1184
+ const nullIncluded = schema.enum.includes(null);
1185
+ const nonNullValues = enumValues.filter(v => v !== 'NULL');
1186
+ if (nullIncluded && nonNullValues.length > 0) {
1187
+ conditions.push(`(${jsonPath} IS NULL OR ${jsonPath} IN (${nonNullValues.join(', ')}))`);
1188
+ }
1189
+ else if (nullIncluded) {
1190
+ conditions.push(`${jsonPath} IS NULL`);
1191
+ }
1192
+ else {
1193
+ conditions.push(`${jsonPath} IN (${enumValues.join(', ')})`);
1194
+ }
1195
+ }
1196
+ // Handle minimum/maximum for numbers
1197
+ if (schema.minimum !== undefined) {
1198
+ params.push(schema.minimum);
1199
+ conditions.push(`${jsonPath} >= ?`);
1200
+ }
1201
+ if (schema.maximum !== undefined) {
1202
+ params.push(schema.maximum);
1203
+ conditions.push(`${jsonPath} <= ?`);
1204
+ }
1205
+ if (schema.exclusiveMinimum !== undefined) {
1206
+ const val = typeof schema.exclusiveMinimum === 'boolean' ? schema.minimum : schema.exclusiveMinimum;
1207
+ if (val !== undefined) {
1208
+ params.push(val);
1209
+ conditions.push(`${jsonPath} > ?`);
1210
+ }
1211
+ }
1212
+ if (schema.exclusiveMaximum !== undefined) {
1213
+ const val = typeof schema.exclusiveMaximum === 'boolean' ? schema.maximum : schema.exclusiveMaximum;
1214
+ if (val !== undefined) {
1215
+ params.push(val);
1216
+ conditions.push(`${jsonPath} < ?`);
1217
+ }
1218
+ }
1219
+ // Handle minLength/maxLength for strings
1220
+ if (schema.minLength !== undefined) {
1221
+ params.push(schema.minLength);
1222
+ conditions.push(`LENGTH(${jsonPath}) >= ?`);
1223
+ }
1224
+ if (schema.maxLength !== undefined) {
1225
+ params.push(schema.maxLength);
1226
+ conditions.push(`LENGTH(${jsonPath}) <= ?`);
1227
+ }
1228
+ // Handle pattern for strings
1229
+ if (schema.pattern) {
1230
+ const likePattern = this.regexToLike(schema.pattern);
1231
+ params.push(likePattern);
1232
+ conditions.push(`${jsonPath} LIKE ?`);
1233
+ }
1234
+ // Handle minItems/maxItems for arrays
1235
+ if (schema.minItems !== undefined) {
1236
+ params.push(schema.minItems);
1237
+ const lenExpr = dialectJsonArrayLength(this.dialect, 'data', path);
1238
+ conditions.push(`${lenExpr} >= ?`);
1239
+ }
1240
+ if (schema.maxItems !== undefined) {
1241
+ params.push(schema.maxItems);
1242
+ const lenExpr = dialectJsonArrayLength(this.dialect, 'data', path);
1243
+ conditions.push(`${lenExpr} <= ?`);
1244
+ }
1245
+ // Handle allOf - all schemas must match
1246
+ if (schema.allOf && schema.allOf.length > 0) {
1247
+ const allOfConditions = schema.allOf.map(s => this.translateJsonSchema(s, path, params));
1248
+ conditions.push(`(${allOfConditions.join(' AND ')})`);
1249
+ }
1250
+ // Handle anyOf - at least one schema must match
1251
+ if (schema.anyOf && schema.anyOf.length > 0) {
1252
+ const anyOfConditions = schema.anyOf.map(s => this.translateJsonSchema(s, path, params));
1253
+ conditions.push(`(${anyOfConditions.join(' OR ')})`);
1254
+ }
1255
+ // Handle oneOf - exactly one schema must match (approximated with OR for SQL)
1256
+ if (schema.oneOf && schema.oneOf.length > 0) {
1257
+ // Note: True oneOf validation (exactly one match) is complex in SQL
1258
+ // We approximate with anyOf behavior since exact oneOf requires counting matches
1259
+ const oneOfConditions = schema.oneOf.map(s => this.translateJsonSchema(s, path, params));
1260
+ conditions.push(`(${oneOfConditions.join(' OR ')})`);
1261
+ }
1262
+ // Handle not - schema must not match
1263
+ if (schema.not) {
1264
+ const notCondition = this.translateJsonSchema(schema.not, path, params);
1265
+ conditions.push(`NOT (${notCondition})`);
1266
+ }
1267
+ if (conditions.length === 0) {
1268
+ return '1 = 1';
1269
+ }
1270
+ if (conditions.length === 1) {
1271
+ return conditions[0];
1272
+ }
1273
+ return `(${conditions.join(' AND ')})`;
1274
+ }
1275
+ }
1276
+ //# sourceMappingURL=query-translator.js.map