@effect-app/infra 4.0.0-beta.26 → 4.0.0-beta.260

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 (503) hide show
  1. package/CHANGELOG.md +1966 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +30 -11
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +35 -14
  6. package/dist/ClusterCosmos.d.ts +64 -0
  7. package/dist/ClusterCosmos.d.ts.map +1 -0
  8. package/dist/ClusterCosmos.js +487 -0
  9. package/dist/ClusterServiceBus.d.ts +67 -0
  10. package/dist/ClusterServiceBus.d.ts.map +1 -0
  11. package/dist/ClusterServiceBus.js +82 -0
  12. package/dist/ContextProvider.d.ts +34 -0
  13. package/dist/ContextProvider.d.ts.map +1 -0
  14. package/dist/ContextProvider.js +40 -0
  15. package/dist/Emailer/Sendgrid.d.ts +111 -147
  16. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  17. package/dist/Emailer/Sendgrid.js +24 -19
  18. package/dist/Emailer/fake.d.ts +2 -2
  19. package/dist/Emailer/fake.d.ts.map +1 -1
  20. package/dist/Emailer/fake.js +4 -4
  21. package/dist/MainFiberSet.d.ts +12 -9
  22. package/dist/MainFiberSet.d.ts.map +1 -1
  23. package/dist/MainFiberSet.js +10 -6
  24. package/dist/QueueMaker/SQLQueue.d.ts +8 -9
  25. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  26. package/dist/QueueMaker/SQLQueue.js +138 -120
  27. package/dist/QueueMaker/errors.d.ts +5 -3
  28. package/dist/QueueMaker/errors.d.ts.map +1 -1
  29. package/dist/QueueMaker/errors.js +4 -2
  30. package/dist/QueueMaker/memQueue.d.ts +10 -6
  31. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  32. package/dist/QueueMaker/memQueue.js +84 -68
  33. package/dist/QueueMaker/sbqueue.d.ts +9 -5
  34. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  35. package/dist/QueueMaker/sbqueue.js +60 -58
  36. package/dist/RequestFiberSet.d.ts +10 -7
  37. package/dist/RequestFiberSet.d.ts.map +1 -1
  38. package/dist/RequestFiberSet.js +13 -8
  39. package/dist/SQL/Model.d.ts +468 -0
  40. package/dist/SQL/Model.d.ts.map +1 -0
  41. package/dist/SQL/Model.js +469 -0
  42. package/dist/SQL.d.ts +2 -0
  43. package/dist/SQL.d.ts.map +1 -0
  44. package/dist/{adapters/SQL.js → SQL.js} +1 -1
  45. package/dist/ServiceBus.d.ts +61 -0
  46. package/dist/ServiceBus.d.ts.map +1 -0
  47. package/dist/ServiceBus.js +108 -0
  48. package/dist/Store/Cosmos/query.d.ts +15 -4
  49. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  50. package/dist/Store/Cosmos/query.js +179 -41
  51. package/dist/Store/Cosmos.d.ts +3 -3
  52. package/dist/Store/Cosmos.d.ts.map +1 -1
  53. package/dist/Store/Cosmos.js +344 -246
  54. package/dist/Store/Disk.d.ts +5 -5
  55. package/dist/Store/Disk.d.ts.map +1 -1
  56. package/dist/Store/Disk.js +78 -38
  57. package/dist/Store/Memory.d.ts +7 -10
  58. package/dist/Store/Memory.d.ts.map +1 -1
  59. package/dist/Store/Memory.js +326 -66
  60. package/dist/Store/SQL/Pg.d.ts +4 -0
  61. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  62. package/dist/Store/SQL/Pg.js +232 -0
  63. package/dist/Store/SQL/query.d.ts +49 -0
  64. package/dist/Store/SQL/query.d.ts.map +1 -0
  65. package/dist/Store/SQL/query.js +527 -0
  66. package/dist/Store/SQL.d.ts +21 -0
  67. package/dist/Store/SQL.d.ts.map +1 -0
  68. package/dist/Store/SQL.js +449 -0
  69. package/dist/Store/codeFilter.d.ts +5 -5
  70. package/dist/Store/codeFilter.d.ts.map +1 -1
  71. package/dist/Store/codeFilter.js +6 -3
  72. package/dist/Store/index.d.ts +7 -5
  73. package/dist/Store/index.d.ts.map +1 -1
  74. package/dist/Store/index.js +18 -5
  75. package/dist/Store/utils.d.ts +4 -3
  76. package/dist/Store/utils.d.ts.map +1 -1
  77. package/dist/Store/utils.js +5 -5
  78. package/dist/WorkflowEngineCosmos.d.ts +29 -0
  79. package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
  80. package/dist/WorkflowEngineCosmos.js +521 -0
  81. package/dist/WorkflowEngineSqlite.d.ts +24 -0
  82. package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
  83. package/dist/WorkflowEngineSqlite.js +550 -0
  84. package/dist/arbs.d.ts +2 -2
  85. package/dist/arbs.d.ts.map +1 -1
  86. package/dist/arbs.js +5 -3
  87. package/dist/codec.d.ts +5 -0
  88. package/dist/codec.d.ts.map +1 -0
  89. package/dist/codec.js +5 -0
  90. package/dist/cosmos-client.d.ts +16 -0
  91. package/dist/cosmos-client.d.ts.map +1 -0
  92. package/dist/cosmos-client.js +11 -0
  93. package/dist/errorReporter.d.ts +7 -5
  94. package/dist/errorReporter.d.ts.map +1 -1
  95. package/dist/errorReporter.js +23 -27
  96. package/dist/errors.d.ts +1 -1
  97. package/dist/fileUtil.d.ts +2 -2
  98. package/dist/fileUtil.d.ts.map +1 -1
  99. package/dist/fileUtil.js +2 -2
  100. package/dist/index.d.ts +3 -2
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +3 -2
  103. package/dist/internal/RequestContextMiddleware.d.ts +5 -0
  104. package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
  105. package/dist/internal/RequestContextMiddleware.js +45 -0
  106. package/dist/internal/auth.d.ts +53 -0
  107. package/dist/internal/auth.d.ts.map +1 -0
  108. package/dist/internal/auth.js +180 -0
  109. package/dist/internal/events.d.ts +11 -0
  110. package/dist/internal/events.d.ts.map +1 -0
  111. package/dist/internal/events.js +49 -0
  112. package/dist/internal/health.d.ts +3 -0
  113. package/dist/internal/health.d.ts.map +1 -0
  114. package/dist/internal/health.js +5 -0
  115. package/dist/layerUtils.d.ts +32 -0
  116. package/dist/layerUtils.d.ts.map +1 -0
  117. package/dist/layerUtils.js +17 -0
  118. package/dist/logger/jsonLogger.d.ts +2 -2
  119. package/dist/logger/jsonLogger.d.ts.map +1 -1
  120. package/dist/logger/jsonLogger.js +5 -3
  121. package/dist/logger/logFmtLogger.d.ts +2 -2
  122. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  123. package/dist/logger/logFmtLogger.js +3 -3
  124. package/dist/logger/shared.d.ts +3 -3
  125. package/dist/logger/shared.d.ts.map +1 -1
  126. package/dist/logger/shared.js +5 -5
  127. package/dist/logger.d.ts +1 -1
  128. package/dist/logger.d.ts.map +1 -1
  129. package/dist/memQueue.d.ts +15 -0
  130. package/dist/memQueue.d.ts.map +1 -0
  131. package/dist/memQueue.js +21 -0
  132. package/dist/middlewares.d.ts +10 -0
  133. package/dist/middlewares.d.ts.map +1 -0
  134. package/dist/{api/middlewares.js → middlewares.js} +1 -1
  135. package/dist/mongo-client.d.ts +11 -0
  136. package/dist/mongo-client.d.ts.map +1 -0
  137. package/dist/mongo-client.js +15 -0
  138. package/dist/otel.d.ts +75 -0
  139. package/dist/otel.d.ts.map +1 -0
  140. package/dist/otel.js +65 -0
  141. package/dist/rateLimit.d.ts +12 -4
  142. package/dist/rateLimit.d.ts.map +1 -1
  143. package/dist/rateLimit.js +7 -12
  144. package/dist/redis-client.d.ts +42 -0
  145. package/dist/redis-client.d.ts.map +1 -0
  146. package/dist/redis-client.js +98 -0
  147. package/dist/reportError.d.ts +4 -0
  148. package/dist/reportError.d.ts.map +1 -0
  149. package/dist/reportError.js +28 -0
  150. package/dist/routing/middleware/RouterMiddleware.d.ts +16 -0
  151. package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
  152. package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
  153. package/dist/routing/middleware/middleware.d.ts +48 -0
  154. package/dist/routing/middleware/middleware.d.ts.map +1 -0
  155. package/dist/routing/middleware/middleware.js +128 -0
  156. package/dist/routing/middleware.d.ts +3 -0
  157. package/dist/routing/middleware.d.ts.map +1 -0
  158. package/dist/{api/routing → routing}/middleware.js +1 -2
  159. package/dist/routing/schema/jwt.d.ts +4 -0
  160. package/dist/routing/schema/jwt.d.ts.map +1 -0
  161. package/dist/routing/schema/jwt.js +13 -0
  162. package/dist/routing/tsort.d.ts +8 -0
  163. package/dist/routing/tsort.d.ts.map +1 -0
  164. package/dist/routing/tsort.js +51 -0
  165. package/dist/routing/utils.d.ts +19 -0
  166. package/dist/routing/utils.d.ts.map +1 -0
  167. package/dist/routing/utils.js +45 -0
  168. package/dist/routing.d.ts +184 -0
  169. package/dist/routing.d.ts.map +1 -0
  170. package/dist/routing.js +236 -0
  171. package/dist/test.d.ts +3 -3
  172. package/dist/test.d.ts.map +1 -1
  173. package/dist/test.js +2 -2
  174. package/dist/util.d.ts +3 -0
  175. package/dist/util.d.ts.map +1 -0
  176. package/dist/util.js +14 -0
  177. package/dist/vitest.d.ts +1 -1
  178. package/docs/cluster-storage.md +26 -0
  179. package/docs/workflow-engine.md +262 -0
  180. package/examples/query.ts +47 -39
  181. package/package.json +31 -345
  182. package/run.sh +1 -0
  183. package/src/CUPS.ts +52 -13
  184. package/src/ClusterCosmos.ts +954 -0
  185. package/src/ClusterServiceBus.ts +242 -0
  186. package/src/{api/ContextProvider.ts → ContextProvider.ts} +19 -16
  187. package/src/Emailer/Sendgrid.ts +82 -59
  188. package/src/Emailer/fake.ts +3 -3
  189. package/src/MainFiberSet.ts +12 -10
  190. package/src/QueueMaker/SQLQueue.ts +153 -156
  191. package/src/QueueMaker/errors.ts +3 -1
  192. package/src/QueueMaker/memQueue.ts +113 -107
  193. package/src/QueueMaker/sbqueue.ts +78 -90
  194. package/src/RequestFiberSet.ts +13 -8
  195. package/src/{adapters/SQL → SQL}/Model.ts +42 -41
  196. package/src/ServiceBus.ts +219 -0
  197. package/src/Store/Cosmos/query.ts +216 -52
  198. package/src/Store/Cosmos.ts +493 -353
  199. package/src/Store/Disk.ts +109 -69
  200. package/src/Store/Memory.ts +365 -96
  201. package/src/Store/SQL/Pg.ts +363 -0
  202. package/src/Store/SQL/query.ts +603 -0
  203. package/src/Store/SQL.ts +735 -0
  204. package/src/Store/codeFilter.ts +8 -5
  205. package/src/Store/index.ts +21 -6
  206. package/src/Store/utils.ts +26 -24
  207. package/src/WorkflowEngineCosmos.ts +719 -0
  208. package/src/WorkflowEngineSqlite.ts +813 -0
  209. package/src/arbs.ts +5 -3
  210. package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
  211. package/src/errorReporter.ts +66 -76
  212. package/src/fileUtil.ts +1 -1
  213. package/src/index.ts +2 -1
  214. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  215. package/src/internal/auth.ts +272 -0
  216. package/src/{api/internal → internal}/events.ts +22 -13
  217. package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
  218. package/src/logger/jsonLogger.ts +4 -2
  219. package/src/logger/logFmtLogger.ts +2 -2
  220. package/src/logger/shared.ts +5 -4
  221. package/src/{adapters/memQueue.ts → memQueue.ts} +5 -4
  222. package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
  223. package/src/otel.ts +152 -0
  224. package/src/rateLimit.ts +34 -23
  225. package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
  226. package/src/{api/reportError.ts → reportError.ts} +3 -2
  227. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
  228. package/src/{api/routing → routing}/middleware/middleware.ts +62 -17
  229. package/src/routing/middleware.ts +4 -0
  230. package/src/{api/routing → routing}/schema/jwt.ts +2 -1
  231. package/src/{api/routing → routing}/utils.ts +2 -1
  232. package/src/routing.ts +768 -0
  233. package/src/test.ts +2 -2
  234. package/test/auth.test.ts +101 -0
  235. package/test/cluster-cosmos.test.ts +503 -0
  236. package/test/cluster-servicebus.test.ts +180 -0
  237. package/test/contextProvider.test.ts +15 -12
  238. package/test/controller.test.ts +28 -32
  239. package/test/cosmos-query.test.ts +159 -0
  240. package/test/dist/_check-agg-infer.test-d.d.ts +2 -0
  241. package/test/dist/_check-agg-infer.test-d.d.ts.map +1 -0
  242. package/test/dist/_check-agg-infer.test-d.js +19 -0
  243. package/test/dist/_check-proj-infer.test-d.d.ts +2 -0
  244. package/test/dist/_check-proj-infer.test-d.d.ts.map +1 -0
  245. package/test/dist/_check-proj-infer.test-d.js +16 -0
  246. package/test/dist/_check-tighten.test-d.d.ts +2 -0
  247. package/test/dist/_check-tighten.test-d.d.ts.map +1 -0
  248. package/test/dist/_check-tighten.test-d.js +21 -0
  249. package/test/dist/auth.test.d.ts.map +1 -0
  250. package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
  251. package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
  252. package/test/dist/contextProvider.test.d.ts.map +1 -1
  253. package/test/dist/controller.test.d.ts.map +1 -1
  254. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  255. package/test/dist/date-query.test.d.ts.map +1 -0
  256. package/test/dist/fixtures.d.ts +30 -12
  257. package/test/dist/fixtures.d.ts.map +1 -1
  258. package/test/dist/fixtures.js +17 -10
  259. package/test/dist/query.test.d.ts.map +1 -1
  260. package/test/dist/rawQuery.test.d.ts.map +1 -1
  261. package/test/dist/repository-ext.test.d.ts.map +1 -0
  262. package/test/dist/requires.test.d.ts.map +1 -1
  263. package/test/dist/router-generator.test.d.ts.map +1 -0
  264. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  265. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  266. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  267. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  268. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  269. package/test/dist/sql-store.test.d.ts.map +1 -0
  270. package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
  271. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  272. package/test/fixtures.ts +16 -9
  273. package/test/layerUtils.test.ts +2 -2
  274. package/test/query.test.ts +905 -40
  275. package/test/rawQuery.test.ts +340 -22
  276. package/test/repository-ext.test.ts +62 -0
  277. package/test/requires.test.ts +10 -5
  278. package/test/router-generator.test.ts +187 -0
  279. package/test/routing-interruptibility.test.ts +66 -0
  280. package/test/rpc-context-map-streaming.test.ts +262 -0
  281. package/test/rpc-e2e-invalidation.test.ts +256 -0
  282. package/test/rpc-multi-middleware.test.ts +85 -10
  283. package/test/rpc-stream-fullstack.test.ts +304 -0
  284. package/test/sql-store.test.ts +1711 -0
  285. package/test/validateSample.test.ts +26 -14
  286. package/test/workflow-engine-cosmos.test.ts +354 -0
  287. package/test/workflow-engine-sqlite.test.ts +299 -0
  288. package/tsconfig.examples.json +1 -1
  289. package/tsconfig.json +2 -1
  290. package/tsconfig.json.bak +2 -2
  291. package/tsconfig.src.json +35 -35
  292. package/tsconfig.test.json +2 -2
  293. package/dist/Emailer/service.d.ts +0 -55
  294. package/dist/Emailer/service.d.ts.map +0 -1
  295. package/dist/Emailer/service.js +0 -6
  296. package/dist/Emailer.d.ts +0 -2
  297. package/dist/Emailer.d.ts.map +0 -1
  298. package/dist/Emailer.js +0 -2
  299. package/dist/Model/Repository/ext.d.ts +0 -41
  300. package/dist/Model/Repository/ext.d.ts.map +0 -1
  301. package/dist/Model/Repository/ext.js +0 -65
  302. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  303. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  304. package/dist/Model/Repository/internal/internal.js +0 -316
  305. package/dist/Model/Repository/legacy.d.ts +0 -19
  306. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  307. package/dist/Model/Repository/legacy.js +0 -2
  308. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  309. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  310. package/dist/Model/Repository/makeRepo.js +0 -24
  311. package/dist/Model/Repository/service.d.ts +0 -89
  312. package/dist/Model/Repository/service.d.ts.map +0 -1
  313. package/dist/Model/Repository/service.js +0 -2
  314. package/dist/Model/Repository/validation.d.ts +0 -42
  315. package/dist/Model/Repository/validation.d.ts.map +0 -1
  316. package/dist/Model/Repository/validation.js +0 -32
  317. package/dist/Model/Repository.d.ts +0 -6
  318. package/dist/Model/Repository.d.ts.map +0 -1
  319. package/dist/Model/Repository.js +0 -6
  320. package/dist/Model/dsl.d.ts +0 -32
  321. package/dist/Model/dsl.d.ts.map +0 -1
  322. package/dist/Model/dsl.js +0 -44
  323. package/dist/Model/filter/filterApi.d.ts +0 -30
  324. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  325. package/dist/Model/filter/filterApi.js +0 -2
  326. package/dist/Model/filter/types/errors.d.ts +0 -29
  327. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  328. package/dist/Model/filter/types/errors.js +0 -2
  329. package/dist/Model/filter/types/fields.d.ts +0 -15
  330. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  331. package/dist/Model/filter/types/fields.js +0 -2
  332. package/dist/Model/filter/types/path/common.d.ts +0 -316
  333. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  334. package/dist/Model/filter/types/path/common.js +0 -2
  335. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  336. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  337. package/dist/Model/filter/types/path/eager.js +0 -31
  338. package/dist/Model/filter/types/path/index.d.ts +0 -4
  339. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  340. package/dist/Model/filter/types/path/index.js +0 -3
  341. package/dist/Model/filter/types/utils.d.ts +0 -79
  342. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  343. package/dist/Model/filter/types/utils.js +0 -2
  344. package/dist/Model/filter/types/validator.d.ts +0 -30
  345. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  346. package/dist/Model/filter/types/validator.js +0 -2
  347. package/dist/Model/filter/types.d.ts +0 -5
  348. package/dist/Model/filter/types.d.ts.map +0 -1
  349. package/dist/Model/filter/types.js +0 -7
  350. package/dist/Model/query/dsl.d.ts +0 -248
  351. package/dist/Model/query/dsl.d.ts.map +0 -1
  352. package/dist/Model/query/dsl.js +0 -104
  353. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  354. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  355. package/dist/Model/query/new-kid-interpreter.js +0 -165
  356. package/dist/Model/query.d.ts +0 -15
  357. package/dist/Model/query.d.ts.map +0 -1
  358. package/dist/Model/query.js +0 -3
  359. package/dist/Model.d.ts +0 -4
  360. package/dist/Model.d.ts.map +0 -1
  361. package/dist/Model.js +0 -4
  362. package/dist/Operations.d.ts +0 -55
  363. package/dist/Operations.d.ts.map +0 -1
  364. package/dist/Operations.js +0 -102
  365. package/dist/OperationsRepo.d.ts +0 -41
  366. package/dist/OperationsRepo.d.ts.map +0 -1
  367. package/dist/OperationsRepo.js +0 -14
  368. package/dist/QueueMaker/service.d.ts +0 -11
  369. package/dist/QueueMaker/service.d.ts.map +0 -1
  370. package/dist/QueueMaker/service.js +0 -4
  371. package/dist/RequestContext.d.ts +0 -63
  372. package/dist/RequestContext.d.ts.map +0 -1
  373. package/dist/RequestContext.js +0 -49
  374. package/dist/Store/ContextMapContainer.d.ts +0 -14
  375. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  376. package/dist/Store/ContextMapContainer.js +0 -16
  377. package/dist/Store/service.d.ts +0 -108
  378. package/dist/Store/service.d.ts.map +0 -1
  379. package/dist/Store/service.js +0 -71
  380. package/dist/Store.d.ts +0 -2
  381. package/dist/Store.d.ts.map +0 -1
  382. package/dist/Store.js +0 -2
  383. package/dist/adapters/SQL/Model.d.ts +0 -479
  384. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  385. package/dist/adapters/SQL/Model.js +0 -478
  386. package/dist/adapters/SQL.d.ts +0 -2
  387. package/dist/adapters/SQL.d.ts.map +0 -1
  388. package/dist/adapters/ServiceBus.d.ts +0 -58
  389. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  390. package/dist/adapters/ServiceBus.js +0 -99
  391. package/dist/adapters/cosmos-client.d.ts +0 -14
  392. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  393. package/dist/adapters/cosmos-client.js +0 -9
  394. package/dist/adapters/index.d.ts +0 -2
  395. package/dist/adapters/index.d.ts.map +0 -1
  396. package/dist/adapters/index.js +0 -2
  397. package/dist/adapters/logger.d.ts +0 -9
  398. package/dist/adapters/logger.d.ts.map +0 -1
  399. package/dist/adapters/logger.js +0 -3
  400. package/dist/adapters/memQueue.d.ts +0 -13
  401. package/dist/adapters/memQueue.d.ts.map +0 -1
  402. package/dist/adapters/memQueue.js +0 -20
  403. package/dist/adapters/mongo-client.d.ts +0 -10
  404. package/dist/adapters/mongo-client.d.ts.map +0 -1
  405. package/dist/adapters/mongo-client.js +0 -13
  406. package/dist/adapters/redis-client.d.ts +0 -39
  407. package/dist/adapters/redis-client.d.ts.map +0 -1
  408. package/dist/adapters/redis-client.js +0 -94
  409. package/dist/api/ContextProvider.d.ts +0 -31
  410. package/dist/api/ContextProvider.d.ts.map +0 -1
  411. package/dist/api/ContextProvider.js +0 -38
  412. package/dist/api/codec.d.ts +0 -5
  413. package/dist/api/codec.d.ts.map +0 -1
  414. package/dist/api/codec.js +0 -5
  415. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  416. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  417. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  418. package/dist/api/internal/auth.d.ts +0 -15
  419. package/dist/api/internal/auth.d.ts.map +0 -1
  420. package/dist/api/internal/auth.js +0 -47
  421. package/dist/api/internal/events.d.ts +0 -9
  422. package/dist/api/internal/events.d.ts.map +0 -1
  423. package/dist/api/internal/events.js +0 -42
  424. package/dist/api/internal/health.d.ts +0 -3
  425. package/dist/api/internal/health.d.ts.map +0 -1
  426. package/dist/api/internal/health.js +0 -5
  427. package/dist/api/layerUtils.d.ts +0 -24
  428. package/dist/api/layerUtils.d.ts.map +0 -1
  429. package/dist/api/layerUtils.js +0 -16
  430. package/dist/api/middlewares.d.ts +0 -10
  431. package/dist/api/middlewares.d.ts.map +0 -1
  432. package/dist/api/reportError.d.ts +0 -4
  433. package/dist/api/reportError.d.ts.map +0 -1
  434. package/dist/api/reportError.js +0 -27
  435. package/dist/api/routing/middleware/RouterMiddleware.d.ts +0 -15
  436. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  437. package/dist/api/routing/middleware/middleware.d.ts +0 -9
  438. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  439. package/dist/api/routing/middleware/middleware.js +0 -92
  440. package/dist/api/routing/middleware.d.ts +0 -4
  441. package/dist/api/routing/middleware.d.ts.map +0 -1
  442. package/dist/api/routing/schema/jwt.d.ts +0 -4
  443. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  444. package/dist/api/routing/schema/jwt.js +0 -12
  445. package/dist/api/routing/tsort.d.ts +0 -8
  446. package/dist/api/routing/tsort.d.ts.map +0 -1
  447. package/dist/api/routing/tsort.js +0 -51
  448. package/dist/api/routing/utils.d.ts +0 -19
  449. package/dist/api/routing/utils.d.ts.map +0 -1
  450. package/dist/api/routing/utils.js +0 -44
  451. package/dist/api/routing.d.ts +0 -138
  452. package/dist/api/routing.d.ts.map +0 -1
  453. package/dist/api/routing.js +0 -166
  454. package/dist/api/setupRequest.d.ts +0 -12
  455. package/dist/api/setupRequest.d.ts.map +0 -1
  456. package/dist/api/setupRequest.js +0 -44
  457. package/dist/api/util.d.ts +0 -3
  458. package/dist/api/util.d.ts.map +0 -1
  459. package/dist/api/util.js +0 -14
  460. package/eslint.config.mjs +0 -24
  461. package/src/Emailer/service.ts +0 -52
  462. package/src/Emailer.ts +0 -1
  463. package/src/Model/Repository/ext.ts +0 -283
  464. package/src/Model/Repository/internal/internal.ts +0 -577
  465. package/src/Model/Repository/legacy.ts +0 -27
  466. package/src/Model/Repository/makeRepo.ts +0 -139
  467. package/src/Model/Repository/service.ts +0 -627
  468. package/src/Model/Repository/validation.ts +0 -31
  469. package/src/Model/Repository.ts +0 -5
  470. package/src/Model/dsl.ts +0 -128
  471. package/src/Model/filter/filterApi.ts +0 -60
  472. package/src/Model/filter/types/errors.ts +0 -47
  473. package/src/Model/filter/types/fields.ts +0 -50
  474. package/src/Model/filter/types/path/common.ts +0 -404
  475. package/src/Model/filter/types/path/eager.ts +0 -298
  476. package/src/Model/filter/types/path/index.ts +0 -4
  477. package/src/Model/filter/types/utils.ts +0 -128
  478. package/src/Model/filter/types/validator.ts +0 -46
  479. package/src/Model/filter/types.ts +0 -6
  480. package/src/Model/query/dsl.ts +0 -2110
  481. package/src/Model/query/new-kid-interpreter.ts +0 -210
  482. package/src/Model/query.ts +0 -13
  483. package/src/Model.ts +0 -3
  484. package/src/Operations.ts +0 -235
  485. package/src/OperationsRepo.ts +0 -16
  486. package/src/QueueMaker/service.ts +0 -17
  487. package/src/RequestContext.ts +0 -63
  488. package/src/Store/ContextMapContainer.ts +0 -20
  489. package/src/Store/service.ts +0 -184
  490. package/src/Store.ts +0 -1
  491. package/src/adapters/ServiceBus.ts +0 -209
  492. package/src/adapters/index.ts +0 -0
  493. package/src/adapters/logger.ts +0 -3
  494. package/src/api/internal/auth.ts +0 -68
  495. package/src/api/routing/middleware.ts +0 -6
  496. package/src/api/routing.ts +0 -598
  497. package/src/api/setupRequest.ts +0 -84
  498. /package/src/{adapters/SQL.ts → SQL.ts} +0 -0
  499. /package/src/{api/codec.ts → codec.ts} +0 -0
  500. /package/src/{api/internal → internal}/health.ts +0 -0
  501. /package/src/{api/middlewares.ts → middlewares.ts} +0 -0
  502. /package/src/{api/routing → routing}/tsort.ts +0 -0
  503. /package/src/{api/util.ts → util.ts} +0 -0
