@warlock.js/core 5.19.1 → 5.21.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 (378) hide show
  1. package/CHANGELOG.md +1136 -1073
  2. package/esm/benchmark/benchmark.mjs +34 -25
  3. package/esm/benchmark/benchmark.mjs.map +1 -1
  4. package/esm/benchmark/profiler.d.mts.map +1 -1
  5. package/esm/benchmark/profiler.mjs +8 -3
  6. package/esm/benchmark/profiler.mjs.map +1 -1
  7. package/esm/bootstrap.d.mts.map +1 -1
  8. package/esm/bootstrap.mjs +3 -0
  9. package/esm/bootstrap.mjs.map +1 -1
  10. package/esm/cache/database-cache-driver.d.mts +2 -2
  11. package/esm/cache/database-cache-driver.d.mts.map +1 -1
  12. package/esm/cache/database-cache-driver.mjs +3 -2
  13. package/esm/cache/database-cache-driver.mjs.map +1 -1
  14. package/esm/cli/cli-commands.manager.mjs +16 -14
  15. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  16. package/esm/cli/commands/add.command.mjs +1 -1
  17. package/esm/cli/commands/add.command.mjs.map +1 -1
  18. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs +3 -2
  19. package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
  20. package/esm/cli/commands/generate/generate.command.mjs +20 -0
  21. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  22. package/esm/cli/commands/generate/generators/column-dsl-parser.mjs +4 -2
  23. package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
  24. package/esm/cli/commands/generate/generators/controller.generator.mjs +1 -1
  25. package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
  26. package/esm/cli/commands/generate/generators/migration.generator.mjs +22 -6
  27. package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
  28. package/esm/cli/commands/generate/generators/model.generator.mjs +22 -7
  29. package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
  30. package/esm/cli/commands/generate/generators/module.generator.mjs +8 -6
  31. package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
  32. package/esm/cli/commands/generate/generators/repository.generator.mjs +1 -1
  33. package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
  34. package/esm/cli/commands/generate/generators/resource.generator.mjs +1 -1
  35. package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
  36. package/esm/cli/commands/generate/generators/service.generator.mjs +1 -1
  37. package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
  38. package/esm/cli/commands/generate/templates/stubs.mjs +7 -7
  39. package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
  40. package/esm/cli/commands/generate/utils/writer.mjs +25 -1
  41. package/esm/cli/commands/generate/utils/writer.mjs.map +1 -1
  42. package/esm/cli/commands/routes/routes-command.action.mjs +5 -1
  43. package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
  44. package/esm/cli/commands/routes/with-console-on-stderr.mjs +24 -0
  45. package/esm/cli/commands/routes/with-console-on-stderr.mjs.map +1 -0
  46. package/esm/cli/commands/routes.command.mjs +3 -4
  47. package/esm/cli/commands/routes.command.mjs.map +1 -1
  48. package/esm/cli/commands/start-production.command.mjs +5 -5
  49. package/esm/cli/commands/start-production.command.mjs.map +1 -1
  50. package/esm/cli/commands/storage-put.action.mjs +2 -1
  51. package/esm/cli/commands/storage-put.action.mjs.map +1 -1
  52. package/esm/cli/commands/update.command.mjs +1 -0
  53. package/esm/cli/commands/update.command.mjs.map +1 -1
  54. package/esm/cli/parse-cli-args.mjs +13 -1
  55. package/esm/cli/parse-cli-args.mjs.map +1 -1
  56. package/esm/commands/cli-command.d.mts.map +1 -1
  57. package/esm/commands/cli-command.mjs +4 -2
  58. package/esm/commands/cli-command.mjs.map +1 -1
  59. package/esm/commands/index.mjs +3 -0
  60. package/esm/config/config-key-from-path.mjs +20 -0
  61. package/esm/config/config-key-from-path.mjs.map +1 -0
  62. package/esm/config/config-loader.d.mts.map +1 -1
  63. package/esm/config/config-loader.mjs +2 -3
  64. package/esm/config/config-loader.mjs.map +1 -1
  65. package/esm/connectors/cache-connector.d.mts.map +1 -1
  66. package/esm/connectors/cache-connector.mjs +5 -0
  67. package/esm/connectors/cache-connector.mjs.map +1 -1
  68. package/esm/connectors/connectors-manager.d.mts +4 -0
  69. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  70. package/esm/connectors/connectors-manager.mjs +30 -2
  71. package/esm/connectors/connectors-manager.mjs.map +1 -1
  72. package/esm/connectors/single-server-warnings.mjs +33 -0
  73. package/esm/connectors/single-server-warnings.mjs.map +1 -0
  74. package/esm/connectors/socket-connector.d.mts +29 -0
  75. package/esm/connectors/socket-connector.d.mts.map +1 -1
  76. package/esm/connectors/socket-connector.mjs +77 -9
  77. package/esm/connectors/socket-connector.mjs.map +1 -1
  78. package/esm/connectors/storage.connector.d.mts.map +1 -1
  79. package/esm/connectors/storage.connector.mjs +6 -0
  80. package/esm/connectors/storage.connector.mjs.map +1 -1
  81. package/esm/connectors/types.d.mts +4 -2
  82. package/esm/connectors/types.d.mts.map +1 -1
  83. package/esm/connectors/types.mjs.map +1 -1
  84. package/esm/database/models/database-log/database-log.d.mts +47 -24
  85. package/esm/database/models/database-log/database-log.d.mts.map +1 -1
  86. package/esm/database/models/database-log/database-log.mjs +36 -4
  87. package/esm/database/models/database-log/database-log.mjs.map +1 -1
  88. package/esm/database/seed-command-action.mjs +10 -2
  89. package/esm/database/seed-command-action.mjs.map +1 -1
  90. package/esm/dev-server/development-server.mjs +15 -4
  91. package/esm/dev-server/development-server.mjs.map +1 -1
  92. package/esm/dev-server/file-event-handler.mjs +26 -6
  93. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  94. package/esm/dev-server/file-manager.d.mts.map +1 -1
  95. package/esm/dev-server/file-manager.mjs +2 -2
  96. package/esm/dev-server/file-manager.mjs.map +1 -1
  97. package/esm/dev-server/files-orchestrator.mjs +3 -1
  98. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  99. package/esm/dev-server/files-watcher.mjs +64 -5
  100. package/esm/dev-server/files-watcher.mjs.map +1 -1
  101. package/esm/dev-server/health-checker/files-healthcare.manager.mjs +31 -9
  102. package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
  103. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +11 -7
  104. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +1 -1
  105. package/esm/dev-server/loader/load-hook.mjs +1 -1
  106. package/esm/dev-server/loader/register-loader.mjs +1 -1
  107. package/esm/dev-server/manifest-manager.d.mts.map +1 -1
  108. package/esm/dev-server/manifest-manager.mjs +12 -6
  109. package/esm/dev-server/manifest-manager.mjs.map +1 -1
  110. package/esm/dev-server/parse-imports.mjs +9 -0
  111. package/esm/dev-server/parse-imports.mjs.map +1 -1
  112. package/esm/dev-server/register-dev-shortcuts.mjs +2 -1
  113. package/esm/dev-server/register-dev-shortcuts.mjs.map +1 -1
  114. package/esm/dev-server/route-types-publisher.mjs +60 -0
  115. package/esm/dev-server/route-types-publisher.mjs.map +1 -0
  116. package/esm/dev-server/shortcuts.mjs +2 -2
  117. package/esm/dev-server/shortcuts.mjs.map +1 -1
  118. package/esm/dev-server/special-file-patterns.mjs +18 -0
  119. package/esm/dev-server/special-file-patterns.mjs.map +1 -0
  120. package/esm/dev-server/special-files-collector.d.mts.map +1 -1
  121. package/esm/dev-server/special-files-collector.mjs +2 -5
  122. package/esm/dev-server/special-files-collector.mjs.map +1 -1
  123. package/esm/dev-server/start-development-server.mjs +1 -2
  124. package/esm/dev-server/start-development-server.mjs.map +1 -1
  125. package/esm/dev-server/supervisor.mjs +23 -3
  126. package/esm/dev-server/supervisor.mjs.map +1 -1
  127. package/esm/dev-server/tsconfig-manager.mjs +22 -12
  128. package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
  129. package/esm/dev-server/type-generator.mjs +10 -9
  130. package/esm/dev-server/type-generator.mjs.map +1 -1
  131. package/esm/encryption/encrypt.d.mts.map +1 -1
  132. package/esm/encryption/encrypt.mjs +16 -5
  133. package/esm/encryption/encrypt.mjs.map +1 -1
  134. package/esm/encryption/hash.d.mts.map +1 -1
  135. package/esm/encryption/hash.mjs +2 -0
  136. package/esm/encryption/hash.mjs.map +1 -1
  137. package/esm/encryption/password.d.mts.map +1 -1
  138. package/esm/encryption/password.mjs +3 -1
  139. package/esm/encryption/password.mjs.map +1 -1
  140. package/esm/generations/features/index.mjs +1 -1
  141. package/esm/generations/features/mysql.feature.mjs +9 -2
  142. package/esm/generations/features/mysql.feature.mjs.map +1 -1
  143. package/esm/generations/features/test.feature.mjs +2 -1
  144. package/esm/generations/features/test.feature.mjs.map +1 -1
  145. package/esm/generations/features/web.feature.mjs +4 -2
  146. package/esm/generations/features/web.feature.mjs.map +1 -1
  147. package/esm/http/build-rate-limit-options.d.mts +37 -0
  148. package/esm/http/build-rate-limit-options.d.mts.map +1 -0
  149. package/esm/http/build-rate-limit-options.mjs +37 -0
  150. package/esm/http/build-rate-limit-options.mjs.map +1 -0
  151. package/esm/http/csrf-origin-policy.d.mts.map +1 -1
  152. package/esm/http/csrf-origin-policy.mjs +5 -2
  153. package/esm/http/csrf-origin-policy.mjs.map +1 -1
  154. package/esm/http/flush-pending-cookies.mjs +52 -0
  155. package/esm/http/flush-pending-cookies.mjs.map +1 -0
  156. package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
  157. package/esm/http/middleware/cache-response-middleware.mjs +4 -2
  158. package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
  159. package/esm/http/middleware/idempotency.middleware.d.mts +5 -0
  160. package/esm/http/middleware/idempotency.middleware.d.mts.map +1 -1
  161. package/esm/http/middleware/idempotency.middleware.mjs +35 -3
  162. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  163. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  164. package/esm/http/middleware/inject-request-context.mjs +14 -3
  165. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  166. package/esm/http/middleware/rate-limit.middleware.d.mts +4 -3
  167. package/esm/http/middleware/rate-limit.middleware.d.mts.map +1 -1
  168. package/esm/http/middleware/rate-limit.middleware.mjs +4 -3
  169. package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
  170. package/esm/http/middleware/utils/cidr-match.d.mts +1 -1
  171. package/esm/http/middleware/utils/cidr-match.d.mts.map +1 -1
  172. package/esm/http/middleware/utils/cidr-match.mjs +11 -2
  173. package/esm/http/middleware/utils/cidr-match.mjs.map +1 -1
  174. package/esm/http/middleware/utils/idempotency-key.mjs +1 -1
  175. package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
  176. package/esm/http/plugins.d.mts.map +1 -1
  177. package/esm/http/plugins.mjs +13 -5
  178. package/esm/http/plugins.mjs.map +1 -1
  179. package/esm/http/request.d.mts +12 -1
  180. package/esm/http/request.d.mts.map +1 -1
  181. package/esm/http/request.mjs +35 -7
  182. package/esm/http/request.mjs.map +1 -1
  183. package/esm/http/response.d.mts +16 -0
  184. package/esm/http/response.d.mts.map +1 -1
  185. package/esm/http/response.mjs +75 -37
  186. package/esm/http/response.mjs.map +1 -1
  187. package/esm/http/types.d.mts +26 -14
  188. package/esm/http/types.d.mts.map +1 -1
  189. package/esm/http/uploads/uploaded-file.controller.mjs +1 -1
  190. package/esm/http/uploads-config.mjs +2 -1
  191. package/esm/http/uploads-config.mjs.map +1 -1
  192. package/esm/http/uploads-types.d.mts +3 -3
  193. package/esm/image/image.d.mts +1 -1
  194. package/esm/image/image.d.mts.map +1 -1
  195. package/esm/image/image.mjs +25 -15
  196. package/esm/image/image.mjs.map +1 -1
  197. package/esm/index.d.mts +6 -4
  198. package/esm/index.mjs +7 -4
  199. package/esm/logger/logger.mjs +6 -1
  200. package/esm/logger/logger.mjs.map +1 -1
  201. package/esm/mail/config.d.mts.map +1 -1
  202. package/esm/mail/config.mjs +31 -21
  203. package/esm/mail/config.mjs.map +1 -1
  204. package/esm/mail/react-mail.mjs +6 -3
  205. package/esm/mail/react-mail.mjs.map +1 -1
  206. package/esm/mail/send-mail.d.mts.map +1 -1
  207. package/esm/mail/send-mail.mjs +40 -19
  208. package/esm/mail/send-mail.mjs.map +1 -1
  209. package/esm/production/assert-generated-imports.mjs +1 -0
  210. package/esm/production/assert-generated-imports.mjs.map +1 -1
  211. package/esm/production/build-contributions.mjs +3 -0
  212. package/esm/production/build-contributions.mjs.map +1 -1
  213. package/esm/production/esbuild-plugins.mjs +3 -2
  214. package/esm/production/esbuild-plugins.mjs.map +1 -1
  215. package/esm/production/production-builder.mjs +49 -37
  216. package/esm/production/production-builder.mjs.map +1 -1
  217. package/esm/production/production-supervisor.mjs +12 -5
  218. package/esm/production/production-supervisor.mjs.map +1 -1
  219. package/esm/production/route-registration-child.d.mts +1 -0
  220. package/esm/production/route-registration-child.mjs +65 -0
  221. package/esm/production/route-registration-child.mjs.map +1 -0
  222. package/esm/production/route-registration-snapshot.mjs +166 -0
  223. package/esm/production/route-registration-snapshot.mjs.map +1 -0
  224. package/esm/repositories/adapters/cascade/cascade-adapter.d.mts +3 -1
  225. package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
  226. package/esm/repositories/adapters/cascade/cascade-adapter.mjs +22 -12
  227. package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
  228. package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +3 -3
  229. package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
  230. package/esm/repositories/adapters/cascade/filter-applicator.mjs +88 -61
  231. package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
  232. package/esm/repositories/adapters/cascade/index.d.mts +1 -1
  233. package/esm/repositories/adapters/cascade/index.mjs +1 -1
  234. package/esm/repositories/contracts/query-builder.contract.d.mts +3 -3
  235. package/esm/repositories/contracts/types.d.mts +5 -0
  236. package/esm/repositories/contracts/types.d.mts.map +1 -1
  237. package/esm/repositories/index.d.mts +1 -1
  238. package/esm/repositories/index.mjs +1 -1
  239. package/esm/repositories/repository.manager.d.mts +65 -3
  240. package/esm/repositories/repository.manager.d.mts.map +1 -1
  241. package/esm/repositories/repository.manager.mjs +146 -57
  242. package/esm/repositories/repository.manager.mjs.map +1 -1
  243. package/esm/resource/resource-field-builder.d.mts.map +1 -1
  244. package/esm/resource/resource-field-builder.mjs +1 -0
  245. package/esm/resource/resource-field-builder.mjs.map +1 -1
  246. package/esm/resource/resource.d.mts +10 -3
  247. package/esm/resource/resource.d.mts.map +1 -1
  248. package/esm/resource/resource.mjs +19 -5
  249. package/esm/resource/resource.mjs.map +1 -1
  250. package/esm/restful/restful.d.mts +12 -5
  251. package/esm/restful/restful.d.mts.map +1 -1
  252. package/esm/restful/restful.mjs +47 -16
  253. package/esm/restful/restful.mjs.map +1 -1
  254. package/esm/router/build-route-rate-limit.mjs +22 -0
  255. package/esm/router/build-route-rate-limit.mjs.map +1 -0
  256. package/esm/router/dev-dispatch.mjs +60 -0
  257. package/esm/router/dev-dispatch.mjs.map +1 -0
  258. package/esm/router/route-builder.mjs +4 -1
  259. package/esm/router/route-builder.mjs.map +1 -1
  260. package/esm/router/route-registry.d.mts.map +1 -1
  261. package/esm/router/route-registry.mjs +2 -2
  262. package/esm/router/route-registry.mjs.map +1 -1
  263. package/esm/router/router.d.mts +1 -1
  264. package/esm/router/router.d.mts.map +1 -1
  265. package/esm/router/router.mjs +57 -39
  266. package/esm/router/router.mjs.map +1 -1
  267. package/esm/socket/index.d.mts +1 -1
  268. package/esm/socket/types.d.mts +25 -3
  269. package/esm/socket/types.d.mts.map +1 -1
  270. package/esm/storage/drivers/cloud-driver.d.mts +26 -0
  271. package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
  272. package/esm/storage/drivers/cloud-driver.mjs +100 -16
  273. package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
  274. package/esm/storage/drivers/do-spaces-driver.d.mts +7 -0
  275. package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
  276. package/esm/storage/drivers/do-spaces-driver.mjs +9 -2
  277. package/esm/storage/drivers/do-spaces-driver.mjs.map +1 -1
  278. package/esm/storage/drivers/local-driver.d.mts +16 -5
  279. package/esm/storage/drivers/local-driver.d.mts.map +1 -1
  280. package/esm/storage/drivers/local-driver.mjs +71 -28
  281. package/esm/storage/drivers/local-driver.mjs.map +1 -1
  282. package/esm/storage/index.d.mts +3 -1
  283. package/esm/storage/index.mjs +4 -2
  284. package/esm/storage/scoped-storage.d.mts +27 -1
  285. package/esm/storage/scoped-storage.d.mts.map +1 -1
  286. package/esm/storage/scoped-storage.mjs +40 -8
  287. package/esm/storage/scoped-storage.mjs.map +1 -1
  288. package/esm/storage/storage.d.mts +11 -5
  289. package/esm/storage/storage.d.mts.map +1 -1
  290. package/esm/storage/storage.mjs +39 -16
  291. package/esm/storage/storage.mjs.map +1 -1
  292. package/esm/storage/types.d.mts +43 -4
  293. package/esm/storage/types.d.mts.map +1 -1
  294. package/esm/storage/utils/classify-ip-address.d.mts +13 -0
  295. package/esm/storage/utils/classify-ip-address.d.mts.map +1 -0
  296. package/esm/storage/utils/classify-ip-address.mjs +98 -0
  297. package/esm/storage/utils/classify-ip-address.mjs.map +1 -0
  298. package/esm/storage/utils/safe-fetch.d.mts +3 -12
  299. package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
  300. package/esm/storage/utils/safe-fetch.mjs +3 -48
  301. package/esm/storage/utils/safe-fetch.mjs.map +1 -1
  302. package/esm/storage/utils/storage-capability-error.d.mts +25 -0
  303. package/esm/storage/utils/storage-capability-error.d.mts.map +1 -0
  304. package/esm/storage/utils/storage-capability-error.mjs +26 -0
  305. package/esm/storage/utils/storage-capability-error.mjs.map +1 -0
  306. package/esm/updater/update-warlock-packages.mjs +11 -1
  307. package/esm/updater/update-warlock-packages.mjs.map +1 -1
  308. package/esm/use-cases/use-case-broadcast.mjs +7 -1
  309. package/esm/use-cases/use-case-broadcast.mjs.map +1 -1
  310. package/esm/use-cases/use-cases-registry.mjs +21 -2
  311. package/esm/use-cases/use-cases-registry.mjs.map +1 -1
  312. package/esm/utils/database-log.mjs +0 -3
  313. package/esm/utils/database-log.mjs.map +1 -1
  314. package/esm/utils/queue.d.mts +6 -4
  315. package/esm/utils/queue.d.mts.map +1 -1
  316. package/esm/utils/queue.mjs +20 -9
  317. package/esm/utils/queue.mjs.map +1 -1
  318. package/esm/validation/database/exists-except-current-id.mjs +6 -2
  319. package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
  320. package/esm/validation/database/unique-except-current-id.mjs +6 -2
  321. package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
  322. package/esm/validation/init.mjs +6 -2
  323. package/esm/validation/init.mjs.map +1 -1
  324. package/esm/validation/validateAll.mjs +3 -2
  325. package/esm/validation/validateAll.mjs.map +1 -1
  326. package/esm/warlock-config/types.d.mts +14 -1
  327. package/esm/warlock-config/types.d.mts.map +1 -1
  328. package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
  329. package/esm/warlock-config/warlock-config.manager.mjs +3 -1
  330. package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
  331. package/esm/warlock-config/warn-deprecated-config.mjs +25 -0
  332. package/esm/warlock-config/warn-deprecated-config.mjs.map +1 -0
  333. package/llms-full.txt +71 -42
  334. package/llms.txt +41 -41
  335. package/package.json +21 -15
  336. package/skills/add-connector/SKILL.md +5 -1
  337. package/skills/benchmark-code/SKILL.md +1 -1
  338. package/skills/build-restful/SKILL.md +1 -1
  339. package/skills/build-url/SKILL.md +1 -1
  340. package/skills/configure-app/SKILL.md +7 -1
  341. package/skills/create-controller/SKILL.md +1 -1
  342. package/skills/create-module/SKILL.md +1 -1
  343. package/skills/define-resource/SKILL.md +1 -1
  344. package/skills/encrypt-data/SKILL.md +1 -1
  345. package/skills/hash-password/SKILL.md +1 -1
  346. package/skills/health-checks/SKILL.md +1 -1
  347. package/skills/index.md +30 -0
  348. package/skills/lower-stage3-decorators/SKILL.md +1 -1
  349. package/skills/process-image/SKILL.md +1 -1
  350. package/skills/register-route/SKILL.md +1 -1
  351. package/skills/request-memo/SKILL.md +1 -1
  352. package/skills/request-tracing/SKILL.md +1 -1
  353. package/skills/resolve-path/SKILL.md +1 -1
  354. package/skills/retry-operation/SKILL.md +1 -1
  355. package/skills/run-app/SKILL.md +1 -1
  356. package/skills/send-mail/SKILL.md +1 -1
  357. package/skills/send-response/SKILL.md +1 -1
  358. package/skills/store-file/SKILL.md +11 -1
  359. package/skills/test-http/SKILL.md +1 -1
  360. package/skills/test-service/SKILL.md +1 -1
  361. package/skills/update-packages/SKILL.md +1 -1
  362. package/skills/upload-file/SKILL.md +1 -1
  363. package/skills/use-app-context/SKILL.md +1 -1
  364. package/skills/use-localization/SKILL.md +1 -1
  365. package/skills/use-middleware/SKILL.md +7 -2
  366. package/skills/use-model-transformers/SKILL.md +1 -1
  367. package/skills/use-repository/SKILL.md +1 -1
  368. package/skills/use-request-locals/SKILL.md +1 -1
  369. package/skills/validate-input/SKILL.md +1 -1
  370. package/skills/warlock-conventions/SKILL.md +1 -1
  371. package/skills/warlock-doctor/SKILL.md +1 -1
  372. package/skills/warlock-routes/SKILL.md +1 -1
  373. package/skills/wire-socket/SKILL.md +5 -1
  374. package/skills/write-cli-command/SKILL.md +1 -1
  375. package/skills/write-middleware/SKILL.md +1 -1
  376. package/skills/write-seeder/SKILL.md +1 -1
  377. package/skills/write-use-case/SKILL.md +1 -1
  378. package/esm/_virtual/_rolldown/runtime.mjs +0 -7