@@ -1,25 +1,35 @@
1
1
  /* eslint-disable unused-imports/no-unused-vars */
2
2
  /* eslint-disable @typescript-eslint/no-empty-object-type */
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
- import { Effect, flow, Layer, Option, pipe, S, ServiceMap, Struct } from "effect-app"
4
+ import * as Context from "effect-app/Context"
5
+ import * as Effect from "effect-app/Effect"
6
+ import * as Layer from "effect-app/Layer"
7
+ import { and, computed, count, expr, make, one, or, order, page, project, projectComputed, type QueryEnd, type QueryProjection, type QueryWhere, relation, toFilter, where } from "effect-app/Model/query"
8
+ import { makeRepo } from "effect-app/Model/Repository"
9
+ import { RepositoryRegistryLive } from "effect-app/Model/Repository/Registry"
10
+ import * as Option from "effect-app/Option"
11
+ import * as S from "effect-app/Schema"
12
+ import { setupRequestContextFromCurrent } from "effect-app/setupRequest"
13
+ import { flow, pipe } from "effect/Function"
14
+ import * as SchemaTransformation from "effect/SchemaTransformation"
15
+ import * as Struct from "effect/Struct"
5
16
  import { inspect } from "util"
6
17
  import { expect, expectTypeOf, it } from "vitest"
7
- import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
8
- import { and, count, make, one, or, order, page, project, type QueryEnd, type QueryProjection, type QueryWhere, toFilter, where } from "../src/Model/query.js"
9
- import { makeRepo } from "../src/Model/Repository.js"
10
18
  import { memFilter, MemoryStoreLive } from "../src/Store/Memory.js"
11
19
  import { SomeService } from "./fixtures.js"
12
20
 
21
+ const TestStoreLive = Layer.merge(MemoryStoreLive, RepositoryRegistryLive)
22
+
13
23
  const str = S.Struct({ _tag: S.Literal("string"), value: S.String })
14
- const num = S.Struct({ _tag: S.Literal("number"), value: S.Number })
24
+ const num = S.Struct({ _tag: S.Literal("number"), value: S.Finite })
15
25
  const someUnion = S.Union([str, num])
16
26
 
17
27
  export class Something extends S.Class<Something>("Something")({
18
- id: S.StringId.withDefault,
28
+ id: S.StringId.withConstructorDefault,
19
29
  displayName: S.NonEmptyString255,
20
- name: S.NullOr(S.NonEmptyString255).withDefault,
21
- n: S.Date.withDefault,
22
- union: someUnion.pipe(S.withDefaultConstructor(() => ({ _tag: "string" as const, value: "hi" })))
30
+ name: S.NullOr(S.NonEmptyString255).withConstructorDefault,
31
+ n: S.Date.withConstructorDefault,
32
+ union: someUnion.pipe(S.withConstructorDefault(Effect.succeed({ _tag: "string" as const, value: "hi" })))
23
33
  }) {}