@@ -1,4 +1,9 @@
1
+ import { BadRequestError } from "../../../http/errors/errors.mjs";
2
+ import dayjs from "dayjs";
3
+ import customParseFormat from "dayjs/plugin/customParseFormat.js";
4
+
1
5
  //#region ../core/src/repositories/adapters/cascade/filter-applicator.ts
6
+ dayjs.extend(customParseFormat);
2
7
  /**
3
8
  * Applies repository filters to a Cascade-Next query builder
4
9
  * Translates repository filter rules into Cascade query builder method calls
@@ -118,6 +123,31 @@ var FilterApplicator = class {
118
123
  }[type];
119
124
  }
120
125
  /**
126
+ * `like` is a contains match on every driver: a value without an explicit
127
+ * `%` is wrapped as `%value%` (Postgres would otherwise match exactly while
128
+ * Mongo matched a substring). Values carrying their own `%` are kept as-is.
129
+ */
130
+ likePattern(value) {
131
+ if (typeof value !== "string" || value.includes("%")) return value;
132
+ return `%${value}%`;
133
+ }
134
+ /**
135
+ * Coerce a client value to an integer or reject with a 400.
136
+ */
137
+ toInt(value) {
138
+ const parsed = parseInt(value);
139
+ if (Number.isNaN(parsed)) throw new BadRequestError(`Invalid integer filter value "${value}"`);
140
+ return parsed;
141
+ }
142
+ /**
143
+ * Coerce a client value to a number or reject with a 400.
144
+ */
145
+ toNumber(value) {
146
+ const parsed = Number(value);
147
+ if (value === "" || value === null || Number.isNaN(parsed)) throw new BadRequestError(`Invalid number filter value "${value}"`);
148
+ return parsed;
149
+ }
150
+ /**
121
151
  * Apply standard where operators
122
152
  */