24
34
  export declare namespace Something {
25
35
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -90,8 +100,8 @@ it("works", () => {
90
100
 
91
101
  const processed = memFilter(interpreted)(items.map((_) =>
92
102
  S.encodeUnknownSync(S.Struct({
93
- ...Something.omit("displayName"),
94
- displayName: S.Literal("Verona", "Riley")
103
+ ...Struct.omit(Something.fields, ["displayName"]),
104
+ displayName: S.Literals(["Verona", "Riley"])
95
105
  }))(_)
96
106
  ))
97
107
 
@@ -99,7 +109,7 @@ it("works", () => {
99
109
  })
100
110
 
101
111
  // @effect-diagnostics-next-line missingEffectServiceDependency:off
102
- class SomethingRepo extends ServiceMap.Service<SomethingRepo>()("SomethingRepo", {
112
+ class SomethingRepo extends Context.Service<SomethingRepo>()("SomethingRepo", {
103
113
  make: Effect.gen(function*() {
104
114
  return yield* makeRepo("Something", Something, {})
105
115
  })
@@ -112,7 +122,7 @@ class SomethingRepo extends ServiceMap.Service<SomethingRepo>()("SomethingRepo",
112
122
  })
113
123
  )
114
124
  .pipe(
115
- Layer.provide(MemoryStoreLive)
125
+ Layer.provide(TestStoreLive)
116
126
  )
117
127
  }
118
128
 
@@ -162,6 +172,7 @@ it("works with repo", () =>
162
172
  .pipe(
163
173
  Effect.provide(Layer.mergeAll(SomethingRepo.Test, SomeService.Default)),
164
174
  setupRequestContextFromCurrent(),
175
+ Effect.scoped,
165
176
  Effect.runPromise
166
177
  ))
167
178
 
@@ -233,18 +244,19 @@ it("collect", () =>
233
244
  .pipe(
234
245
  Effect.provide(Layer.mergeAll(SomethingRepo.Test, SomeService.Default)),
235
246
  setupRequestContextFromCurrent(),
247
+ Effect.scoped,
236
248
  Effect.runPromise
237
249
  ))
238
250
 
239
- class Person extends S.ExtendedTaggedClass<Person, Person.Encoded>()("person", {
251
+ class Person extends S.TaggedClass<Person, Person.Encoded>()("person", {
240
252
  id: S.String,
241
253
  surname: S.String
242
254
  }) {}
243
- class Animal extends S.ExtendedTaggedClass<Animal, Animal.Encoded>()("animal", {
255
+ class Animal extends S.TaggedClass<Animal, Animal.Encoded>()("animal", {
244
256
  id: S.String,
245
257
  surname: S.String
246
258
  }) {}
247
- class Test extends S.ExtendedTaggedClass<Test, Test.Encoded>()("test", {
259
+ class Test extends S.TaggedClass<Test, Test.Encoded>()("test", {
248
260
  id: S.String
249
261
  }) {}
250
262
 
@@ -279,7 +291,7 @@ it(
279
291
  expect(result).toEqual([])
280
292
  expect(result2).toEqual([])
281
293
  })
282
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
294
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
283
295
  )
284
296
 
285
297
  it(
@@ -465,7 +477,7 @@ it(
465
477
 
466
478
  expect([]).toEqual([])
467
479
  })
468
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
480
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
469
481
  )
470
482
 
471
483
  it(
@@ -508,7 +520,7 @@ it(
508
520
 
509
521
  expect([]).toEqual([])
510
522
  })
511
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
523
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
512
524
  )
513
525
 
514
526
  it(
@@ -519,8 +531,8 @@ it(
519
531
  const schema = S.Struct({
520
532
  id: S.String,
521
533
  createdAt: S.Date.pipe(
522
- S.withDecodingDefault(() => new Date().toISOString()),
523
- S.withConstructorDefault(() => Option.some(new Date()))
534
+ S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
535
+ S.withConstructorDefault(Effect.sync(() => new Date()))
524
536
  )
525
537
  })
526
538
  const repo = yield* makeRepo(
@@ -532,8 +544,8 @@ it(
532
544
  const outputSchema = S.Struct({
533
545
  id: S.Literal("123"),
534
546
  createdAt: S.Date.pipe(
535
- S.withDecodingDefault(() => new Date().toISOString()),
536
- S.withConstructorDefault(() => Option.some(new Date()))
547
+ S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
548
+ S.withConstructorDefault(Effect.sync(() => new Date()))
537
549
  )
538
550
  })
539
551
 
@@ -541,9 +553,246 @@ it(
541
553
 
542
554
  expect(result).toEqual([])
543
555
  })
544
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
556
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
545
557
  )
546
558
 
559
+ it(
560
+ "project with encodeKeys in projection maps encoded keys",
561
+ () =>
562
+ Effect
563
+ .gen(function*() {
564
+ const schema = S.Struct({
565
+ id: S.String,
566
+ a: S.Number
567
+ })
568
+
569
+ const repo = yield* makeRepo(
570
+ "test",
571
+ schema,
572
+ {
573
+ makeInitial: Effect.sync(() => [{ id: "1", a: 1 }])
574
+ }
575
+ )
576
+
577
+ const outputSchema = S.Struct({ b: S.Number }).pipe(S.encodeKeys({ b: "a" }))
578
+
579
+ const result = yield* repo.query(project(outputSchema))
580
+
581
+ expect(result).toStrictEqual([{ b: 1 }])
582
+ })
583
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
584
+ )
585
+
586
+ it("projectComputed sets computed IR and forces project mode", () => {
587
+ const baseSchema = S.Struct({
588
+ id: S.String,
589
+ items: S.Array(S.Struct({
590
+ state: S.Struct({
591
+ _tag: S.String
592
+ })
593
+ }))
594
+ })
595
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
596
+ projectComputed(
597
+ S.Struct({
598
+ pickedCount: S.NonNegativeInt
599
+ }),
600
+ computed({
601
+ pickedCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").count(where("state._tag", "Picked"))
602
+ })
603
+ )
604
+ )
605
+ const interpreted = toFilter(query, baseSchema)
606
+ expect(interpreted.mode).toBe("project")
607
+ expect(interpreted.select).toEqual([
608
+ {
609
+ key: "pickedCount",
610
+ computed: {
611
+ _tag: "relation-count",
612
+ path: "items",
613
+ filter: [{ t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }]
614
+ }
615
+ }
616
+ ])
617
+ expect(interpreted.computed?.["pickedCount"]?._tag).toBe("relation-count")
618
+ expect(interpreted.computed?.["pickedCount"]?.path).toBe("items")
619
+ expect((interpreted.computed?.["pickedCount"] as { filter: unknown } | undefined)?.filter).toEqual([
620
+ { t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
621
+ ])
622
+ })
623
+
624
+ it("projectComputed validates extra computed keys", () => {
625
+ const baseSchema = S.Struct({
626
+ id: S.String,
627
+ items: S.Array(S.Struct({ value: S.Number }))
628
+ })
629
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
630
+ projectComputed(
631
+ S.Struct({ id: S.String }),
632
+ computed({
633
+ pickedCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").count()
634
+ })
635
+ )
636
+ )
637
+ expect(() => toFilter(query, baseSchema)).toThrowError("Computed projection keys must exist in projection schema")
638
+ })
639
+
640
+ it("projection schema with computed fields fails without computed map", () => {
641
+ const baseSchema = S.Struct({
642
+ id: S.String,
643
+ items: S.Array(S.Struct({ value: S.Number }))
644
+ })
645
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
646
+ projectComputed(S.Struct({ pickedCount: S.NonNegativeInt }), computed({}))
647
+ )
648
+ expect(() => toFilter(query, baseSchema)).toThrowError("Missing computed projections for schema keys")
649
+ })
650
+
651
+ it("projectComputed.every emits relation-every IR", () => {
652
+ const baseSchema = S.Struct({
653
+ id: S.String,
654
+ items: S.Array(S.Struct({ state: S.Struct({ _tag: S.String }) }))
655
+ })
656
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
657
+ projectComputed(
658
+ S.Struct({ allPicked: S.Boolean }),
659
+ computed({
660
+ allPicked: relation<S.Codec.Encoded<typeof baseSchema>>("items").every(where("state._tag", "Picked"))
661
+ })
662
+ )
663
+ )
664
+ const interpreted = toFilter(query, baseSchema)
665
+ expect(interpreted.computed?.["allPicked"]?._tag).toBe("relation-every")
666
+ expect(interpreted.computed?.["allPicked"]?.path).toBe("items")
667
+ expect((interpreted.computed?.["allPicked"] as { filter: unknown } | undefined)?.filter).toEqual([
668
+ { t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
669
+ ])
670
+ })
671
+
672
+ it("projectComputed.distinctCount emits relation-distinct-count IR with field", () => {
673
+ const baseSchema = S.Struct({
674
+ id: S.String,
675
+ items: S.Array(S.Struct({ rowId: S.String, state: S.Struct({ _tag: S.String }) }))
676
+ })
677
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
678
+ projectComputed(
679
+ S.Struct({ positionCount: S.NonNegativeInt }),
680
+ computed({
681
+ positionCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").distinctCount(
682
+ "rowId",
683
+ where("state._tag", "neq", "cancelled")
684
+ )
685
+ })
686
+ )
687
+ )
688
+ const interpreted = toFilter(query, baseSchema)
689
+ const ir = interpreted.computed?.["positionCount"]
690
+ expect(ir?._tag).toBe("relation-distinct-count")
691
+ expect((ir as { field: string } | undefined)?.field).toBe("rowId")
692
+ expect((ir as { filter: unknown } | undefined)?.filter).toEqual([
693
+ { t: "where", path: "items.-1.state._tag", op: "neq", value: "cancelled" }
694
+ ])
695
+ })
696
+
697
+ it("projectComputed.sum emits relation-sum IR with field", () => {
698
+ const baseSchema = S.Struct({
699
+ id: S.String,
700
+ items: S.Array(S.Struct({ weight: S.Number }))
701
+ })
702
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
703
+ projectComputed(
704
+ S.Struct({ totalWeight: S.Number }),
705
+ computed({ totalWeight: relation<S.Codec.Encoded<typeof baseSchema>>("items").sum("weight") })
706
+ )
707
+ )
708
+ const interpreted = toFilter(query, baseSchema)
709
+ const ir = interpreted.computed?.["totalWeight"]
710
+ expect(ir?._tag).toBe("relation-sum")
711
+ expect((ir as { field: string } | undefined)?.field).toBe("weight")
712
+ expect((ir as { filter: unknown } | undefined)?.filter).toEqual([])
713
+ })
714
+
715
+ it("projectComputed.collect / collectDistinct emit relation-collect IR", () => {
716
+ const baseSchema = S.Struct({
717
+ id: S.String,
718
+ items: S.Array(S.Struct({ articleId: S.String }))
719
+ })
720
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
721
+ projectComputed(
722
+ S.Struct({
723
+ all: S.Array(S.String),
724
+ distinct: S.Array(S.String)
725
+ }),
726
+ computed({
727
+ all: relation<S.Codec.Encoded<typeof baseSchema>>("items").collect("articleId"),
728
+ distinct: relation<S.Codec.Encoded<typeof baseSchema>>("items").collectDistinct("articleId")
729
+ })
730
+ )
731
+ )
732
+ const interpreted = toFilter(query, baseSchema)
733
+ const all = interpreted.computed?.["all"]
734
+ const distinct = interpreted.computed?.["distinct"]
735
+ expect(all?._tag).toBe("relation-collect")
736
+ expect((all as { distinct: boolean } | undefined)?.distinct).toBe(false)
737
+ expect(distinct?._tag).toBe("relation-collect")
738
+ expect((distinct as { distinct: boolean } | undefined)?.distinct).toBe(true)
739
+ })
740
+
741
+ it("projectComputed.sumExpr emits relation-sum-expr IR", () => {
742
+ const baseSchema = S.Struct({
743
+ id: S.String,
744
+ items: S.Array(S.Struct({
745
+ weight: S.Number,
746
+ tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
747
+ }))
748
+ })
749
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
750
+ projectComputed(
751
+ S.Struct({ total: S.Number }),
752
+ computed({
753
+ total: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExpr(
754
+ expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
755
+ )
756
+ })
757
+ )
758
+ )
759
+ const interpreted = toFilter(query, baseSchema)
760
+ const ir = interpreted.computed?.["total"]
761
+ expect(ir?._tag).toBe("relation-sum-expr")
762
+ expect((ir as { expression: unknown } | undefined)?.expression).toEqual({
763
+ _tag: "mul",
764
+ left: { _tag: "field", field: "weight" },
765
+ right: { _tag: "field", field: "tradeUnit.amount" }
766
+ })
767
+ })
768
+
769
+ it("projectComputed.sumExprBy emits relation-sum-expr-by IR", () => {
770
+ const baseSchema = S.Struct({
771
+ id: S.String,
772
+ items: S.Array(S.Struct({
773
+ weight: S.Number,
774
+ tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
775
+ }))
776
+ })
777
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
778
+ projectComputed(
779
+ S.Struct({
780
+ totals: S.Array(S.Struct({ unit: S.String, total: S.Number }))
781
+ }),
782
+ computed({
783
+ totals: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExprBy(
784
+ expr.mul(expr.field("weight"), expr.field("tradeUnit.amount")),
785
+ { unit: "tradeUnit.unit" }
786
+ )
787
+ })
788
+ )
789
+ )
790
+ const interpreted = toFilter(query, baseSchema)
791
+ const ir = interpreted.computed?.["totals"]
792
+ expect(ir?._tag).toBe("relation-sum-expr-by")
793
+ expect((ir as { unit: string } | undefined)?.unit).toBe("tradeUnit.unit")
794
+ })
795
+
547
796
  it(
548
797
  "doesn't mess when refining fields",
549
798
  () =>
@@ -551,7 +800,7 @@ it(
551
800
  .gen(function*() {
552
801
  const schema = S.Struct({
553
802
  id: S.String,
554
- literals: S.Literal("a", "b", "c")
803
+ literals: S.Literals(["a", "b", "c"])
555
804
  })
556
805
 
557
806
  type Schema = typeof schema.Type
@@ -571,7 +820,7 @@ it(
571
820
 
572
821
  expect(result).toEqual([])
573
822
  })
574
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
823
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
575
824
  )
576
825
 
577
826
  it(
@@ -581,7 +830,7 @@ it(
581
830
  .gen(function*() {
582
831
  const schema = S.Struct({
583
832
  id: S.String,
584
- literals: S.Union([S.Literal("a", "b", "c"), S.Null])
833
+ literals: S.Union([S.Literals(["a", "b", "c"]), S.Null])
585
834
  })
586
835
 
587
836
  type Schema = typeof schema.Type
@@ -615,7 +864,7 @@ it(
615
864
 
616
865
  expect(result).toEqual([])
617
866
  })
618
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
867
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
619
868
  )
620
869
 
621
870
  it(
@@ -659,7 +908,7 @@ it(
659
908
 
660
909
  expect(result).toEqual([])
661
910
  })
662
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
911
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
663
912
  )
664
913
 
665
914
  it("remove null from one constituent of a tagged union", () =>
@@ -672,7 +921,7 @@ it("remove null from one constituent of a tagged union", () =>
672
921
 
673
922
  class BB extends S.Class<BB>("BB")({
674
923
  id: S.Literal("BB"),
675
- b: S.NullOr(S.Number)
924
+ b: S.NullOr(S.Finite)
676
925
  }) {}
677
926
 
678
927
  type Union = AA | BB
@@ -708,7 +957,7 @@ it("remove null from one constituent of a tagged union", () =>
708
957
  })[]
709
958
  >()
710
959
  })
711
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
960
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
712
961
 
713
962
  it("refine 3", () =>
714
963
  Effect
@@ -746,7 +995,7 @@ it("refine 3", () =>
746
995
  const resQuer1 = yield* repo.query(where("id", "AA"))
747
996
  expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
748
997
  })
749
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
998
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
750
999
 
751
1000
  it("my test", () =>
752
1001
  Effect
@@ -764,7 +1013,7 @@ it("my test", () =>
764
1013
  )
765
1014
  expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
766
1015
  })
767
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1016
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
768
1017
 
769
1018
  it("refine inner without imposing a projection", () =>
770
1019
  Effect
@@ -808,7 +1057,7 @@ it("refine inner without imposing a projection", () =>
808
1057
  where("union._tag", "AA"),
809
1058
  // But if I wanna the whole Data as output ignoring the inner refinement
810
1059
  // I wanna be able to do so
811
- project(S.Struct(Data.pick("union")))
1060
+ project(Data.mapFields(Struct.pick(["union"])))
812
1061
  )
813
1062
 
814
1063
  expectTypeOf(query2).toEqualTypeOf<
@@ -839,7 +1088,7 @@ it("refine inner without imposing a projection", () =>
839
1088
  }[]
840
1089
  >()
841
1090
  })
842
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1091
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
843
1092
 
844
1093
  it("does not allow string queries on arrays", () =>
845
1094
  Effect
@@ -874,7 +1123,7 @@ it("does not allow string queries on arrays", () =>
874
1123
  expectTypeOf(good3).toEqualTypeOf<QueryWhere<Some, Some>>()
875
1124
  expectTypeOf(good4).toEqualTypeOf<QueryWhere<Some, Some>>()
876
1125
  })
877
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1126
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
878
1127
 
879
1128
  it("test array.length", () =>
880
1129
  Effect
@@ -915,7 +1164,7 @@ it("test array.length", () =>
915
1164
  QueryWhere<Something, Something>
916
1165
  >()
917
1166
  })
918
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1167
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
919
1168
 
920
1169
  it("distribution over union", () =>
921
1170
  Effect
@@ -939,7 +1188,7 @@ it("distribution over union", () =>
939
1188
  })[]
940
1189
  >()
941
1190
  })
942
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1191
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
943
1192
 
944
1193
  it("refine nested union", () =>
945
1194
  Effect
@@ -980,7 +1229,158 @@ it("refine nested union", () =>
980
1229
  }[]
981
1230
  >()
982
1231
  })
983
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1232
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1233
+
1234
+ it("find with transformed id", () =>
1235
+ Effect
1236
+ .gen(function*() {
1237
+ const ConfiguratorId = S.NonEmptyString255
1238
+
1239
+ class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
1240
+ configuratorId: ConfiguratorId,
1241
+ label: S.NonEmptyString50
1242
+ }) {}
1243
+
1244
+ const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
1245
+ S.decodeTo(
1246
+ S.toType(PreconfigurationId),
1247
+ SchemaTransformation.transformOrFail({
1248
+ decode: Effect.fnUntraced(function*(value) {
1249
+ const values = value.split("_")
1250
+ const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
1251
+ const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
1252
+ values.join("_")
1253
+ )
1254
+ return new PreconfigurationId({ configuratorId, label })
1255
+ }),
1256
+ encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
1257
+ })
1258
+ ),
1259
+ S.revealCodec
1260
+ )
1261
+
1262
+ const Preconfiguration = S.Struct({
1263
+ id: PreconfigurationIdFromString,
1264
+ name: S.String
1265
+ })
1266
+
1267
+ const repo = yield* makeRepo("Preconfiguration", Preconfiguration, { idKey: "id" as const })
1268
+
1269
+ const id = new PreconfigurationId({
1270
+ configuratorId: S.NonEmptyString255("myConfigurator"),
1271
+ label: S.NonEmptyString50("myLabel")
1272
+ })
1273
+ const item = { id, name: "test preconfig" }
1274
+
1275
+ yield* repo.saveAndPublish([item])
1276
+
1277
+ const found = yield* repo.find(id)
1278
+ expect(Option.isSome(found)).toBe(true)
1279
+ expect(Option.getOrThrow(found).name).toBe("test preconfig")
1280
+ expect(Option.getOrThrow(found).id).toEqual(id)
1281
+
1282
+ const notFound = yield* repo.find(
1283
+ new PreconfigurationId({
1284
+ configuratorId: S.NonEmptyString255("other"),
1285
+ label: S.NonEmptyString50("nope")
1286
+ })
1287
+ )
1288
+ expect(Option.isNone(notFound)).toBe(true)
1289
+ })
1290
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1291
+
1292
+ it("find with transformed id in tagged union", () =>
1293
+ Effect
1294
+ .gen(function*() {
1295
+ const ConfiguratorId = S.NonEmptyString255
1296
+
1297
+ class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
1298
+ configuratorId: ConfiguratorId,
1299
+ label: S.NonEmptyString50
1300
+ }) {}
1301
+
1302
+ const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
1303
+ S.decodeTo(
1304
+ S.toType(PreconfigurationId),
1305
+ SchemaTransformation.transformOrFail({
1306
+ decode: Effect.fnUntraced(function*(value) {
1307
+ const values = value.split("_")
1308
+ const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
1309
+ const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
1310
+ values.join("_")
1311
+ )
1312
+ return new PreconfigurationId({ configuratorId, label })
1313
+ }),
1314
+ encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
1315
+ })
1316
+ ),
1317
+ S.revealCodec
1318
+ )
1319
+
1320
+ class Draft extends S.TaggedClass<Draft>()("Draft", {
1321
+ id: PreconfigurationIdFromString,
1322
+ name: S.String
1323
+ }) {}
1324
+
1325
+ class Published extends S.TaggedClass<Published>()("Published", {
1326
+ id: PreconfigurationIdFromString,
1327
+ name: S.String,
1328
+ publishedAt: S.String
1329
+ }) {}
1330
+
1331
+ class Archived extends S.TaggedClass<Archived>()("Archived", {
1332
+ id: PreconfigurationIdFromString,
1333
+ name: S.String,
1334
+ archivedAt: S.String
1335
+ }) {}
1336
+
1337
+ const Preconfiguration = S.Union([Draft, Published, Archived])
1338
+
1339
+ const repo = yield* makeRepo("Preconfiguration", Preconfiguration, {})
1340
+
1341
+ const id1 = new PreconfigurationId({
1342
+ configuratorId: S.NonEmptyString255("conf1"),
1343
+ label: S.NonEmptyString50("draft1")
1344
+ })
1345
+ const id2 = new PreconfigurationId({
1346
+ configuratorId: S.NonEmptyString255("conf2"),
1347
+ label: S.NonEmptyString50("pub1")
1348
+ })
1349
+ const id3 = new PreconfigurationId({
1350
+ configuratorId: S.NonEmptyString255("conf3"),
1351
+ label: S.NonEmptyString50("arch1")
1352
+ })
1353
+
1354
+ const draft = new Draft({ id: id1, name: "my draft" })
1355
+ const published = new Published({ id: id2, name: "my published", publishedAt: "2024-01-01" })
1356
+ const archived = new Archived({ id: id3, name: "my archived", archivedAt: "2024-06-01" })
1357
+
1358
+ yield* repo.saveAndPublish([draft, published, archived])
1359
+
1360
+ // find each by their PreconfigurationId instance
1361
+ const foundDraft = yield* repo.find(id1)
1362
+ expect(Option.isSome(foundDraft)).toBe(true)
1363
+ expect(Option.getOrThrow(foundDraft)._tag).toBe("Draft")
1364
+ expect(Option.getOrThrow(foundDraft).name).toBe("my draft")
1365
+
1366
+ const foundPublished = yield* repo.find(id2)
1367
+ expect(Option.isSome(foundPublished)).toBe(true)
1368
+ expect(Option.getOrThrow(foundPublished)._tag).toBe("Published")
1369
+
1370
+ const foundArchived = yield* repo.find(id3)
1371
+ expect(Option.isSome(foundArchived)).toBe(true)
1372
+ expect(Option.getOrThrow(foundArchived)._tag).toBe("Archived")
1373
+
1374
+ // not found
1375
+ const notFound = yield* repo.find(
1376
+ new PreconfigurationId({
1377
+ configuratorId: S.NonEmptyString255("nope"),
1378
+ label: S.NonEmptyString50("nope")
1379
+ })
1380
+ )
1381
+ expect(Option.isNone(notFound)).toBe(true)
1382
+ })
1383
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
984
1384
 