123
153
  applyWhereOperator(query, operator, column, columns, value, ruleName) {
@@ -143,7 +173,13 @@ var FilterApplicator = class {
143
173
  query.whereNotIn(column, Array.isArray(value) ? value : [value]);
144
174
  break;
145
175
  case "like":
146
- query.whereLike(column, value);
176
+ query.whereLike(column, this.likePattern(value));
177
+ break;
178
+ case "startsWith":
179
+ query.whereLike(column, `${value}%`);
180
+ break;
181
+ case "endsWith":
182
+ query.whereLike(column, `%${value}`);
147
183
  break;
148
184
  case "not like":
149
185
  query.whereNotLike(column, value);
@@ -155,17 +191,24 @@ var FilterApplicator = class {
155
191
  query.whereNotBetween(column, value);
156
192
  break;
157
193
  }
158
- else if (columns) if (operator === "=") {
159
- const conditions = {};
160
- for (const col of columns) conditions[col] = value;
161
- query.orWhere(conditions);
162
- } else if (operator === "like") query.orWhere((sub) => {
194
+ else if (columns) if (operator === "=") this.anyOf(query, columns, (q, col) => q.where(col, value));
195
+ else if (operator === "like") this.anyOf(query, columns, (q, col) => q.where(col, "like", this.likePattern(value)));
196
+ else throw new Error(`Unsupported multi-column filter operator "${operator}" for rule "${ruleName ?? columns.join(",")}". Multi-column filters only support "=" and "like".`);
197
+ }
198
+ /**
199
+ * Apply a condition across several columns as ONE grouped OR: `(a OR b OR ...)`.
200
+ * The group is ANDed with the rest of the query, so a tenant filter
201
+ * (`org_id = 5`) can never be widened by a multi-column search. Never emit a
202
+ * top-level `orWhere` for multi-column filters.
203
+ */
204
+ anyOf(query, columns, apply) {
205
+ query.where((group) => {
163
206
  columns.forEach((col, index) => {
164
- if (index === 0) sub.where(col, "like", value);
165
- else sub.orWhere(col, "like", value);
207
+ const build = (q) => apply(q, col);
208
+ if (index === 0) group.where(build);
209
+ else group.orWhere(build);
166
210
  });
167
211
  });
168
- else throw new Error(`Unsupported multi-column filter operator "${operator}" for rule "${ruleName ?? columns.join(",")}". Multi-column filters only support "=" and "like".`);
169
212
  }
170
213
  /**
171
214
  * Coerce a filter value to a boolean.
@@ -186,66 +229,50 @@ var FilterApplicator = class {
186
229
  handleBoolean(query, column, columns, value) {
187
230
  const boolValue = this.coerceBoolean(value);
188
231
  if (column) query.where(column, boolValue);
189
- else if (columns) {
190
- const conditions = {};
191
- for (const col of columns) conditions[col] = boolValue;
192
- query.orWhere(conditions);
193
- }
232
+ else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, boolValue));
194
233
  }
195
234
  handleInt(query, column, columns, value) {
196
- const intValue = parseInt(value);
235
+ const intValue = this.toInt(value);
197
236
  if (column) query.where(column, intValue);
198
- else if (columns) {
199
- const conditions = {};
200
- for (const col of columns) conditions[col] = intValue;
201
- query.orWhere(conditions);
202
- }
237
+ else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, intValue));
203
238
  }
204
239
  handleNotInt(query, column, columns, value) {
205
- const intValue = parseInt(value);
240
+ const intValue = this.toInt(value);
206
241
  if (column) query.where(column, "!=", intValue);
207
- else if (columns) for (const col of columns) query.orWhere(col, "!=", intValue);
242
+ else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, "!=", intValue));
208
243
  }
209
244
  handleIntComparison(query, column, columns, value, operator) {
210
- const intValue = parseInt(value);
245
+ const intValue = this.toInt(value);
211
246
  if (column) query.where(column, operator, intValue);
212
- else if (columns) for (const col of columns) query.orWhere(col, operator, intValue);
247
+ else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, operator, intValue));
213
248
  }
214
249
  handleInInt(query, column, columns, value) {
215
- const values = (Array.isArray(value) ? value : [value]).map((v) => parseInt(v));
250
+ const values = (Array.isArray(value) ? value : [value]).map((v) => this.toInt(v));
216
251
  if (column) query.whereIn(column, values);
217
- else if (columns) for (const col of columns) query.orWhere((q) => q.whereIn(col, values));
252
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereIn(col, values));
218
253
  }
219
254
  handleNumber(query, column, columns, value) {
220
- const numValue = Number(value);
255
+ const numValue = this.toNumber(value);
221
256
  if (column) query.where(column, numValue);
222
- else if (columns) {
223
- const conditions = {};
224
- for (const col of columns) conditions[col] = numValue;
225
- query.orWhere(conditions);
226
- }
257
+ else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, numValue));
227
258
  }
228
259
  handleInNumber(query, column, columns, value) {
229
- const values = (Array.isArray(value) ? value : [value]).map((v) => Number(v));
260
+ const values = (Array.isArray(value) ? value : [value]).map((v) => this.toNumber(v));
230
261
  if (column) query.whereIn(column, values);
231
- else if (columns) for (const col of columns) query.orWhere((q) => q.whereIn(col, values));
262
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereIn(col, values));
232
263
  }
233
264
  handleFloat(query, column, columns, value) {
234
265
  const floatValue = parseFloat(value);
235
266
  if (column) query.where(column, floatValue);
236
- else if (columns) {
237
- const conditions = {};
238
- for (const col of columns) conditions[col] = floatValue;
239
- query.orWhere(conditions);
240
- }
267
+ else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, floatValue));
241
268
  }
242
269
  handleNull(query, column, columns) {
243
270
  if (column) query.whereNull(column);
244
- else if (columns) for (const col of columns) query.orWhere({ [col]: null });
271
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereNull(col));
245
272
  }
246
273
  handleNotNull(query, column, columns) {
247
274
  if (column) query.whereNotNull(column);
248
- else if (columns) for (const col of columns) query.orWhere((q) => q.whereNotNull(col));
275
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereNotNull(col));
249
276
  }
250
277
  /**
251
278
  * Handle scope filter - applies local scope and passes the filter value.
@@ -339,67 +366,67 @@ var FilterApplicator = class {
339
366
  handleDate(query, column, columns, value, options) {
340
367
  const dateValue = this.parseDate(value, options?.dateFormat);
341
368
  if (column) query.whereDate(column, dateValue);
342
- else if (columns) for (const col of columns) query.orWhere((q) => q.whereDate(col, dateValue));
369
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereDate(col, dateValue));
343
370
  }
344
371
  handleDateComparison(query, column, columns, value, options, operator) {
345
372
  const dateValue = this.parseDate(value, options?.dateFormat);
346
373
  if (column) if (operator === ">" || operator === ">=") query.whereDateAfter(column, dateValue);
347
374
  else query.whereDateBefore(column, dateValue);
348
- else if (columns) for (const col of columns) if (operator === ">" || operator === ">=") query.orWhere((q) => q.whereDateAfter(col, dateValue));
349
- else query.orWhere((q) => q.whereDateBefore(col, dateValue));
375
+ else if (columns) {
376
+ const after = operator === ">" || operator === ">=";
377
+ this.anyOf(query, columns, (q, col) => after ? q.whereDateAfter(col, dateValue) : q.whereDateBefore(col, dateValue));
378
+ }
350
379
  }
351
380
  handleDateBetween(query, column, columns, value, options) {
352
381
  if (!Array.isArray(value) || value.length !== 2) return;
353
382
  const [start, end] = value.map((v) => this.parseDate(v, options?.dateFormat));
354
383
  if (start === void 0 || end === void 0) return;
355
384
  if (column) query.whereDateBetween(column, [start, end]);
356
- else if (columns) for (const col of columns) query.orWhere((q) => q.whereDateBetween(col, [start, end]));
385
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereDateBetween(col, [start, end]));
357
386
  }
358
387
  handleInDate(query, column, columns, value, options) {
359
388
  const dates = (Array.isArray(value) ? value : [value]).map((v) => this.parseDate(v, options?.dateFormat));
360
389
  if (column) query.whereIn(column, dates);
361
- else if (columns) for (const col of columns) query.orWhere((q) => q.whereIn(col, dates));
390
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereIn(col, dates));
362
391
  }
363
392
  handleDateTime(query, column, columns, value, options) {
364
393
  const dateValue = this.parseDateTime(value, options?.dateTimeFormat);
365
394
  if (column) query.where(column, dateValue);
366
- else if (columns) {
367
- const conditions = {};
368
- for (const col of columns) conditions[col] = dateValue;
369
- query.orWhere(conditions);
370
- }
395
+ else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, dateValue));
371
396
  }
372
397
  handleDateTimeComparison(query, column, columns, value, options, operator) {
373
398
  const dateValue = this.parseDateTime(value, options?.dateTimeFormat);
374
399
  if (column) query.where(column, operator, dateValue);
375
- else if (columns) for (const col of columns) query.orWhere(col, operator, dateValue);
400
+ else if (columns) this.anyOf(query, columns, (q, col) => q.where(col, operator, dateValue));
376
401
  }
377
402
  handleDateTimeBetween(query, column, columns, value, options) {
378
403
  if (!Array.isArray(value) || value.length !== 2) return;
379
404
  const [start, end] = value.map((v) => this.parseDateTime(v, options?.dateTimeFormat));
380
405
  if (column) query.whereBetween(column, [start, end]);
381
- else if (columns) for (const col of columns) query.orWhere((q) => q.whereBetween(col, [start, end]));
406
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereBetween(col, [start, end]));
382
407
  }
383
408
  handleInDateTime(query, column, columns, value, options) {
384
409
  const dates = (Array.isArray(value) ? value : [value]).map((v) => this.parseDateTime(v, options?.dateTimeFormat));
385
410
  if (column) query.whereIn(column, dates);
386
- else if (columns) for (const col of columns) query.orWhere((q) => q.whereIn(col, dates));
411
+ else if (columns) this.anyOf(query, columns, (q, col) => q.whereIn(col, dates));
387
412
  }
388
413
  /**
389
- * Parse date string to Date object
390
- * TODO: Implement proper date parsing with format support
414
+ * Parse a date string with the declared format; invalid input is a 400.
391
415
  */
392
416
  parseDate(value, format) {
393
- if (value instanceof Date) return value;
394
- return new Date(value);
417
+ return this.parseWithFormat(value, format);
395
418
  }
396
419
  /**
397
- * Parse datetime string to Date object
398
- * TODO: Implement proper datetime parsing with format support
420
+ * Parse a datetime string with the declared format; invalid input is a 400.
399
421
  */
400
422
  parseDateTime(value, format) {
423
+ return this.parseWithFormat(value, format);
424
+ }
425
+ parseWithFormat(value, format) {
401
426
  if (value instanceof Date) return value;
402
- return new Date(value);
427
+ const parsed = format ? dayjs(String(value), format, true) : dayjs(value);
428
+ if (!parsed.isValid()) throw new BadRequestError(`Invalid date filter value "${value}"${format ? ` (expected ${format})` : ""}`);
429
+ return parsed.toDate();
403
430
  }
404
431
  };
405
432
 
@@ -1 +1 @@
1
- {"version":3,"file":"filter-applicator.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/filter-applicator.ts"],"sourcesContent":["import type {\n QueryBuilderContract as CascadeQueryBuilder,\n QueryBuilderContract,\n} from \"@warlock.js/cascade\";\nimport type { FilterOptions, FilterRule, FilterRules } from \"../../contracts\";\n\n/**\n * Applies repository filters to a Cascade-Next query builder\n * Translates repository filter rules into Cascade query builder method calls\n */\nexport class FilterApplicator {\n /**\n * Apply filters to a Cascade query builder\n *\n * @param query - Cascade query builder instance\n * @param filters - Filter structure defining how to filter\n * @param data - Data containing filter values\n * @param options - Additional filter options (date formats, etc.)\n */\n public apply(\n query: CascadeQueryBuilder<any>,\n filters: FilterRules,\n data: any,\n options: FilterOptions,\n ): void {\n // `Object.entries` hands the rule over already narrowed; a `for...in` index\n // read is `FilterRule | undefined` under the strictness contract.\n for (const [key, filterRule] of Object.entries(filters)) {\n const value = data[key];\n\n if (value === undefined) continue;\n if (filterRule === undefined) continue;\n\n const rule = this.parseFilterRule(key, filterRule);\n this.applyFilterRule(query, rule, value, data, options);\n }\n }\n\n /**\n * Parse a filter rule into a structured format\n */\n private parseFilterRule(key: string, rule: FilterRule) {\n // Handle custom function\n if (typeof rule === \"function\") {\n return { type: \"function\", fn: rule, column: key, key };\n }\n\n // Handle array format: [\"operator\"] or [\"operator\", \"column\"] or [\"operator\", [\"col1\", \"col2\"]]\n if (Array.isArray(rule)) {\n const [operator, target] = rule;\n\n if (target === undefined) {\n return { type: operator, column: key, columns: undefined, key };\n }\n\n if (Array.isArray(target)) {\n return { type: operator, column: undefined, columns: target, key };\n }\n\n return { type: operator, column: target, columns: undefined, key };\n }\n\n // Handle simple operator string\n return { type: rule, column: key, columns: undefined, key };\n }\n\n /**\n * Apply a single filter rule to the query\n */\n private applyFilterRule(\n query: CascadeQueryBuilder<any>,\n rule: any,\n value: any,\n data: any,\n options: FilterOptions,\n ): void {\n // 1. Custom function filter\n if (rule.type === \"function\") {\n rule.fn(value, query, data);\n return;\n }\n\n // 2. Predefined filter types\n const handler = this.getFilterHandler(rule.type);\n if (handler) {\n handler.call(this, query, rule.column, rule.columns, value, options);\n return;\n }\n\n // 3. Standard where operators\n this.applyWhereOperator(query, rule.type, rule.column, rule.columns, value, rule.key);\n }\n\n /**\n * Get filter handler for predefined types\n */\n private getFilterHandler(type: string): Function | undefined {\n const handlers: Record<string, Function> = {\n // Boolean filters\n bool: this.handleBoolean,\n boolean: this.handleBoolean,\n\n // Numeric filters\n int: this.handleInt,\n integer: this.handleInt,\n \"!int\": this.handleNotInt,\n \"int>\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">\"),\n \"int>=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">=\"),\n \"int<\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<\"),\n \"int<=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<=\"),\n inInt: this.handleInInt,\n number: this.handleNumber,\n inNumber: this.handleInNumber,\n float: this.handleFloat,\n double: this.handleFloat,\n inFloat: this.handleInNumber,\n\n // Null filters\n null: this.handleNull,\n notNull: this.handleNotNull,\n \"!null\": this.handleNotNull,\n\n // Date filters\n date: this.handleDate,\n \"date>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">\"),\n \"date>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">=\"),\n \"date<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<\"),\n \"date<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<=\"),\n dateBetween: this.handleDateBetween,\n inDate: this.handleInDate,\n\n // DateTime filters\n dateTime: this.handleDateTime,\n \"dateTime>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">\"),\n \"dateTime>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">=\"),\n \"dateTime<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<\"),\n \"dateTime<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<=\"),\n dateTimeBetween: this.handleDateTimeBetween,\n inDateTime: this.handleInDateTime,\n\n // Scope filter - applies local scope when value is truthy\n scope: this.handleScope,\n\n // With filter - eager-loads a relation when value is truthy\n with: this.handleWith,\n\n // JoinWith filter - eager-loads a relation via SQL JOIN when value is truthy\n joinWith: this.handleJoinWith,\n\n // Vector similarity search — calls similarTo(column, embedding[])\n similarTo: this.handleSimilarTo,\n };\n\n return handlers[type];\n }\n\n /**\n * Apply standard where operators\n */\n private applyWhereOperator(\n query: QueryBuilderContract,\n operator: string,\n column?: string,\n columns?: string[],\n value?: any,\n ruleName?: string,\n ): void {\n // Handle \"in\" prefix for array values\n if (operator.startsWith(\"in\") && operator !== \"int\" && !Array.isArray(value)) {\n value = [value];\n }\n\n // Single column\n if (column) {\n switch (operator) {\n case \"=\":\n query.where(column, value);\n break;\n case \"!=\":\n case \"<>\":\n query.where(column, \"!=\", value);\n break;\n case \">\":\n case \">=\":\n case \"<\":\n case \"<=\":\n query.where(column, operator, value);\n break;\n case \"in\":\n query.whereIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"not in\":\n query.whereNotIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"like\":\n query.whereLike(column, value);\n break;\n case \"not like\":\n query.whereNotLike(column, value);\n break;\n case \"between\":\n query.whereBetween(column, value);\n break;\n case \"not between\":\n query.whereNotBetween(column, value);\n break;\n }\n }\n // Multiple columns (OR condition)\n else if (columns) {\n if (operator === \"=\") {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = value;\n }\n query.orWhere(conditions);\n } else if (operator === \"like\") {\n // (col0 LIKE value OR col1 LIKE value OR ...), OR'd with prior conditions —\n // the object form of orWhere() only ever emits \"=\", so \"like\" needs the\n // callback/group form to keep the operator instead of silently degrading\n // to equality (D1).\n query.orWhere((sub: QueryBuilderContract) => {\n columns.forEach((col, index) => {\n if (index === 0) {\n sub.where(col, \"like\", value);\n } else {\n sub.orWhere(col, \"like\", value);\n }\n });\n });\n } else {\n // Every other operator silently degraded to equality via the object form\n // of orWhere() — refuse instead of returning a correct-looking, wrong query.\n throw new Error(\n `Unsupported multi-column filter operator \"${operator}\" for rule \"${ruleName ?? columns.join(\",\")}\". ` +\n `Multi-column filters only support \"=\" and \"like\".`,\n );\n }\n }\n }\n\n // ============================================================================\n // BOOLEAN FILTERS\n // ============================================================================\n\n /**\n * Coerce a filter value to a boolean.\n *\n * Recognized truthy forms: `true`, `1`, `\"true\"`, `\"1\"`.\n * Recognized falsy forms: `false`, `0`, `\"false\"`, `\"0\"`.\n * Anything else falls back to `Boolean(value)`.\n *\n * NOTE: this intentionally does NOT treat \"non-empty\" as true — the previous\n * `|| !isEmpty(value)` fallback coerced `false`/`0` to `true`, inverting the\n * filter for explicitly-false values.\n */\n private coerceBoolean(value: any): boolean {\n if (value === true || value === 1 || value === \"true\" || value === \"1\") {\n return true;\n }\n\n if (value === false || value === 0 || value === \"false\" || value === \"0\") {\n return false;\n }\n\n return Boolean(value);\n }\n\n private handleBoolean(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const boolValue = this.coerceBoolean(value);\n if (column) {\n query.where(column, boolValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = boolValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // NUMERIC FILTERS\n // ============================================================================\n private handleInt(query: QueryBuilderContract, column?: string, columns?: string[], value?: any) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, intValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = intValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleNotInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, \"!=\", intValue);\n } else if (columns) {\n // Use multiple orWhere calls for OR logic across columns\n for (const col of columns) {\n query.orWhere(col, \"!=\", intValue);\n }\n }\n }\n\n private handleIntComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n operator: string,\n ) {\n const intValue = parseInt(value);\n if (column) {\n query.where(column, operator, intValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere(col, operator, intValue);\n }\n }\n }\n\n private handleInInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => parseInt(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, values));\n }\n }\n }\n\n private handleNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const numValue = Number(value);\n if (column) {\n query.where(column, numValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = numValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleInNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => Number(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, values));\n }\n }\n }\n\n private handleFloat(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const floatValue = parseFloat(value);\n if (column) {\n query.where(column, floatValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = floatValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n // ============================================================================\n // NULL FILTERS\n // ============================================================================\n\n private handleNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNull(column);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere({ [col]: null });\n }\n }\n }\n\n private handleNotNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNotNull(column);\n } else if (columns) {\n // Use whereNotNull for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereNotNull(col));\n }\n }\n }\n\n // ============================================================================\n // SCOPE FILTER\n // ============================================================================\n\n /**\n * Handle scope filter - applies local scope and passes the filter value.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * active: \"scope\", // Uses the filter key as scope name\n * isAdmin: [\"scope\", \"admin\"] // Uses custom scope name\n * }\n * ```\n *\n * When list({ active: true }) is called, it will call query.scope(\"active\", true)\n * When list({ status: \"pending\" }) is called, it will call query.scope(\"status\", \"pending\")\n */\n private handleScope(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n // column holds the scope name (either the filter key or custom name from array format)\n if (column) {\n query.scope(column, value);\n }\n }\n\n // ============================================================================\n // WITH (EAGER LOAD) FILTER\n // ============================================================================\n\n /**\n * Handle with filter - eager-loads a relation when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"with\", \"ai_model\"], // load single relation\n * with_all: [\"with\", [\"ai_model\", \"unit\"]] // load multiple relations\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.with(\"ai_model\")\n */\n private handleWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n // Load a single named relation\n if (column) {\n if (query.with) {\n query.with(column);\n }\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n for (const relation of columns) {\n if (query.with) {\n query.with(relation);\n }\n }\n }\n }\n\n /**\n * Handle joinWith filter - eager-loads a relation via SQL JOIN when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"joinWith\", \"ai_model\"], // load single relation via join\n * with_all: [\"joinWith\", [\"ai_model\", \"unit\"]] // load multiple relations via join\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.joinWith(\"ai_model\")\n */\n private handleJoinWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n if (!query.joinWith) {\n console.warn(\n \"[Repository] joinWith is not supported by the query builder. using with instead.\",\n );\n return this.handleWith(query, column, columns, value);\n }\n\n // Load a single named relation\n if (column) {\n query.joinWith(column);\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n query.joinWith(...columns);\n }\n }\n\n // ============================================================================\n // VECTOR FILTER\n // ============================================================================\n\n /**\n * Handle similarTo filter — performs vector similarity search.\n *\n * The filter value must be a `number[]` (pre-computed embedding).\n * Delegates to `query.similarTo(column, embedding)` which is handled\n * driver-specifically (pgvector or MongoDB Atlas $vectorSearch).\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * organization_id: \"=\",\n * embedding: \"similarTo\",\n * }\n * ```\n *\n * Then in the service:\n * ```typescript\n * await vectorsRepository.list({ embedding: queryEmbedding, organization_id: orgId });\n * ```\n */\n private handleSimilarTo(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n if (!column || !Array.isArray(value)) return;\n\n // Cast to any: Cascade's internal QueryBuilderContract still uses nearestTo;\n // our wrapper (CascadeQueryBuilder.similarTo) delegates to it correctly.\n (query as any).similarTo(column, value);\n }\n\n // ============================================================================\n // DATE FILTERS\n // ============================================================================\n\n private handleDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n query.whereDate(column, dateValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereDate(col, dateValue));\n }\n }\n }\n\n private handleDateComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n if (operator === \">\" || operator === \">=\") {\n query.whereDateAfter(column, dateValue);\n } else {\n query.whereDateBefore(column, dateValue);\n }\n } else if (columns) {\n for (const col of columns) {\n if (operator === \">\" || operator === \">=\") {\n query.orWhere((q: any) => q.whereDateAfter(col, dateValue));\n } else {\n query.orWhere((q: any) => q.whereDateBefore(col, dateValue));\n }\n }\n }\n }\n\n private handleDateBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDate(v, options?.dateFormat));\n\n // `value.length === 2` is checked above, so both exist — but the compiler\n // does not narrow destructured elements from a length test. Returning\n // rather than defaulting: this builds a date RANGE for a query, and a\n // half-formed range would silently filter on a boundary the caller never\n // asked for.\n if (start === undefined || end === undefined) return;\n\n if (column) {\n query.whereDateBetween(column, [start, end]);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereDateBetween(col, [start, end]));\n }\n }\n }\n\n private handleInDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDate(v, options?.dateFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, dates));\n }\n }\n }\n\n // ============================================================================\n // DATETIME FILTERS\n // ============================================================================\n\n private handleDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, dateValue);\n } else if (columns) {\n const conditions: any = {};\n for (const col of columns) {\n conditions[col] = dateValue;\n }\n\n query.orWhere(conditions);\n }\n }\n\n private handleDateTimeComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, operator, dateValue);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere(col, operator, dateValue);\n }\n }\n }\n\n private handleDateTimeBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDateTime(v, options?.dateTimeFormat));\n if (column) {\n query.whereBetween(column, [start, end]);\n } else if (columns) {\n for (const col of columns) {\n query.orWhere((q: any) => q.whereBetween(col, [start, end]));\n }\n }\n }\n\n private handleInDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDateTime(v, options?.dateTimeFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Use multiple orWhere calls with whereIn for each column\n for (const col of columns) {\n query.orWhere((q: any) => q.whereIn(col, dates));\n }\n }\n }\n\n // ============================================================================\n // DATE PARSING UTILITIES\n // ============================================================================\n\n /**\n * Parse date string to Date object\n * TODO: Implement proper date parsing with format support\n */\n private parseDate(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n return new Date(value);\n }\n\n /**\n * Parse datetime string to Date object\n * TODO: Implement proper datetime parsing with format support\n */\n private parseDateTime(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n return new Date(value);\n }\n}\n"],"mappings":";;;;;AAUA,IAAa,mBAAb,MAA8B;;;;;;;;;CAS5B,AAAO,MACL,OACA,SACA,MACA,SACM;EAGN,KAAK,MAAM,CAAC,KAAK,eAAe,OAAO,QAAQ,OAAO,GAAG;GACvD,MAAM,QAAQ,KAAK;GAEnB,IAAI,UAAU,QAAW;GACzB,IAAI,eAAe,QAAW;GAE9B,MAAM,OAAO,KAAK,gBAAgB,KAAK,UAAU;GACjD,KAAK,gBAAgB,OAAO,MAAM,OAAO,MAAM,OAAO;EACxD;CACF;;;;CAKA,AAAQ,gBAAgB,KAAa,MAAkB;EAErD,IAAI,OAAO,SAAS,YAClB,OAAO;GAAE,MAAM;GAAY,IAAI;GAAM,QAAQ;GAAK;EAAI;EAIxD,IAAI,MAAM,QAAQ,IAAI,GAAG;GACvB,MAAM,CAAC,UAAU,UAAU;GAE3B,IAAI,WAAW,QACb,OAAO;IAAE,MAAM;IAAU,QAAQ;IAAK,SAAS;IAAW;GAAI;GAGhE,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO;IAAE,MAAM;IAAU,QAAQ;IAAW,SAAS;IAAQ;GAAI;GAGnE,OAAO;IAAE,MAAM;IAAU,QAAQ;IAAQ,SAAS;IAAW;GAAI;EACnE;EAGA,OAAO;GAAE,MAAM;GAAM,QAAQ;GAAK,SAAS;GAAW;EAAI;CAC5D;;;;CAKA,AAAQ,gBACN,OACA,MACA,OACA,MACA,SACM;EAEN,IAAI,KAAK,SAAS,YAAY;GAC5B,KAAK,GAAG,OAAO,OAAO,IAAI;GAC1B;EACF;EAGA,MAAM,UAAU,KAAK,iBAAiB,KAAK,IAAI;EAC/C,IAAI,SAAS;GACX,QAAQ,KAAK,MAAM,OAAO,KAAK,QAAQ,KAAK,SAAS,OAAO,OAAO;GACnE;EACF;EAGA,KAAK,mBAAmB,OAAO,KAAK,MAAM,KAAK,QAAQ,KAAK,SAAS,OAAO,KAAK,GAAG;CACtF;;;;CAKA,AAAQ,iBAAiB,MAAoC;EAqE3D,OAAO;GAlEL,MAAM,KAAK;GACX,SAAS,KAAK;GAGd,KAAK,KAAK;GACV,SAAS,KAAK;GACd,QAAQ,KAAK;GACb,SAAS,GAAQ,KAAU,MAAW,QACpC,KAAK,oBAAoB,GAAG,KAAK,MAAM,KAAK,GAAG;GACjD,UAAU,GAAQ,KAAU,MAAW,QACrC,KAAK,oBAAoB,GAAG,KAAK,MAAM,KAAK,IAAI;GAClD,SAAS,GAAQ,KAAU,MAAW,QACpC,KAAK,oBAAoB,GAAG,KAAK,MAAM,KAAK,GAAG;GACjD,UAAU,GAAQ,KAAU,MAAW,QACrC,KAAK,oBAAoB,GAAG,KAAK,MAAM,KAAK,IAAI;GAClD,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,UAAU,KAAK;GACf,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,SAAS,KAAK;GAGd,MAAM,KAAK;GACX,SAAS,KAAK;GACd,SAAS,KAAK;GAGd,MAAM,KAAK;GACX,UAAU,GAAQ,KAAU,MAAW,KAAU,SAC/C,KAAK,qBAAqB,GAAG,KAAK,MAAM,KAAK,MAAM,GAAG;GACxD,WAAW,GAAQ,KAAU,MAAW,KAAU,SAChD,KAAK,qBAAqB,GAAG,KAAK,MAAM,KAAK,MAAM,IAAI;GACzD,UAAU,GAAQ,KAAU,MAAW,KAAU,SAC/C,KAAK,qBAAqB,GAAG,KAAK,MAAM,KAAK,MAAM,GAAG;GACxD,WAAW,GAAQ,KAAU,MAAW,KAAU,SAChD,KAAK,qBAAqB,GAAG,KAAK,MAAM,KAAK,MAAM,IAAI;GACzD,aAAa,KAAK;GAClB,QAAQ,KAAK;GAGb,UAAU,KAAK;GACf,cAAc,GAAQ,KAAU,MAAW,KAAU,SACnD,KAAK,yBAAyB,GAAG,KAAK,MAAM,KAAK,MAAM,GAAG;GAC5D,eAAe,GAAQ,KAAU,MAAW,KAAU,SACpD,KAAK,yBAAyB,GAAG,KAAK,MAAM,KAAK,MAAM,IAAI;GAC7D,cAAc,GAAQ,KAAU,MAAW,KAAU,SACnD,KAAK,yBAAyB,GAAG,KAAK,MAAM,KAAK,MAAM,GAAG;GAC5D,eAAe,GAAQ,KAAU,MAAW,KAAU,SACpD,KAAK,yBAAyB,GAAG,KAAK,MAAM,KAAK,MAAM,IAAI;GAC7D,iBAAiB,KAAK;GACtB,YAAY,KAAK;GAGjB,OAAO,KAAK;GAGZ,MAAM,KAAK;GAGX,UAAU,KAAK;GAGf,WAAW,KAAK;EAGJ,EAAE;CAClB;;;;CAKA,AAAQ,mBACN,OACA,UACA,QACA,SACA,OACA,UACM;EAEN,IAAI,SAAS,WAAW,IAAI,KAAK,aAAa,SAAS,CAAC,MAAM,QAAQ,KAAK,GACzE,QAAQ,CAAC,KAAK;EAIhB,IAAI,QACF,QAAQ,UAAR;GACE,KAAK;IACH,MAAM,MAAM,QAAQ,KAAK;IACzB;GACF,KAAK;GACL,KAAK;IACH,MAAM,MAAM,QAAQ,MAAM,KAAK;IAC/B;GACF,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;IACH,MAAM,MAAM,QAAQ,UAAU,KAAK;IACnC;GACF,KAAK;IACH,MAAM,QAAQ,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;IAC5D;GACF,KAAK;IACH,MAAM,WAAW,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;IAC/D;GACF,KAAK;IACH,MAAM,UAAU,QAAQ,KAAK;IAC7B;GACF,KAAK;IACH,MAAM,aAAa,QAAQ,KAAK;IAChC;GACF,KAAK;IACH,MAAM,aAAa,QAAQ,KAAK;IAChC;GACF,KAAK;IACH,MAAM,gBAAgB,QAAQ,KAAK;IACnC;EACJ;OAGG,IAAI,SACP,IAAI,aAAa,KAAK;GACpB,MAAM,aAAkB,CAAC;GACzB,KAAK,MAAM,OAAO,SAChB,WAAW,OAAO;GAEpB,MAAM,QAAQ,UAAU;EAC1B,OAAO,IAAI,aAAa,QAKtB,MAAM,SAAS,QAA8B;GAC3C,QAAQ,SAAS,KAAK,UAAU;IAC9B,IAAI,UAAU,GACZ,IAAI,MAAM,KAAK,QAAQ,KAAK;SAE5B,IAAI,QAAQ,KAAK,QAAQ,KAAK;GAElC,CAAC;EACH,CAAC;OAID,MAAM,IAAI,MACR,6CAA6C,SAAS,cAAc,YAAY,QAAQ,KAAK,GAAG,EAAE,qDAEpG;CAGN;;;;;;;;;;;;CAiBA,AAAQ,cAAc,OAAqB;EACzC,IAAI,UAAU,QAAQ,UAAU,KAAK,UAAU,UAAU,UAAU,KACjE,OAAO;EAGT,IAAI,UAAU,SAAS,UAAU,KAAK,UAAU,WAAW,UAAU,KACnE,OAAO;EAGT,OAAO,QAAQ,KAAK;CACtB;CAEA,AAAQ,cACN,OACA,QACA,SACA,OACA;EACA,MAAM,YAAY,KAAK,cAAc,KAAK;EAC1C,IAAI,QACF,MAAM,MAAM,QAAQ,SAAS;OACxB,IAAI,SAAS;GAClB,MAAM,aAAkB,CAAC;GACzB,KAAK,MAAM,OAAO,SAChB,WAAW,OAAO;GAGpB,MAAM,QAAQ,UAAU;EAC1B;CACF;CAKA,AAAQ,UAAU,OAA6B,QAAiB,SAAoB,OAAa;EAC/F,MAAM,WAAW,SAAS,KAAK;EAC/B,IAAI,QACF,MAAM,MAAM,QAAQ,QAAQ;OACvB,IAAI,SAAS;GAClB,MAAM,aAAkB,CAAC;GACzB,KAAK,MAAM,OAAO,SAChB,WAAW,OAAO;GAGpB,MAAM,QAAQ,UAAU;EAC1B;CACF;CAEA,AAAQ,aACN,OACA,QACA,SACA,OACA;EACA,MAAM,WAAW,SAAS,KAAK;EAC/B,IAAI,QACF,MAAM,MAAM,QAAQ,MAAM,QAAQ;OAC7B,IAAI,SAET,KAAK,MAAM,OAAO,SAChB,MAAM,QAAQ,KAAK,MAAM,QAAQ;CAGvC;CAEA,AAAQ,oBACN,OACA,QACA,SACA,OACA,UACA;EACA,MAAM,WAAW,SAAS,KAAK;EAC/B,IAAI,QACF,MAAM,MAAM,QAAQ,UAAU,QAAQ;OACjC,IAAI,SACT,KAAK,MAAM,OAAO,SAChB,MAAM,QAAQ,KAAK,UAAU,QAAQ;CAG3C;CAEA,AAAQ,YACN,OACA,QACA,SACA,OACA;EACA,MAAM,UAAU,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAC,CAAE,KAAK,MAAW,SAAS,CAAC,CAAC;EACnF,IAAI,QACF,MAAM,QAAQ,QAAQ,MAAM;OACvB,IAAI,SAET,KAAK,MAAM,OAAO,SAChB,MAAM,SAAS,MAAW,EAAE,QAAQ,KAAK,MAAM,CAAC;CAGtD;CAEA,AAAQ,aACN,OACA,QACA,SACA,OACA;EACA,MAAM,WAAW,OAAO,KAAK;EAC7B,IAAI,QACF,MAAM,MAAM,QAAQ,QAAQ;OACvB,IAAI,SAAS;GAClB,MAAM,aAAkB,CAAC;GACzB,KAAK,MAAM,OAAO,SAChB,WAAW,OAAO;GAGpB,MAAM,QAAQ,UAAU;EAC1B;CACF;CAEA,AAAQ,eACN,OACA,QACA,SACA,OACA;EACA,MAAM,UAAU,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAC,CAAE,KAAK,MAAW,OAAO,CAAC,CAAC;EACjF,IAAI,QACF,MAAM,QAAQ,QAAQ,MAAM;OACvB,IAAI,SAET,KAAK,MAAM,OAAO,SAChB,MAAM,SAAS,MAAW,EAAE,QAAQ,KAAK,MAAM,CAAC;CAGtD;CAEA,AAAQ,YACN,OACA,QACA,SACA,OACA;EACA,MAAM,aAAa,WAAW,KAAK;EACnC,IAAI,QACF,MAAM,MAAM,QAAQ,UAAU;OACzB,IAAI,SAAS;GAClB,MAAM,aAAkB,CAAC;GACzB,KAAK,MAAM,OAAO,SAChB,WAAW,OAAO;GAGpB,MAAM,QAAQ,UAAU;EAC1B;CACF;CAMA,AAAQ,WAAW,OAA6B,QAAiB,SAAoB;EACnF,IAAI,QACF,MAAM,UAAU,MAAM;OACjB,IAAI,SACT,KAAK,MAAM,OAAO,SAChB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC;CAGnC;CAEA,AAAQ,cAAc,OAA6B,QAAiB,SAAoB;EACtF,IAAI,QACF,MAAM,aAAa,MAAM;OACpB,IAAI,SAET,KAAK,MAAM,OAAO,SAChB,MAAM,SAAS,MAAW,EAAE,aAAa,GAAG,CAAC;CAGnD;;;;;;;;;;;;;;;CAoBA,AAAQ,YACN,OACA,QACA,UACA,OACA;EAEA,IAAI,QACF,MAAM,MAAM,QAAQ,KAAK;CAE7B;;;;;;;;;;;;;;CAmBA,AAAQ,WACN,OACA,QACA,SACA,OACA;EACA,IAAI,CAAC,OAAO;EAGZ,IAAI,QAAQ;GACV,IAAI,MAAM,MACR,MAAM,KAAK,MAAM;GAEnB;EACF;EAGA,IAAI,SACF;QAAK,MAAM,YAAY,SACrB,IAAI,MAAM,MACR,MAAM,KAAK,QAAQ;EAEvB;CAEJ;;;;;;;;;;;;;;CAeA,AAAQ,eACN,OACA,QACA,SACA,OACA;EACA,IAAI,CAAC,OAAO;EAEZ,IAAI,CAAC,MAAM,UAAU;GACnB,QAAQ,KACN,kFACF;GACA,OAAO,KAAK,WAAW,OAAO,QAAQ,SAAS,KAAK;EACtD;EAGA,IAAI,QAAQ;GACV,MAAM,SAAS,MAAM;GACrB;EACF;EAGA,IAAI,SACF,MAAM,SAAS,GAAG,OAAO;CAE7B;;;;;;;;;;;;;;;;;;;;;CA0BA,AAAQ,gBACN,OACA,QACA,UACA,OACA;EACA,IAAI,CAAC,UAAU,CAAC,MAAM,QAAQ,KAAK,GAAG;EAItC,AAAC,MAAc,UAAU,QAAQ,KAAK;CACxC;CAMA,AAAQ,WACN,OACA,QACA,SACA,OACA,SACA;EACA,MAAM,YAAY,KAAK,UAAU,OAAO,SAAS,UAAU;EAC3D,IAAI,QACF,MAAM,UAAU,QAAQ,SAAS;OAC5B,IAAI,SACT,KAAK,MAAM,OAAO,SAChB,MAAM,SAAS,MAAW,EAAE,UAAU,KAAK,SAAS,CAAC;CAG3D;CAEA,AAAQ,qBACN,OACA,QACA,SACA,OACA,SACA,UACA;EACA,MAAM,YAAY,KAAK,UAAU,OAAO,SAAS,UAAU;EAC3D,IAAI,QACF,IAAI,aAAa,OAAO,aAAa,MACnC,MAAM,eAAe,QAAQ,SAAS;OAEtC,MAAM,gBAAgB,QAAQ,SAAS;OAEpC,IAAI,SACT,KAAK,MAAM,OAAO,SAChB,IAAI,aAAa,OAAO,aAAa,MACnC,MAAM,SAAS,MAAW,EAAE,eAAe,KAAK,SAAS,CAAC;OAE1D,MAAM,SAAS,MAAW,EAAE,gBAAgB,KAAK,SAAS,CAAC;CAInE;CAEA,AAAQ,kBACN,OACA,QACA,SACA,OACA,SACA;EACA,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;EACjD,MAAM,CAAC,OAAO,OAAO,MAAM,KAAK,MAAW,KAAK,UAAU,GAAG,SAAS,UAAU,CAAC;EAOjF,IAAI,UAAU,UAAa,QAAQ,QAAW;EAE9C,IAAI,QACF,MAAM,iBAAiB,QAAQ,CAAC,OAAO,GAAG,CAAC;OACtC,IAAI,SACT,KAAK,MAAM,OAAO,SAChB,MAAM,SAAS,MAAW,EAAE,iBAAiB,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;CAGrE;CAEA,AAAQ,aACN,OACA,QACA,SACA,OACA,SACA;EACA,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAC,CAAE,KAAK,MAC1D,KAAK,UAAU,GAAG,SAAS,UAAU,CACvC;EACA,IAAI,QACF,MAAM,QAAQ,QAAQ,KAAK;OACtB,IAAI,SAET,KAAK,MAAM,OAAO,SAChB,MAAM,SAAS,MAAW,EAAE,QAAQ,KAAK,KAAK,CAAC;CAGrD;CAMA,AAAQ,eACN,OACA,QACA,SACA,OACA,SACA;EACA,MAAM,YAAY,KAAK,cAAc,OAAO,SAAS,cAAc;EACnE,IAAI,QACF,MAAM,MAAM,QAAQ,SAAS;OACxB,IAAI,SAAS;GAClB,MAAM,aAAkB,CAAC;GACzB,KAAK,MAAM,OAAO,SAChB,WAAW,OAAO;GAGpB,MAAM,QAAQ,UAAU;EAC1B;CACF;CAEA,AAAQ,yBACN,OACA,QACA,SACA,OACA,SACA,UACA;EACA,MAAM,YAAY,KAAK,cAAc,OAAO,SAAS,cAAc;EACnE,IAAI,QACF,MAAM,MAAM,QAAQ,UAAU,SAAS;OAClC,IAAI,SACT,KAAK,MAAM,OAAO,SAChB,MAAM,QAAQ,KAAK,UAAU,SAAS;CAG5C;CAEA,AAAQ,sBACN,OACA,QACA,SACA,OACA,SACA;EACA,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;EACjD,MAAM,CAAC,OAAO,OAAO,MAAM,KAAK,MAAW,KAAK,cAAc,GAAG,SAAS,cAAc,CAAC;EACzF,IAAI,QACF,MAAM,aAAa,QAAQ,CAAC,OAAO,GAAG,CAAC;OAClC,IAAI,SACT,KAAK,MAAM,OAAO,SAChB,MAAM,SAAS,MAAW,EAAE,aAAa,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;CAGjE;CAEA,AAAQ,iBACN,OACA,QACA,SACA,OACA,SACA;EACA,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAC,CAAE,KAAK,MAC1D,KAAK,cAAc,GAAG,SAAS,cAAc,CAC/C;EACA,IAAI,QACF,MAAM,QAAQ,QAAQ,KAAK;OACtB,IAAI,SAET,KAAK,MAAM,OAAO,SAChB,MAAM,SAAS,MAAW,EAAE,QAAQ,KAAK,KAAK,CAAC;CAGrD;;;;;CAUA,AAAQ,UAAU,OAAY,QAAuB;EACnD,IAAI,iBAAiB,MAAM,OAAO;EAClC,OAAO,IAAI,KAAK,KAAK;CACvB;;;;;CAMA,AAAQ,cAAc,OAAY,QAAuB;EACvD,IAAI,iBAAiB,MAAM,OAAO;EAClC,OAAO,IAAI,KAAK,KAAK;CACvB;AACF"}
1
+ {"version":3,"file":"filter-applicator.mjs","names":[],"sources":["../../../../../../../../../core/src/repositories/adapters/cascade/filter-applicator.ts"],"sourcesContent":["import type {\n QueryBuilderContract as CascadeQueryBuilder,\n QueryBuilderContract,\n} from \"@warlock.js/cascade\";\nimport dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat.js\";\nimport { BadRequestError } from \"../../../http/errors/errors\";\nimport type { FilterOptions, FilterRule, FilterRules } from \"../../contracts\";\n\ndayjs.extend(customParseFormat);\n\n/**\n * Applies repository filters to a Cascade-Next query builder\n * Translates repository filter rules into Cascade query builder method calls\n */\nexport class FilterApplicator {\n /**\n * Apply filters to a Cascade query builder\n *\n * @param query - Cascade query builder instance\n * @param filters - Filter structure defining how to filter\n * @param data - Data containing filter values\n * @param options - Additional filter options (date formats, etc.)\n */\n public apply(\n query: CascadeQueryBuilder<any>,\n filters: FilterRules,\n data: any,\n options: FilterOptions,\n ): void {\n // `Object.entries` hands the rule over already narrowed; a `for...in` index\n // read is `FilterRule | undefined` under the strictness contract.\n for (const [key, filterRule] of Object.entries(filters)) {\n const value = data[key];\n\n if (value === undefined) continue;\n if (filterRule === undefined) continue;\n\n const rule = this.parseFilterRule(key, filterRule);\n this.applyFilterRule(query, rule, value, data, options);\n }\n }\n\n /**\n * Parse a filter rule into a structured format\n */\n private parseFilterRule(key: string, rule: FilterRule) {\n // Handle custom function\n if (typeof rule === \"function\") {\n return { type: \"function\", fn: rule, column: key, key };\n }\n\n // Handle array format: [\"operator\"] or [\"operator\", \"column\"] or [\"operator\", [\"col1\", \"col2\"]]\n if (Array.isArray(rule)) {\n const [operator, target] = rule;\n\n if (target === undefined) {\n return { type: operator, column: key, columns: undefined, key };\n }\n\n if (Array.isArray(target)) {\n return { type: operator, column: undefined, columns: target, key };\n }\n\n return { type: operator, column: target, columns: undefined, key };\n }\n\n // Handle simple operator string\n return { type: rule, column: key, columns: undefined, key };\n }\n\n /**\n * Apply a single filter rule to the query\n */\n private applyFilterRule(\n query: CascadeQueryBuilder<any>,\n rule: any,\n value: any,\n data: any,\n options: FilterOptions,\n ): void {\n // 1. Custom function filter\n if (rule.type === \"function\") {\n rule.fn(value, query, data);\n return;\n }\n\n // 2. Predefined filter types\n const handler = this.getFilterHandler(rule.type);\n if (handler) {\n handler.call(this, query, rule.column, rule.columns, value, options);\n return;\n }\n\n // 3. Standard where operators\n this.applyWhereOperator(query, rule.type, rule.column, rule.columns, value, rule.key);\n }\n\n /**\n * Get filter handler for predefined types\n */\n private getFilterHandler(type: string): Function | undefined {\n const handlers: Record<string, Function> = {\n // Boolean filters\n bool: this.handleBoolean,\n boolean: this.handleBoolean,\n\n // Numeric filters\n int: this.handleInt,\n integer: this.handleInt,\n \"!int\": this.handleNotInt,\n \"int>\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">\"),\n \"int>=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \">=\"),\n \"int<\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<\"),\n \"int<=\": (q: any, col: any, cols: any, val: any) =>\n this.handleIntComparison(q, col, cols, val, \"<=\"),\n inInt: this.handleInInt,\n number: this.handleNumber,\n inNumber: this.handleInNumber,\n float: this.handleFloat,\n double: this.handleFloat,\n inFloat: this.handleInNumber,\n\n // Null filters\n null: this.handleNull,\n notNull: this.handleNotNull,\n \"!null\": this.handleNotNull,\n\n // Date filters\n date: this.handleDate,\n \"date>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">\"),\n \"date>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \">=\"),\n \"date<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<\"),\n \"date<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateComparison(q, col, cols, val, opts, \"<=\"),\n dateBetween: this.handleDateBetween,\n inDate: this.handleInDate,\n\n // DateTime filters\n dateTime: this.handleDateTime,\n \"dateTime>\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">\"),\n \"dateTime>=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \">=\"),\n \"dateTime<\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<\"),\n \"dateTime<=\": (q: any, col: any, cols: any, val: any, opts: any) =>\n this.handleDateTimeComparison(q, col, cols, val, opts, \"<=\"),\n dateTimeBetween: this.handleDateTimeBetween,\n inDateTime: this.handleInDateTime,\n\n // Scope filter - applies local scope when value is truthy\n scope: this.handleScope,\n\n // With filter - eager-loads a relation when value is truthy\n with: this.handleWith,\n\n // JoinWith filter - eager-loads a relation via SQL JOIN when value is truthy\n joinWith: this.handleJoinWith,\n\n // Vector similarity search — calls similarTo(column, embedding[])\n similarTo: this.handleSimilarTo,\n };\n\n return handlers[type];\n }\n\n /**\n * `like` is a contains match on every driver: a value without an explicit\n * `%` is wrapped as `%value%` (Postgres would otherwise match exactly while\n * Mongo matched a substring). Values carrying their own `%` are kept as-is.\n */\n private likePattern(value: any): any {\n if (typeof value !== \"string\" || value.includes(\"%\")) return value;\n return `%${value}%`;\n }\n\n /**\n * Coerce a client value to an integer or reject with a 400.\n */\n private toInt(value: any): number {\n const parsed = parseInt(value);\n if (Number.isNaN(parsed)) {\n throw new BadRequestError(`Invalid integer filter value \"${value}\"`);\n }\n return parsed;\n }\n\n /**\n * Coerce a client value to a number or reject with a 400.\n */\n private toNumber(value: any): number {\n const parsed = Number(value);\n if (value === \"\" || value === null || Number.isNaN(parsed)) {\n throw new BadRequestError(`Invalid number filter value \"${value}\"`);\n }\n return parsed;\n }\n\n /**\n * Apply standard where operators\n */\n private applyWhereOperator(\n query: QueryBuilderContract,\n operator: string,\n column?: string,\n columns?: string[],\n value?: any,\n ruleName?: string,\n ): void {\n // Handle \"in\" prefix for array values\n if (operator.startsWith(\"in\") && operator !== \"int\" && !Array.isArray(value)) {\n value = [value];\n }\n\n // Single column\n if (column) {\n switch (operator) {\n case \"=\":\n query.where(column, value);\n break;\n case \"!=\":\n case \"<>\":\n query.where(column, \"!=\", value);\n break;\n case \">\":\n case \">=\":\n case \"<\":\n case \"<=\":\n query.where(column, operator, value);\n break;\n case \"in\":\n query.whereIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"not in\":\n query.whereNotIn(column, Array.isArray(value) ? value : [value]);\n break;\n case \"like\":\n query.whereLike(column, this.likePattern(value));\n break;\n case \"startsWith\":\n query.whereLike(column, `${value}%`);\n break;\n case \"endsWith\":\n query.whereLike(column, `%${value}`);\n break;\n case \"not like\":\n query.whereNotLike(column, value);\n break;\n case \"between\":\n query.whereBetween(column, value);\n break;\n case \"not between\":\n query.whereNotBetween(column, value);\n break;\n }\n }\n // Multiple columns (OR condition)\n else if (columns) {\n if (operator === \"=\") {\n this.anyOf(query, columns, (q, col) => q.where(col, value));\n } else if (operator === \"like\") {\n // (col0 LIKE value OR col1 LIKE value OR ...), OR'd with prior conditions —\n // the object form of orWhere() only ever emits \"=\", so \"like\" needs the\n // callback/group form to keep the operator instead of silently degrading\n // to equality (D1).\n this.anyOf(query, columns, (q, col) => q.where(col, \"like\", this.likePattern(value)));\n } else {\n // Every other operator silently degraded to equality via the object form\n // of orWhere() — refuse instead of returning a correct-looking, wrong query.\n throw new Error(\n `Unsupported multi-column filter operator \"${operator}\" for rule \"${ruleName ?? columns.join(\",\")}\". ` +\n `Multi-column filters only support \"=\" and \"like\".`,\n );\n }\n }\n }\n\n /**\n * Apply a condition across several columns as ONE grouped OR: `(a OR b OR ...)`.\n * The group is ANDed with the rest of the query, so a tenant filter\n * (`org_id = 5`) can never be widened by a multi-column search. Never emit a\n * top-level `orWhere` for multi-column filters.\n */\n private anyOf(\n query: QueryBuilderContract,\n columns: string[],\n apply: (q: QueryBuilderContract, col: string) => void,\n ) {\n query.where((group: QueryBuilderContract) => {\n columns.forEach((col, index) => {\n const build = (q: QueryBuilderContract) => apply(q, col);\n\n if (index === 0) {\n group.where(build);\n } else {\n group.orWhere(build);\n }\n });\n });\n }\n\n // ============================================================================\n // BOOLEAN FILTERS\n // ============================================================================\n\n /**\n * Coerce a filter value to a boolean.\n *\n * Recognized truthy forms: `true`, `1`, `\"true\"`, `\"1\"`.\n * Recognized falsy forms: `false`, `0`, `\"false\"`, `\"0\"`.\n * Anything else falls back to `Boolean(value)`.\n *\n * NOTE: this intentionally does NOT treat \"non-empty\" as true — the previous\n * `|| !isEmpty(value)` fallback coerced `false`/`0` to `true`, inverting the\n * filter for explicitly-false values.\n */\n private coerceBoolean(value: any): boolean {\n if (value === true || value === 1 || value === \"true\" || value === \"1\") {\n return true;\n }\n\n if (value === false || value === 0 || value === \"false\" || value === \"0\") {\n return false;\n }\n\n return Boolean(value);\n }\n\n private handleBoolean(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const boolValue = this.coerceBoolean(value);\n if (column) {\n query.where(column, boolValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, boolValue));\n }\n }\n\n // ============================================================================\n // NUMERIC FILTERS\n // ============================================================================\n private handleInt(query: QueryBuilderContract, column?: string, columns?: string[], value?: any) {\n const intValue = this.toInt(value);\n if (column) {\n query.where(column, intValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, intValue));\n }\n }\n\n private handleNotInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const intValue = this.toInt(value);\n if (column) {\n query.where(column, \"!=\", intValue);\n } else if (columns) {\n // Grouped OR across columns\n this.anyOf(query, columns, (q, col) => q.where(col, \"!=\", intValue));\n }\n }\n\n private handleIntComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n operator: string,\n ) {\n const intValue = this.toInt(value);\n if (column) {\n query.where(column, operator, intValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, operator, intValue));\n }\n }\n\n private handleInInt(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => this.toInt(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Grouped OR of whereIn across columns\n this.anyOf(query, columns, (q, col) => q.whereIn(col, values));\n }\n }\n\n private handleNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const numValue = this.toNumber(value);\n if (column) {\n query.where(column, numValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, numValue));\n }\n }\n\n private handleInNumber(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const values = (Array.isArray(value) ? value : [value]).map((v: any) => this.toNumber(v));\n if (column) {\n query.whereIn(column, values);\n } else if (columns) {\n // Grouped OR of whereIn across columns\n this.anyOf(query, columns, (q, col) => q.whereIn(col, values));\n }\n }\n\n private handleFloat(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n const floatValue = parseFloat(value);\n if (column) {\n query.where(column, floatValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, floatValue));\n }\n }\n\n // ============================================================================\n // NULL FILTERS\n // ============================================================================\n\n private handleNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNull(column);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.whereNull(col));\n }\n }\n\n private handleNotNull(query: QueryBuilderContract, column?: string, columns?: string[]) {\n if (column) {\n query.whereNotNull(column);\n } else if (columns) {\n // Use whereNotNull for each column\n this.anyOf(query, columns, (q, col) => q.whereNotNull(col));\n }\n }\n\n // ============================================================================\n // SCOPE FILTER\n // ============================================================================\n\n /**\n * Handle scope filter - applies local scope and passes the filter value.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * active: \"scope\", // Uses the filter key as scope name\n * isAdmin: [\"scope\", \"admin\"] // Uses custom scope name\n * }\n * ```\n *\n * When list({ active: true }) is called, it will call query.scope(\"active\", true)\n * When list({ status: \"pending\" }) is called, it will call query.scope(\"status\", \"pending\")\n */\n private handleScope(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n // column holds the scope name (either the filter key or custom name from array format)\n if (column) {\n query.scope(column, value);\n }\n }\n\n // ============================================================================\n // WITH (EAGER LOAD) FILTER\n // ============================================================================\n\n /**\n * Handle with filter - eager-loads a relation when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"with\", \"ai_model\"], // load single relation\n * with_all: [\"with\", [\"ai_model\", \"unit\"]] // load multiple relations\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.with(\"ai_model\")\n */\n private handleWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n // Load a single named relation\n if (column) {\n if (query.with) {\n query.with(column);\n }\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n for (const relation of columns) {\n if (query.with) {\n query.with(relation);\n }\n }\n }\n }\n\n /**\n * Handle joinWith filter - eager-loads a relation via SQL JOIN when the filter value is truthy.\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * with_ai_model: [\"joinWith\", \"ai_model\"], // load single relation via join\n * with_all: [\"joinWith\", [\"ai_model\", \"unit\"]] // load multiple relations via join\n * }\n * ```\n *\n * When list({ with_ai_model: true }) is called, it will call query.joinWith(\"ai_model\")\n */\n private handleJoinWith(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n ) {\n if (!value) return;\n\n if (!query.joinWith) {\n console.warn(\n \"[Repository] joinWith is not supported by the query builder. using with instead.\",\n );\n return this.handleWith(query, column, columns, value);\n }\n\n // Load a single named relation\n if (column) {\n query.joinWith(column);\n return;\n }\n\n // Load multiple relations from the columns array\n if (columns) {\n query.joinWith(...columns);\n }\n }\n\n // ============================================================================\n // VECTOR FILTER\n // ============================================================================\n\n /**\n * Handle similarTo filter — performs vector similarity search.\n *\n * The filter value must be a `number[]` (pre-computed embedding).\n * Delegates to `query.similarTo(column, embedding)` which is handled\n * driver-specifically (pgvector or MongoDB Atlas $vectorSearch).\n *\n * Usage in filterBy:\n * ```typescript\n * filterBy: {\n * organization_id: \"=\",\n * embedding: \"similarTo\",\n * }\n * ```\n *\n * Then in the service:\n * ```typescript\n * await vectorsRepository.list({ embedding: queryEmbedding, organization_id: orgId });\n * ```\n */\n private handleSimilarTo(\n query: QueryBuilderContract,\n column?: string,\n _columns?: string[],\n value?: any,\n ) {\n if (!column || !Array.isArray(value)) return;\n\n // Cast to any: Cascade's internal QueryBuilderContract still uses nearestTo;\n // our wrapper (CascadeQueryBuilder.similarTo) delegates to it correctly.\n (query as any).similarTo(column, value);\n }\n\n // ============================================================================\n // DATE FILTERS\n // ============================================================================\n\n private handleDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n query.whereDate(column, dateValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.whereDate(col, dateValue));\n }\n }\n\n private handleDateComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDate(value, options?.dateFormat);\n if (column) {\n if (operator === \">\" || operator === \">=\") {\n query.whereDateAfter(column, dateValue);\n } else {\n query.whereDateBefore(column, dateValue);\n }\n } else if (columns) {\n const after = operator === \">\" || operator === \">=\";\n this.anyOf(query, columns, (q, col) =>\n after ? q.whereDateAfter(col, dateValue) : q.whereDateBefore(col, dateValue),\n );\n }\n }\n\n private handleDateBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDate(v, options?.dateFormat));\n\n // `value.length === 2` is checked above, so both exist — but the compiler\n // does not narrow destructured elements from a length test. Returning\n // rather than defaulting: this builds a date RANGE for a query, and a\n // half-formed range would silently filter on a boundary the caller never\n // asked for.\n if (start === undefined || end === undefined) return;\n\n if (column) {\n query.whereDateBetween(column, [start, end]);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.whereDateBetween(col, [start, end]));\n }\n }\n\n private handleInDate(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDate(v, options?.dateFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Grouped OR of whereIn across columns\n this.anyOf(query, columns, (q, col) => q.whereIn(col, dates));\n }\n }\n\n // ============================================================================\n // DATETIME FILTERS\n // ============================================================================\n\n private handleDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, dateValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, dateValue));\n }\n }\n\n private handleDateTimeComparison(\n query: QueryBuilderContract,\n column: any,\n columns: any,\n value: any,\n options: any,\n operator: string,\n ) {\n const dateValue = this.parseDateTime(value, options?.dateTimeFormat);\n if (column) {\n query.where(column, operator, dateValue);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.where(col, operator, dateValue));\n }\n }\n\n private handleDateTimeBetween(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n if (!Array.isArray(value) || value.length !== 2) return;\n const [start, end] = value.map((v: any) => this.parseDateTime(v, options?.dateTimeFormat));\n if (column) {\n query.whereBetween(column, [start, end]);\n } else if (columns) {\n this.anyOf(query, columns, (q, col) => q.whereBetween(col, [start, end]));\n }\n }\n\n private handleInDateTime(\n query: QueryBuilderContract,\n column?: string,\n columns?: string[],\n value?: any,\n options?: FilterOptions,\n ) {\n const dates = (Array.isArray(value) ? value : [value]).map((v: any) =>\n this.parseDateTime(v, options?.dateTimeFormat),\n );\n if (column) {\n query.whereIn(column, dates);\n } else if (columns) {\n // Grouped OR of whereIn across columns\n this.anyOf(query, columns, (q, col) => q.whereIn(col, dates));\n }\n }\n\n // ============================================================================\n // DATE PARSING UTILITIES\n // ============================================================================\n\n /**\n * Parse a date string with the declared format; invalid input is a 400.\n */\n private parseDate(value: any, format?: string): Date {\n return this.parseWithFormat(value, format);\n }\n\n /**\n * Parse a datetime string with the declared format; invalid input is a 400.\n */\n private parseDateTime(value: any, format?: string): Date {\n return this.parseWithFormat(value, format);\n }\n\n private parseWithFormat(value: any, format?: string): Date {\n if (value instanceof Date) return value;\n\n const parsed = format ? dayjs(String(value), format, true) : dayjs(value);\n\n if (!parsed.isValid()) {\n throw new BadRequestError(\n `Invalid date filter value \"${value}\"${format ? ` (expected ${format})` : \"\"}`,\n );\n }\n\n return parsed.toDate();\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { CascadeAdapter } from "./cascade-adapter.mjs";
1
+ import { CascadeAdapter, UPDATE_MANY_BATCH_SIZE } from "./cascade-adapter.mjs";
2
2
  import { CascadeQueryBuilder } from "./cascade-query-builder.mjs";
@@ -1,4 +1,4 @@
1
1
  import { CascadeQueryBuilder } from "./cascade-query-builder.mjs";
2
- import { CascadeAdapter } from "./cascade-adapter.mjs";
2
+ import { CascadeAdapter, UPDATE_MANY_BATCH_SIZE } from "./cascade-adapter.mjs";
3
3
 
4
4
  export { };
@@ -286,21 +286,21 @@ interface QueryBuilderContract<T> {
286
286
  * @example
287
287
  * await query.where("status", "paid").avg("total")
288
288
  */
289
- avg(field: string): Promise<number>;
289
+ avg(field: string): Promise<number | null>;
290
290
  /**
291
291
  * Minimum value of a field across all matching records.
292
292
  *
293
293
  * @param field - Field name
294
294
  * @returns Promise resolving to the minimum value (0 when no records match)
295
295
  */
296
- min(field: string): Promise<number>;
296
+ min(field: string): Promise<number | null>;
297
297
  /**
298
298
  * Maximum value of a field across all matching records.
299
299
  *
300
300
  * @param field - Field name
301
301
  * @returns Promise resolving to the maximum value (0 when no records match)
302
302
  */
303
- max(field: string): Promise<number>;
303
+ max(field: string): Promise<number | null>;
304
304
  /**
305
305
  * Group matching records by one or more fields and compute aggregates.
306
306
  *
@@ -155,6 +155,11 @@ type PaginationMode = "pages" | "cursor";
155
155
  * Repository list options
156
156
  */
157
157
  type RepositoryOptions = {
158
+ /**
159
+ * Explicit cache key for `listCached()`. Required to cache a call whose
160
+ * options contain callback filters, which cannot be keyed safely.
161
+ */
162
+ cacheKey?: string;
158
163
  /**
159
164
  * Pagination mode - "pages" for traditional pagination, "cursor" for cursor-based
160
165
  * @default "pages"
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../core/src/repositories/contracts/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../core/src/repositories/contracts/types.ts"],"mappings":"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"}
@@ -2,5 +2,5 @@ import { RepositoryManager } from "./repository.manager.mjs";
2
2
  import { RepositoryAdapterContract } from "./contracts/repository-adapter.contract.mjs";
3
3
  import { AllRepositoryOptions, CachedRepositoryOptions, ChunkCallback, CursorPaginationOptions, CursorPaginationResult, FilterFunction, FilterOperator, FilterOptions, FilterRule, FilterRules, PaginationMode, PaginationResult, RepositoryConfigurations, RepositoryEvent, RepositoryOptions, RepositoryOptionsWithCursor, RepositoryOptionsWithPages, SaveMode, TypedAllRepositoryOptions, TypedRepositoryOptions, TypedRepositoryOptionsWithCursor, TypedRepositoryOptionsWithPages, WhereOperator } from "./contracts/types.mjs";
4
4
  import { AggregateExpressionInput, AggregateExpressions, GroupByFields, QueryBuilderContract } from "./contracts/query-builder.contract.mjs";
5
- import { CascadeAdapter } from "./adapters/cascade/cascade-adapter.mjs";
5
+ import { CascadeAdapter, UPDATE_MANY_BATCH_SIZE } from "./adapters/cascade/cascade-adapter.mjs";
6
6
  import { CascadeQueryBuilder } from "./adapters/cascade/cascade-query-builder.mjs";
@@ -1,5 +1,5 @@
1
1
  import { CascadeQueryBuilder } from "./adapters/cascade/cascade-query-builder.mjs";
2
- import { CascadeAdapter } from "./adapters/cascade/cascade-adapter.mjs";
2
+ import { CascadeAdapter, UPDATE_MANY_BATCH_SIZE } from "./adapters/cascade/cascade-adapter.mjs";
3
3
  import "./adapters/cascade/index.mjs";
4
4
  import { RepositoryManager } from "./repository.manager.mjs";
5
5
 
@@ -40,6 +40,25 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
40
40
  * @protected
41
41
  */
42
42
  protected filterBy: FilterRules;
43
+ /**
44
+ * Columns a client may sort by (`orderBy` / `sortBy` / `cursorColumn`) when the
45
+ * options come from a request. `id` and the declared `filterBy` keys are always
46
+ * allowed; anything else is ignored.
47
+ * @protected
48
+ */
49
+ protected sortable: string[];
50
+ /**
51
+ * Columns a client may request through `select`. Empty by default: a client
52
+ * `select` is ignored unless the repository whitelists the columns here.
53
+ * @protected
54
+ */
55
+ protected selectable: string[];
56
+ /**
57
+ * Hard cap on `limit` / `perPage` for every list, all and cursor query.
58
+ * @default 100
59
+ * @protected
60
+ */
61
+ protected maxPerPage: number;
43
62
  /**
44
63
  * Get adapter instance (lazy-loaded)
45
64
  * Resolution order:
@@ -150,6 +169,14 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
150
169
  protected getIsActiveFilter(): {
151
170
  [x: string]: any;
152
171
  };
172
+ /**
173
+ * Attach the active scope to the options. It travels under its own key (after
174
+ * the caller's options, so it can't be overridden) and is applied as a real
175
+ * `where` by `applyOptionsToQuery`, independent of `filterBy`.
176
+ */
177
+ protected withActive<O extends object>(options?: O): O & {
178
+ activeScope: Record<string, any>;
179
+ };
153
180
  /**
154
181
  * Find a record by ID
155
182
  * @param id - Record ID or model instance
@@ -358,6 +385,33 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
358
385
  */
359
386
  protected asTyped(opts: RepositoryOptions): TypedRepositoryOptions<F>;
360
387
  protected asTypedAll(opts: AllRepositoryOptions): TypedAllRepositoryOptions<F>;
388
+ /**
389
+ * Option keys that control the query rather than filter it.
390
+ */
391
+ private static readonly controlKeys;
392
+ /**
393
+ * Columns `orderBy` / `sortBy` / `cursorColumn` may target.
394
+ */
395
+ protected getSortableColumns(): Set<string>;
396
+ /**
397
+ * Split client-controllable control options from filters: `limit` is capped by
398
+ * `maxPerPage`, sort / cursor columns must be whitelisted (`sortable`, `filterBy`
399
+ * keys or `id`) and `select` must be within `selectable`. Anything else is
400
+ * dropped, so `list({ ...request.all() })` cannot steer the query.
401
+ */
402
+ protected sanitizeControls(options: RepositoryOptions): RepositoryOptions;
403
+ /**
404
+ * Read the active scope only when it is exactly what `getIsActiveFilter()`
405
+ * produces, so a client can't smuggle an arbitrary `where` through it.
406
+ */
407
+ protected readActiveScope(options: RepositoryOptions): Record<string, any> | undefined;
408
+ /**
409
+ * Apply plain `{ column: scalar }` lookups that are not declared in `filterBy`
410
+ * as equality wheres. Used by `first` (and so `exists`, `findOrCreate`,
411
+ * `updateOrCreate`) so an undeclared key is never silently dropped and can't
412
+ * match the wrong row.
413
+ */
414
+ protected applyLookupFilters(query: QueryBuilderContract<T>, options: RepositoryOptions): void;
361
415
  /**
362
416
  * Apply repository options to query
363
417
  * @param query - Query builder instance
@@ -583,7 +637,7 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
583
637
  * @returns Promise resolving to the filtered numeric average
584
638
  * @public
585
639
  */
586
- avg(field: string, options?: TypedRepositoryOptions<F>): Promise<number>;
640
+ avg(field: string, options?: TypedRepositoryOptions<F>): Promise<number | null>;
587
641
  /**
588
642
  * Minimum value of a field across records matching options.
589
643
  *
@@ -595,7 +649,7 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
595
649
  * @returns Promise resolving to the filtered minimum value
596
650
  * @public
597
651
  */
598
- min(field: string, options?: TypedRepositoryOptions<F>): Promise<number>;
652
+ min(field: string, options?: TypedRepositoryOptions<F>): Promise<number | null>;
599
653
  /**
600
654
  * Maximum value of a field across records matching options.
601
655
  *
@@ -607,7 +661,7 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
607
661
  * @returns Promise resolving to the filtered maximum value
608
662
  * @public
609
663
  */
610
- max(field: string, options?: TypedRepositoryOptions<F>): Promise<number>;
664
+ max(field: string, options?: TypedRepositoryOptions<F>): Promise<number | null>;
611
665
  /**
612
666
  * Group records by one or more fields and compute aggregates.
613
667
  *
@@ -674,6 +728,10 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
674
728
  * @protected
675
729
  */
676
730
  protected cacheKey(key: string | Record<string, any>, moreOptions?: Record<string, any>): string;
731
+ /**
732
+ * Whether a value holds a function at any depth (cycle-safe).
733
+ */
734
+ protected containsFunction(value: any, seen?: WeakSet<object>): boolean;
677
735
  /**
678
736
  * Deterministically serialize a value for use in a cache key.
679
737
  *
@@ -768,6 +826,10 @@ declare class RepositoryManager<T = unknown, F = Record<string, any>> {
768
826
  * export const cleanup = usersRepository.cleanup.bind(usersRepository);
769
827
  */
770
828
  $cleanup(): Promise<void>;
829
+ /**
830
+ * Cache read that treats a driver failure (e.g. Redis down) as a miss.
831
+ */
832
+ private safeCacheGet;
771
833
  /**
772
834
  * Clear all cache for this repository
773
835
  * @public