985
1385
  it("refine union with nested union", () =>
986
1386
  Effect
@@ -1095,4 +1495,469 @@ it("refine union with nested union", () =>
1095
1495
  })[]
1096
1496
  >()
1097
1497
  })
1098
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1498
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1499
+
1500
+ // ---------------------------------------------------------------------------
1501
+ // memFilter: computed projection execution (in-memory) and code filter coverage
1502
+ // ---------------------------------------------------------------------------
1503
+
1504
+ const computedBaseSchema = S.Struct({
1505
+ id: S.String,
1506
+ status: S.Literals(["active", "archived"]),
1507
+ items: S.Array(S.Struct({
1508
+ id: S.String,
1509
+ tag: S.Literals(["a", "b", "c"]),
1510
+ qty: S.Finite,
1511
+ note: S.String
1512
+ }))
1513
+ })
1514
+ type ComputedBase = S.Codec.Encoded<typeof computedBaseSchema>
1515
+
1516
+ const computedRows: ComputedBase[] = [
1517
+ {
1518
+ id: "r1",
1519
+ status: "active",
1520
+ items: [
1521
+ { id: "i1", tag: "a", qty: 10, note: "alpha" },
1522
+ { id: "i2", tag: "a", qty: 20, note: "alpha" },
1523
+ { id: "i3", tag: "b", qty: 5, note: "beta" }
1524
+ ]
1525
+ },
1526
+ { id: "r2", status: "active", items: [] },
1527
+ {
1528
+ id: "r3",
1529
+ status: "archived",
1530
+ items: [
1531
+ { id: "i4", tag: "b", qty: 7, note: "gamma" },
1532
+ { id: "i5", tag: "b", qty: 7, note: "gamma" },
1533
+ { id: "i6", tag: "c", qty: 3, note: "delta" }
1534
+ ]
1535
+ }
1536
+ ]
1537
+
1538
+ it("memFilter: relation-count with filter", () => {
1539
+ const q = make<ComputedBase>().pipe(
1540
+ projectComputed(
1541
+ S.Struct({ id: S.String, aCount: S.NonNegativeInt }),
1542
+ computed({
1543
+ aCount: relation<ComputedBase>("items").count(where("tag", "a"))
1544
+ })
1545
+ )
1546
+ )
1547
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1548
+ { id: "r1", aCount: 2 },
1549
+ { id: "r2", aCount: 0 },
1550
+ { id: "r3", aCount: 0 }
1551
+ ])
1552
+ })
1553
+
1554
+ it("memFilter: relation-any / every with filter", () => {
1555
+ const q = make<ComputedBase>().pipe(
1556
+ projectComputed(
1557
+ S.Struct({
1558
+ id: S.String,
1559
+ hasA: S.Boolean,
1560
+ allB: S.Boolean
1561
+ }),
1562
+ computed({
1563
+ hasA: relation<ComputedBase>("items").any(where("tag", "a")),
1564
+ allB: relation<ComputedBase>("items").every(where("tag", "b"))
1565
+ })
1566
+ )
1567
+ )
1568
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1569
+ { id: "r1", hasA: true, allB: false },
1570
+ // empty array: any → false, every → true (JS Array.every on [] is true)
1571
+ { id: "r2", hasA: false, allB: true },
1572
+ { id: "r3", hasA: false, allB: false }
1573
+ ])
1574
+ })
1575
+
1576
+ it("memFilter: relation-distinct-count with filter", () => {
1577
+ const q = make<ComputedBase>().pipe(
1578
+ projectComputed(
1579
+ S.Struct({ id: S.String, distinctNotes: S.NonNegativeInt }),
1580
+ computed({
1581
+ distinctNotes: relation<ComputedBase>("items").distinctCount("note", where("tag", "neq", "c"))
1582
+ })
1583
+ )
1584
+ )
1585
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1586
+ { id: "r1", distinctNotes: 2 }, // alpha, beta
1587
+ { id: "r2", distinctNotes: 0 },
1588
+ { id: "r3", distinctNotes: 1 } // gamma (delta filtered out)
1589
+ ])
1590
+ })
1591
+
1592
+ it("memFilter: relation-sum with filter", () => {
1593
+ const q = make<ComputedBase>().pipe(
1594
+ projectComputed(
1595
+ S.Struct({ id: S.String, totalQty: S.Finite }),
1596
+ computed({
1597
+ totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
1598
+ })
1599
+ )
1600
+ )
1601
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1602
+ { id: "r1", totalQty: 35 },
1603
+ { id: "r2", totalQty: 0 },
1604
+ { id: "r3", totalQty: 14 }
1605
+ ])
1606
+ })
1607
+
1608
+ it("memFilter: relation-collect / collectDistinct with filter", () => {
1609
+ const q = make<ComputedBase>().pipe(
1610
+ projectComputed(
1611
+ S.Struct({
1612
+ id: S.String,
1613
+ notes: S.Array(S.String),
1614
+ distinctNotes: S.Array(S.String)
1615
+ }),
1616
+ computed({
1617
+ notes: relation<ComputedBase>("items").collect("note", where("tag", "neq", "c")),
1618
+ distinctNotes: relation<ComputedBase>("items").collectDistinct("note", where("tag", "neq", "c"))
1619
+ })
1620
+ )
1621
+ )
1622
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1623
+ { id: "r1", notes: ["alpha", "alpha", "beta"], distinctNotes: ["alpha", "beta"] },
1624
+ { id: "r2", notes: [], distinctNotes: [] },
1625
+ { id: "r3", notes: ["gamma", "gamma"], distinctNotes: ["gamma"] }
1626
+ ])
1627
+ })
1628
+
1629
+ it("memFilter: computed projection with multi-statement relation filter", () => {
1630
+ const q = make<ComputedBase>().pipe(
1631
+ projectComputed(
1632
+ S.Struct({ id: S.String, hits: S.NonNegativeInt }),
1633
+ computed({
1634
+ hits: relation<ComputedBase>("items").count(
1635
+ flow(
1636
+ where("tag", "a"),
1637
+ and("qty", "gt", 10)
1638
+ )
1639
+ )
1640
+ })
1641
+ )
1642
+ )
1643
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1644
+ { id: "r1", hits: 1 }, // only i2 (a, qty 20)
1645
+ { id: "r2", hits: 0 },
1646
+ { id: "r3", hits: 0 }
1647
+ ])
1648
+ })
1649
+
1650
+ it("memFilter: relation-sum-expr / sum-expr-by / sum-expr-normalized", () => {
1651
+ const schema = S.Struct({
1652
+ id: S.String,
1653
+ items: S.Array(S.Struct({
1654
+ weight: S.Finite,
1655
+ tradeUnit: S.Struct({ amount: S.Finite, unit: S.String })
1656
+ }))
1657
+ })
1658
+ type Row = S.Codec.Encoded<typeof schema>
1659
+ const rows: Row[] = [
1660
+ {
1661
+ id: "r1",
1662
+ items: [
1663
+ { weight: 2, tradeUnit: { amount: 5, unit: "kg" } },
1664
+ { weight: 4, tradeUnit: { amount: 1000, unit: "g" } },
1665
+ { weight: 3, tradeUnit: { amount: 1, unit: "kg" } }
1666
+ ]
1667
+ },
1668
+ { id: "r2", items: [] }
1669
+ ]
1670
+ const weighted = expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
1671
+ const q = make<Row>().pipe(
1672
+ projectComputed(
1673
+ S.Struct({
1674
+ id: S.String,
1675
+ totalRaw: S.Finite,
1676
+ totalsByUnit: S.Array(S.Struct({ unit: S.String, total: S.Finite })),
1677
+ totalKg: S.Finite
1678
+ }),
1679
+ computed({
1680
+ totalRaw: relation<Row>("items").sumExpr(weighted, where("weight", "gte", 0)),
1681
+ totalsByUnit: relation<Row>("items").sumExprBy(weighted, { unit: "tradeUnit.unit" }, where("weight", "gte", 0)),
1682
+ totalKg: relation<Row>("items").sumExprNormalized(weighted, {
1683
+ unit: "tradeUnit.unit",
1684
+ toBase: "kg",
1685
+ factors: { g: 0.001 }
1686
+ }, where("weight", "gte", 0))
1687
+ })
1688
+ )
1689
+ )
1690
+ expect(memFilter(toFilter(q, schema))(rows)).toEqual([
1691
+ {
1692
+ id: "r1",
1693
+ totalRaw: 4013,
1694
+ totalsByUnit: [{ unit: "kg", total: 13 }, { unit: "g", total: 4000 }],
1695
+ totalKg: 17
1696
+ },
1697
+ { id: "r2", totalRaw: 0, totalsByUnit: [], totalKg: 0 }
1698
+ ])
1699
+ })
1700
+
1701
+ it("memFilter: computed projection combined with root where filter", () => {
1702
+ const q = make<ComputedBase>().pipe(
1703
+ where("id", "neq", "r3"),
1704
+ projectComputed(
1705
+ S.Struct({ id: S.String, totalQty: S.Finite }),
1706
+ computed({
1707
+ totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
1708
+ })
1709
+ )
1710
+ )
1711
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1712
+ { id: "r1", totalQty: 35 },
1713
+ { id: "r2", totalQty: 0 }
1714
+ ])
1715
+ })
1716
+
1717
+ it("memFilter: computed projection with order/limit/skip applied to base rows", () => {
1718
+ const q = make<ComputedBase>().pipe(
1719
+ order("id", "DESC"),
1720
+ page({ skip: 1, take: 1 }),
1721
+ projectComputed(
1722
+ S.Struct({ id: S.String, total: S.NonNegativeInt }),
1723
+ computed({
1724
+ total: relation<ComputedBase>("items").count(where("qty", "gte", 0))
1725
+ })
1726
+ )
1727
+ )
1728
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1729
+ { id: "r2", total: 0 }
1730
+ ])
1731
+ })
1732
+
1733
+ it("memFilter: computed projection - relation missing on row returns empty value", () => {
1734
+ const partial: ComputedBase[] = [
1735
+ { id: "x1" } as ComputedBase,
1736
+ { id: "x2", status: "active", items: undefined as unknown as ComputedBase["items"] }
1737
+ ]
1738
+ const q = make<ComputedBase>().pipe(
1739
+ projectComputed(
1740
+ S.Struct({
1741
+ id: S.String,
1742
+ c: S.NonNegativeInt,
1743
+ s: S.Finite,
1744
+ any_: S.Boolean,
1745
+ every_: S.Boolean,
1746
+ coll: S.Array(S.String)
1747
+ }),
1748
+ computed({
1749
+ c: relation<ComputedBase>("items").count(where("tag", "a")),
1750
+ s: relation<ComputedBase>("items").sum("qty", where("tag", "a")),
1751
+ any_: relation<ComputedBase>("items").any(where("tag", "a")),
1752
+ every_: relation<ComputedBase>("items").every(where("tag", "a")),
1753
+ coll: relation<ComputedBase>("items").collect("note", where("tag", "a"))
1754
+ })
1755
+ )
1756
+ )
1757
+ expect(memFilter(toFilter(q, computedBaseSchema))(partial)).toEqual([
1758
+ { id: "x1", c: 0, s: 0, any_: false, every_: true, coll: [] },
1759
+ { id: "x2", c: 0, s: 0, any_: false, every_: true, coll: [] }
1760
+ ])
1761
+ })
1762
+
1763
+ it("memFilter: rejects extra computed keys not in projection schema", () => {
1764
+ const q = make<ComputedBase>().pipe(
1765
+ projectComputed(
1766
+ S.Struct({ id: S.String }),
1767
+ computed({
1768
+ bogus: relation<ComputedBase>("items").count(where("tag", "a"))
1769
+ })
1770
+ )
1771
+ )
1772
+ expect(() => toFilter(q, computedBaseSchema)).toThrowError(
1773
+ "Computed projection keys must exist in projection schema"
1774
+ )
1775
+ })
1776
+
1777
+ // ---------------------------------------------------------------------------
1778
+ // memFilter: code filter (where/and/or/scopes) execution coverage
1779
+ // ---------------------------------------------------------------------------
1780
+
1781
+ type CFRow = {
1782
+ readonly id: string
1783
+ readonly tag: "x" | "y" | "z"
1784
+ readonly qty: number
1785
+ readonly desc: string
1786
+ readonly tags: ReadonlyArray<string>
1787
+ readonly nested: { readonly kind: "k1" | "k2"; readonly v: number }
1788
+ }
1789
+
1790
+ const cfRows: CFRow[] = [
1791
+ { id: "1", tag: "x", qty: 10, desc: "Hello World", tags: ["red", "green"], nested: { kind: "k1", v: 1 } },
1792
+ { id: "2", tag: "y", qty: 20, desc: "Goodbye", tags: ["blue"], nested: { kind: "k2", v: 5 } },
1793
+ { id: "3", tag: "z", qty: 0, desc: "Hello again", tags: ["red", "blue", "green"], nested: { kind: "k1", v: 9 } },
1794
+ { id: "4", tag: "y", qty: 30, desc: "World cup", tags: [], nested: { kind: "k2", v: 0 } }
1795
+ ]
1796
+
1797
+ const runCF = (q: any) => (memFilter(toFilter(q))(cfRows) as unknown as readonly CFRow[]).map((_) => _.id)
1798
+
1799
+ it("codeFilter: where + and chain", () => {
1800
+ const q = make<CFRow>().pipe(
1801
+ where("tag", "y"),
1802
+ and("qty", "gt", 25)
1803
+ )
1804
+ expect(runCF(q)).toEqual(["4"])
1805
+ })
1806
+
1807
+ it("codeFilter: where + or chain", () => {
1808
+ const q = make<CFRow>().pipe(
1809
+ where("tag", "x"),
1810
+ or("tag", "z")
1811
+ )
1812
+ expect(runCF(q).sort()).toEqual(["1", "3"])
1813
+ })
1814
+
1815
+ it("codeFilter: nested scope precedence (a AND (b OR c))", () => {
1816
+ const q = make<CFRow>().pipe(
1817
+ where("tag", "y"),
1818
+ and(
1819
+ where("qty", "gt", 25),
1820
+ or("desc", "contains", "good")
1821
+ )
1822
+ )
1823
+ // tag=y AND (qty>25 OR desc contains "good") → row 2 (Goodbye) and row 4 (qty 30)
1824
+ expect(runCF(q).sort()).toEqual(["2", "4"])
1825
+ })
1826
+
1827
+ it("codeFilter: contains/startsWith/endsWith are case-insensitive", () => {
1828
+ expect(runCF(make<CFRow>().pipe(where("desc", "contains", "WORLD"))).sort()).toEqual(["1", "4"])
1829
+ expect(runCF(make<CFRow>().pipe(where("desc", "startsWith", "hello"))).sort()).toEqual(["1", "3"])
1830
+ expect(runCF(make<CFRow>().pipe(where("desc", "endsWith", "AGAIN")))).toEqual(["3"])
1831
+ })
1832
+
1833
+ it("codeFilter: array includes / includes-any / includes-all", () => {
1834
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes", "red"))).sort()).toEqual(["1", "3"])
1835
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes-any", ["blue", "green"]))).sort()).toEqual([
1836
+ "1",
1837
+ "2",
1838
+ "3"
1839
+ ])
1840
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes-all", ["red", "blue"])))).toEqual(["3"])
1841
+ })
1842
+
1843
+ it("codeFilter: in / notIn", () => {
1844
+ expect(runCF(make<CFRow>().pipe(where("tag", "in", ["x", "z"]))).sort()).toEqual(["1", "3"])
1845
+ expect(runCF(make<CFRow>().pipe(where("tag", "notIn", ["x", "z"]))).sort()).toEqual(["2", "4"])
1846
+ })
1847
+
1848
+ it("codeFilter: gt / gte / lt / lte / neq", () => {
1849
+ expect(runCF(make<CFRow>().pipe(where("qty", "gt", 10))).sort()).toEqual(["2", "4"])
1850
+ expect(runCF(make<CFRow>().pipe(where("qty", "gte", 10))).sort()).toEqual(["1", "2", "4"])
1851
+ expect(runCF(make<CFRow>().pipe(where("qty", "lt", 10)))).toEqual(["3"])
1852
+ expect(runCF(make<CFRow>().pipe(where("qty", "lte", 10))).sort()).toEqual(["1", "3"])
1853
+ expect(runCF(make<CFRow>().pipe(where("qty", "neq", 0))).sort()).toEqual(["1", "2", "4"])
1854
+ })
1855
+
1856
+ it("codeFilter: nested path access through dot notation", () => {
1857
+ expect(runCF(make<CFRow>().pipe(where("nested.kind", "k1"))).sort()).toEqual(["1", "3"])
1858
+ expect(
1859
+ runCF(
1860
+ make<CFRow>().pipe(
1861
+ where("nested.kind", "k2"),
1862
+ and("nested.v", "gt", 0)
1863
+ )
1864
+ )
1865
+ )
1866
+ .toEqual(["2"])
1867
+ })
1868
+
1869
+ it("codeFilter: array length predicates", () => {
1870
+ expect(runCF(make<CFRow>().pipe(where("tags.length", 0)))).toEqual(["4"])
1871
+ expect(runCF(make<CFRow>().pipe(where("tags.length", "gte", 2))).sort()).toEqual(["1", "3"])
1872
+ })
1873
+
1874
+ it("codeFilter: order + skip + limit applied after filter", () => {
1875
+ const q = make<CFRow>().pipe(
1876
+ where("tag", "neq", "z"),
1877
+ order("qty", "DESC"),
1878
+ page({ skip: 1, take: 2 })
1879
+ )
1880
+ expect(runCF(q)).toEqual(["2", "1"])
1881
+ })
1882
+
1883
+ // ─────────────────────────────────────────────────────────────────────────────
1884
+ // memFilter: aggregate (GROUP BY) support
1885
+ // ─────────────────────────────────────────────────────────────────────────────
1886
+
1887
+ it("memFilter: agg-count-when groups rows and counts conditionally", () => {
1888
+ type Row = { city: string; status: string }
1889
+ const rows: Row[] = [
1890
+ { city: "NYC", status: "active" },
1891
+ { city: "NYC", status: "inactive" },
1892
+ { city: "NYC", status: "active" },
1893
+ { city: "LA", status: "active" }
1894
+ ]
1895
+ const result = memFilter<Row>({
1896
+ t: {} as Row,
1897
+ select: [
1898
+ { key: "city", path: "city" },
1899
+ {
1900
+ key: "activeCount",
1901
+ aggregate: {
1902
+ _tag: "agg-count-when",
1903
+ filter: [{ t: "where", path: "status", op: "eq", value: "active" }]
1904
+ }
1905
+ },
1906
+ { key: "total", aggregate: { _tag: "agg-count" } }
1907
+ ] as any
1908
+ })(rows as any) as any[]
1909
+
1910
+ expect(result.length).toBe(2)
1911
+ const nyc = result.find((r: any) => r.city === "NYC")!
1912
+ expect(nyc.activeCount).toBe(2)
1913
+ expect(nyc.total).toBe(3)
1914
+ const la = result.find((r: any) => r.city === "LA")!
1915
+ expect(la.activeCount).toBe(1)
1916
+ expect(la.total).toBe(1)
1917
+ })
1918
+
1919
+ it("memFilter: agg-sum / agg-min / agg-max aggregate numerics", () => {
1920
+ type Row = { dept: string; salary: number }
1921
+ const rows: Row[] = [
1922
+ { dept: "eng", salary: 100 },
1923
+ { dept: "eng", salary: 200 },
1924
+ { dept: "hr", salary: 50 }
1925
+ ]
1926
+ const result = memFilter<Row>({
1927
+ t: {} as Row,
1928
+ select: [
1929
+ { key: "dept", path: "dept" },
1930
+ { key: "total", aggregate: { _tag: "agg-sum", field: "salary" } },
1931
+ { key: "min", aggregate: { _tag: "agg-min", field: "salary" } },
1932
+ { key: "max", aggregate: { _tag: "agg-max", field: "salary" } }
1933
+ ] as any
1934
+ })(rows as any) as any[]
1935
+
1936
+ expect(result.length).toBe(2)
1937
+ const eng = result.find((r: any) => r.dept === "eng")!
1938
+ expect(eng.total).toBe(300)
1939
+ expect(eng.min).toBe(100)
1940
+ expect(eng.max).toBe(200)
1941
+ const hr = result.find((r: any) => r.dept === "hr")!
1942
+ expect(hr.total).toBe(50)
1943
+ })
1944
+
1945
+ it("memFilter: aggregate with nested path grouping", () => {
1946
+ type Row = { address: { city: string }; value: number }
1947
+ const rows: Row[] = [
1948
+ { address: { city: "NYC" }, value: 10 },
1949
+ { address: { city: "NYC" }, value: 20 },
1950
+ { address: { city: "LA" }, value: 5 }
1951
+ ]
1952
+ const result = memFilter<Row>({
1953
+ t: {} as Row,
1954
+ select: [
1955
+ { key: "city", path: "address.city" },
1956
+ { key: "count", aggregate: { _tag: "agg-count" } }
1957
+ ] as any
1958
+ })(rows as any) as any[]
1959
+
1960
+ expect(result.length).toBe(2)
1961
+ expect(result.find((r: any) => r.city === "NYC")!.count).toBe(2)
1962
+ expect(result.find((r: any) => r.city === "LA")!.count).toBe(1)
1963
+ })