@effect-app/infra 4.0.0-beta.3 → 4.0.0-beta.300

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 (474) hide show
  1. package/CHANGELOG.md +2485 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +29 -10
  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 +547 -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 +110 -146
  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 +1 -1
  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 -8
  22. package/dist/MainFiberSet.d.ts.map +1 -1
  23. package/dist/MainFiberSet.js +23 -7
  24. package/dist/QueueMaker/errors.d.ts +4 -2
  25. package/dist/QueueMaker/errors.d.ts.map +1 -1
  26. package/dist/QueueMaker/errors.js +4 -2
  27. package/dist/{adapters/SQL → SQL}/Model.d.ts +33 -47
  28. package/dist/SQL/Model.d.ts.map +1 -0
  29. package/dist/SQL/Model.js +469 -0
  30. package/dist/SQL.d.ts.map +1 -0
  31. package/dist/{adapters/SQL.js → SQL.js} +1 -1
  32. package/dist/Store/Cosmos/query.d.ts +14 -3
  33. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  34. package/dist/Store/Cosmos/query.js +179 -41
  35. package/dist/Store/Cosmos.d.ts +2 -2
  36. package/dist/Store/Cosmos.d.ts.map +1 -1
  37. package/dist/Store/Cosmos.js +385 -254
  38. package/dist/Store/Disk.d.ts +4 -4
  39. package/dist/Store/Disk.d.ts.map +1 -1
  40. package/dist/Store/Disk.js +79 -39
  41. package/dist/Store/Memory.d.ts +5 -9
  42. package/dist/Store/Memory.d.ts.map +1 -1
  43. package/dist/Store/Memory.js +324 -67
  44. package/dist/Store/SQL/Pg.d.ts +4 -0
  45. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  46. package/dist/Store/SQL/Pg.js +232 -0
  47. package/dist/Store/SQL/query.d.ts +49 -0
  48. package/dist/Store/SQL/query.d.ts.map +1 -0
  49. package/dist/Store/SQL/query.js +527 -0
  50. package/dist/Store/SQL.d.ts +21 -0
  51. package/dist/Store/SQL.d.ts.map +1 -0
  52. package/dist/Store/SQL.js +456 -0
  53. package/dist/Store/codeFilter.d.ts +4 -4
  54. package/dist/Store/codeFilter.d.ts.map +1 -1
  55. package/dist/Store/codeFilter.js +6 -4
  56. package/dist/Store/index.d.ts +6 -4
  57. package/dist/Store/index.d.ts.map +1 -1
  58. package/dist/Store/index.js +18 -5
  59. package/dist/Store/utils.d.ts +5 -2
  60. package/dist/Store/utils.d.ts.map +1 -1
  61. package/dist/Store/utils.js +9 -5
  62. package/dist/WorkflowEngineCosmos.d.ts +29 -0
  63. package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
  64. package/dist/WorkflowEngineCosmos.js +542 -0
  65. package/dist/WorkflowEngineSqlite.d.ts +24 -0
  66. package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
  67. package/dist/WorkflowEngineSqlite.js +550 -0
  68. package/dist/arbs.d.ts +1 -1
  69. package/dist/arbs.d.ts.map +1 -1
  70. package/dist/arbs.js +5 -3
  71. package/dist/codec.d.ts.map +1 -0
  72. package/dist/codec.js +5 -0
  73. package/dist/{adapters/cosmos-client.d.ts → cosmos-client.d.ts} +4 -2
  74. package/dist/cosmos-client.d.ts.map +1 -0
  75. package/dist/cosmos-client.js +11 -0
  76. package/dist/errorReporter.d.ts +6 -4
  77. package/dist/errorReporter.d.ts.map +1 -1
  78. package/dist/errorReporter.js +23 -27
  79. package/dist/fileUtil.d.ts +1 -1
  80. package/dist/fileUtil.d.ts.map +1 -1
  81. package/dist/fileUtil.js +4 -3
  82. package/dist/index.d.ts +2 -1
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +3 -2
  85. package/dist/internal/RequestContextMiddleware.d.ts +5 -0
  86. package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
  87. package/dist/internal/RequestContextMiddleware.js +45 -0
  88. package/dist/internal/auth.d.ts +53 -0
  89. package/dist/internal/auth.d.ts.map +1 -0
  90. package/dist/internal/auth.js +180 -0
  91. package/dist/internal/events.d.ts +11 -0
  92. package/dist/internal/events.d.ts.map +1 -0
  93. package/dist/internal/events.js +49 -0
  94. package/dist/internal/health.d.ts.map +1 -0
  95. package/dist/internal/health.js +5 -0
  96. package/dist/{api/layerUtils.d.ts → layerUtils.d.ts} +14 -6
  97. package/dist/layerUtils.d.ts.map +1 -0
  98. package/dist/layerUtils.js +17 -0
  99. package/dist/logger/jsonLogger.d.ts +1 -1
  100. package/dist/logger/jsonLogger.d.ts.map +1 -1
  101. package/dist/logger/jsonLogger.js +5 -3
  102. package/dist/logger/logFmtLogger.d.ts +1 -1
  103. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  104. package/dist/logger/logFmtLogger.js +3 -3
  105. package/dist/logger/shared.d.ts +2 -2
  106. package/dist/logger/shared.d.ts.map +1 -1
  107. package/dist/logger/shared.js +5 -5
  108. package/dist/logger.d.ts +4 -4
  109. package/dist/logger.d.ts.map +1 -1
  110. package/dist/middlewares.d.ts.map +1 -0
  111. package/dist/{api/middlewares.js → middlewares.js} +1 -1
  112. package/dist/{adapters/mongo-client.d.ts → mongo-client.d.ts} +3 -2
  113. package/dist/mongo-client.d.ts.map +1 -0
  114. package/dist/mongo-client.js +15 -0
  115. package/dist/otel.d.ts +75 -0
  116. package/dist/otel.d.ts.map +1 -0
  117. package/dist/otel.js +65 -0
  118. package/dist/rateLimit.d.ts +11 -3
  119. package/dist/rateLimit.d.ts.map +1 -1
  120. package/dist/rateLimit.js +7 -12
  121. package/dist/{adapters/redis-client.d.ts → redis-client.d.ts} +6 -3
  122. package/dist/redis-client.d.ts.map +1 -0
  123. package/dist/redis-client.js +98 -0
  124. package/dist/{api/reportError.d.ts → reportError.d.ts} +1 -1
  125. package/dist/reportError.d.ts.map +1 -0
  126. package/dist/reportError.js +28 -0
  127. package/dist/{api/routing → routing}/middleware/RouterMiddleware.d.ts +4 -3
  128. package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
  129. package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
  130. package/dist/routing/middleware/middleware.d.ts +48 -0
  131. package/dist/routing/middleware/middleware.d.ts.map +1 -0
  132. package/dist/routing/middleware/middleware.js +152 -0
  133. package/dist/{api/routing → routing}/middleware.d.ts +0 -1
  134. package/dist/routing/middleware.d.ts.map +1 -0
  135. package/dist/{api/routing → routing}/middleware.js +1 -2
  136. package/dist/{api/routing → routing}/schema/jwt.d.ts +1 -1
  137. package/dist/routing/schema/jwt.d.ts.map +1 -0
  138. package/dist/routing/schema/jwt.js +13 -0
  139. package/dist/routing/tsort.d.ts.map +1 -0
  140. package/dist/routing/tsort.js +51 -0
  141. package/dist/{api/routing → routing}/utils.d.ts +3 -3
  142. package/dist/routing/utils.d.ts.map +1 -0
  143. package/dist/routing/utils.js +45 -0
  144. package/dist/routing.d.ts +182 -0
  145. package/dist/routing.d.ts.map +1 -0
  146. package/dist/routing.js +256 -0
  147. package/dist/test.d.ts +2 -2
  148. package/dist/test.d.ts.map +1 -1
  149. package/dist/test.js +2 -2
  150. package/dist/util.d.ts.map +1 -0
  151. package/dist/util.js +14 -0
  152. package/docs/cluster-storage.md +26 -0
  153. package/docs/workflow-engine.md +262 -0
  154. package/examples/query.ts +47 -39
  155. package/package.json +38 -352
  156. package/src/CUPS.ts +53 -14
  157. package/src/ClusterCosmos.ts +1118 -0
  158. package/src/ClusterServiceBus.ts +242 -0
  159. package/src/{api/ContextProvider.ts → ContextProvider.ts} +20 -17
  160. package/src/Emailer/Sendgrid.ts +83 -60
  161. package/src/Emailer/fake.ts +4 -4
  162. package/src/MainFiberSet.ts +32 -13
  163. package/src/QueueMaker/errors.ts +3 -1
  164. package/src/{adapters/SQL → SQL}/Model.ts +116 -110
  165. package/src/SQL.ts +1 -0
  166. package/src/Store/Cosmos/query.ts +218 -54
  167. package/src/Store/Cosmos.ts +529 -365
  168. package/src/Store/Disk.ts +112 -70
  169. package/src/Store/Memory.ts +366 -102
  170. package/src/Store/SQL/Pg.ts +363 -0
  171. package/src/Store/SQL/query.ts +603 -0
  172. package/src/Store/SQL.ts +746 -0
  173. package/src/Store/codeFilter.ts +8 -6
  174. package/src/Store/index.ts +25 -10
  175. package/src/Store/utils.ts +32 -25
  176. package/src/WorkflowEngineCosmos.ts +749 -0
  177. package/src/WorkflowEngineSqlite.ts +813 -0
  178. package/src/arbs.ts +5 -3
  179. package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
  180. package/src/errorReporter.ts +68 -78
  181. package/src/fileUtil.ts +3 -2
  182. package/src/index.ts +2 -1
  183. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  184. package/src/internal/auth.ts +272 -0
  185. package/src/{api/internal → internal}/events.ts +25 -15
  186. package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
  187. package/src/logger/jsonLogger.ts +5 -3
  188. package/src/logger/logFmtLogger.ts +3 -3
  189. package/src/logger/shared.ts +5 -4
  190. package/src/middlewares.ts +10 -0
  191. package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
  192. package/src/otel.ts +152 -0
  193. package/src/rateLimit.ts +34 -23
  194. package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
  195. package/src/{api/reportError.ts → reportError.ts} +3 -2
  196. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
  197. package/src/routing/middleware/middleware.ts +196 -0
  198. package/src/routing/middleware.ts +4 -0
  199. package/src/{api/routing → routing}/schema/jwt.ts +4 -4
  200. package/src/{api/routing → routing}/utils.ts +2 -1
  201. package/src/routing.ts +823 -0
  202. package/src/test.ts +4 -4
  203. package/src/vitest.ts +1 -1
  204. package/test/auth.test.ts +101 -0
  205. package/test/cluster-cosmos.test.ts +612 -0
  206. package/test/cluster-servicebus.test.ts +180 -0
  207. package/test/cluster-sqlite.test.ts +205 -0
  208. package/test/contextProvider.test.ts +15 -12
  209. package/test/controller.test.ts +30 -32
  210. package/test/cosmos-query.test.ts +228 -0
  211. package/test/dist/auth.test.d.ts.map +1 -0
  212. package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
  213. package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
  214. package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
  215. package/test/dist/contextProvider.test.d.ts.map +1 -1
  216. package/test/dist/controller.test.d.ts.map +1 -1
  217. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  218. package/test/dist/fixtures.d.ts +29 -11
  219. package/test/dist/fixtures.d.ts.map +1 -1
  220. package/test/dist/fixtures.js +17 -10
  221. package/test/dist/query.test.d.ts.map +1 -1
  222. package/test/dist/rawQuery.test.d.ts.map +1 -1
  223. package/test/dist/repository-ext.test.d.ts.map +1 -0
  224. package/test/dist/requires.test.d.ts.map +1 -1
  225. package/test/dist/router-generator.test.d.ts.map +1 -0
  226. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  227. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  228. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  229. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  230. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  231. package/test/dist/sql-store.test.d.ts.map +1 -0
  232. package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
  233. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  234. package/test/fixtures.ts +16 -9
  235. package/test/layerUtils.test.ts +2 -2
  236. package/test/query.test.ts +1007 -43
  237. package/test/rawQuery.test.ts +340 -22
  238. package/test/repository-ext.test.ts +178 -0
  239. package/test/requires.test.ts +10 -5
  240. package/test/router-generator.test.ts +187 -0
  241. package/test/routing-interruptibility.test.ts +66 -0
  242. package/test/rpc-context-map-streaming.test.ts +262 -0
  243. package/test/rpc-e2e-invalidation.test.ts +418 -0
  244. package/test/rpc-multi-middleware.test.ts +86 -11
  245. package/test/rpc-stream-fullstack.test.ts +304 -0
  246. package/test/sql-store.test.ts +1711 -0
  247. package/test/validateSample.test.ts +26 -14
  248. package/test/workflow-engine-cosmos.test.ts +345 -0
  249. package/test/workflow-engine-sqlite.test.ts +297 -0
  250. package/tsconfig.examples.json +1 -1
  251. package/tsconfig.json +3 -1
  252. package/tsconfig.src.json +35 -35
  253. package/tsconfig.test.json +2 -2
  254. package/dist/Emailer/service.d.ts +0 -55
  255. package/dist/Emailer/service.d.ts.map +0 -1
  256. package/dist/Emailer/service.js +0 -6
  257. package/dist/Emailer.d.ts +0 -2
  258. package/dist/Emailer.d.ts.map +0 -1
  259. package/dist/Emailer.js +0 -2
  260. package/dist/Model/Repository/ext.d.ts +0 -41
  261. package/dist/Model/Repository/ext.d.ts.map +0 -1
  262. package/dist/Model/Repository/ext.js +0 -65
  263. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  264. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  265. package/dist/Model/Repository/internal/internal.js +0 -316
  266. package/dist/Model/Repository/legacy.d.ts +0 -19
  267. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  268. package/dist/Model/Repository/legacy.js +0 -2
  269. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  270. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  271. package/dist/Model/Repository/makeRepo.js +0 -24
  272. package/dist/Model/Repository/service.d.ts +0 -89
  273. package/dist/Model/Repository/service.d.ts.map +0 -1
  274. package/dist/Model/Repository/service.js +0 -2
  275. package/dist/Model/Repository/validation.d.ts +0 -42
  276. package/dist/Model/Repository/validation.d.ts.map +0 -1
  277. package/dist/Model/Repository/validation.js +0 -32
  278. package/dist/Model/Repository.d.ts +0 -6
  279. package/dist/Model/Repository.d.ts.map +0 -1
  280. package/dist/Model/Repository.js +0 -6
  281. package/dist/Model/dsl.d.ts +0 -32
  282. package/dist/Model/dsl.d.ts.map +0 -1
  283. package/dist/Model/dsl.js +0 -44
  284. package/dist/Model/filter/filterApi.d.ts +0 -30
  285. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  286. package/dist/Model/filter/filterApi.js +0 -2
  287. package/dist/Model/filter/types/errors.d.ts +0 -29
  288. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  289. package/dist/Model/filter/types/errors.js +0 -2
  290. package/dist/Model/filter/types/fields.d.ts +0 -15
  291. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  292. package/dist/Model/filter/types/fields.js +0 -2
  293. package/dist/Model/filter/types/path/common.d.ts +0 -316
  294. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  295. package/dist/Model/filter/types/path/common.js +0 -2
  296. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  297. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  298. package/dist/Model/filter/types/path/eager.js +0 -31
  299. package/dist/Model/filter/types/path/index.d.ts +0 -4
  300. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  301. package/dist/Model/filter/types/path/index.js +0 -3
  302. package/dist/Model/filter/types/utils.d.ts +0 -79
  303. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  304. package/dist/Model/filter/types/utils.js +0 -2
  305. package/dist/Model/filter/types/validator.d.ts +0 -30
  306. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  307. package/dist/Model/filter/types/validator.js +0 -2
  308. package/dist/Model/filter/types.d.ts +0 -5
  309. package/dist/Model/filter/types.d.ts.map +0 -1
  310. package/dist/Model/filter/types.js +0 -7
  311. package/dist/Model/query/dsl.d.ts +0 -248
  312. package/dist/Model/query/dsl.d.ts.map +0 -1
  313. package/dist/Model/query/dsl.js +0 -104
  314. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  315. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  316. package/dist/Model/query/new-kid-interpreter.js +0 -165
  317. package/dist/Model/query.d.ts +0 -15
  318. package/dist/Model/query.d.ts.map +0 -1
  319. package/dist/Model/query.js +0 -3
  320. package/dist/Model.d.ts +0 -4
  321. package/dist/Model.d.ts.map +0 -1
  322. package/dist/Model.js +0 -4
  323. package/dist/Operations.d.ts +0 -55
  324. package/dist/Operations.d.ts.map +0 -1
  325. package/dist/Operations.js +0 -102
  326. package/dist/OperationsRepo.d.ts +0 -41
  327. package/dist/OperationsRepo.d.ts.map +0 -1
  328. package/dist/OperationsRepo.js +0 -14
  329. package/dist/QueueMaker/SQLQueue.d.ts +0 -16
  330. package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
  331. package/dist/QueueMaker/SQLQueue.js +0 -130
  332. package/dist/QueueMaker/memQueue.d.ts +0 -11
  333. package/dist/QueueMaker/memQueue.d.ts.map +0 -1
  334. package/dist/QueueMaker/memQueue.js +0 -75
  335. package/dist/QueueMaker/sbqueue.d.ts +0 -12
  336. package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
  337. package/dist/QueueMaker/sbqueue.js +0 -89
  338. package/dist/QueueMaker/service.d.ts +0 -11
  339. package/dist/QueueMaker/service.d.ts.map +0 -1
  340. package/dist/QueueMaker/service.js +0 -4
  341. package/dist/RequestContext.d.ts +0 -63
  342. package/dist/RequestContext.d.ts.map +0 -1
  343. package/dist/RequestContext.js +0 -49
  344. package/dist/RequestFiberSet.d.ts +0 -36
  345. package/dist/RequestFiberSet.d.ts.map +0 -1
  346. package/dist/RequestFiberSet.js +0 -78
  347. package/dist/Store/ContextMapContainer.d.ts +0 -14
  348. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  349. package/dist/Store/ContextMapContainer.js +0 -16
  350. package/dist/Store/service.d.ts +0 -108
  351. package/dist/Store/service.d.ts.map +0 -1
  352. package/dist/Store/service.js +0 -71
  353. package/dist/Store.d.ts +0 -2
  354. package/dist/Store.d.ts.map +0 -1
  355. package/dist/Store.js +0 -2
  356. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  357. package/dist/adapters/SQL/Model.js +0 -470
  358. package/dist/adapters/SQL.d.ts.map +0 -1
  359. package/dist/adapters/ServiceBus.d.ts +0 -58
  360. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  361. package/dist/adapters/ServiceBus.js +0 -99
  362. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  363. package/dist/adapters/cosmos-client.js +0 -9
  364. package/dist/adapters/index.d.ts +0 -2
  365. package/dist/adapters/index.d.ts.map +0 -1
  366. package/dist/adapters/index.js +0 -2
  367. package/dist/adapters/logger.d.ts +0 -9
  368. package/dist/adapters/logger.d.ts.map +0 -1
  369. package/dist/adapters/logger.js +0 -3
  370. package/dist/adapters/memQueue.d.ts +0 -13
  371. package/dist/adapters/memQueue.d.ts.map +0 -1
  372. package/dist/adapters/memQueue.js +0 -20
  373. package/dist/adapters/mongo-client.d.ts.map +0 -1
  374. package/dist/adapters/mongo-client.js +0 -13
  375. package/dist/adapters/redis-client.d.ts.map +0 -1
  376. package/dist/adapters/redis-client.js +0 -94
  377. package/dist/api/ContextProvider.d.ts +0 -31
  378. package/dist/api/ContextProvider.d.ts.map +0 -1
  379. package/dist/api/ContextProvider.js +0 -38
  380. package/dist/api/codec.d.ts.map +0 -1
  381. package/dist/api/codec.js +0 -5
  382. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  383. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  384. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  385. package/dist/api/internal/auth.d.ts +0 -15
  386. package/dist/api/internal/auth.d.ts.map +0 -1
  387. package/dist/api/internal/auth.js +0 -47
  388. package/dist/api/internal/events.d.ts +0 -9
  389. package/dist/api/internal/events.d.ts.map +0 -1
  390. package/dist/api/internal/events.js +0 -40
  391. package/dist/api/internal/health.d.ts.map +0 -1
  392. package/dist/api/internal/health.js +0 -5
  393. package/dist/api/layerUtils.d.ts.map +0 -1
  394. package/dist/api/layerUtils.js +0 -16
  395. package/dist/api/middlewares.d.ts.map +0 -1
  396. package/dist/api/reportError.d.ts.map +0 -1
  397. package/dist/api/reportError.js +0 -27
  398. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  399. package/dist/api/routing/middleware/middleware.d.ts +0 -9
  400. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  401. package/dist/api/routing/middleware/middleware.js +0 -92
  402. package/dist/api/routing/middleware.d.ts.map +0 -1
  403. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  404. package/dist/api/routing/schema/jwt.js +0 -12
  405. package/dist/api/routing/tsort.d.ts.map +0 -1
  406. package/dist/api/routing/tsort.js +0 -51
  407. package/dist/api/routing/utils.d.ts.map +0 -1
  408. package/dist/api/routing/utils.js +0 -44
  409. package/dist/api/routing.d.ts +0 -138
  410. package/dist/api/routing.d.ts.map +0 -1
  411. package/dist/api/routing.js +0 -166
  412. package/dist/api/setupRequest.d.ts +0 -12
  413. package/dist/api/setupRequest.d.ts.map +0 -1
  414. package/dist/api/setupRequest.js +0 -44
  415. package/dist/api/util.d.ts.map +0 -1
  416. package/dist/api/util.js +0 -14
  417. package/eslint.config.mjs +0 -24
  418. package/src/Emailer/service.ts +0 -52
  419. package/src/Emailer.ts +0 -1
  420. package/src/Model/Repository/ext.ts +0 -283
  421. package/src/Model/Repository/internal/internal.ts +0 -577
  422. package/src/Model/Repository/legacy.ts +0 -27
  423. package/src/Model/Repository/makeRepo.ts +0 -139
  424. package/src/Model/Repository/service.ts +0 -627
  425. package/src/Model/Repository/validation.ts +0 -31
  426. package/src/Model/Repository.ts +0 -5
  427. package/src/Model/dsl.ts +0 -128
  428. package/src/Model/filter/filterApi.ts +0 -60
  429. package/src/Model/filter/types/errors.ts +0 -47
  430. package/src/Model/filter/types/fields.ts +0 -50
  431. package/src/Model/filter/types/path/common.ts +0 -404
  432. package/src/Model/filter/types/path/eager.ts +0 -298
  433. package/src/Model/filter/types/path/index.ts +0 -4
  434. package/src/Model/filter/types/utils.ts +0 -128
  435. package/src/Model/filter/types/validator.ts +0 -46
  436. package/src/Model/filter/types.ts +0 -6
  437. package/src/Model/query/dsl.ts +0 -2110
  438. package/src/Model/query/new-kid-interpreter.ts +0 -210
  439. package/src/Model/query.ts +0 -13
  440. package/src/Model.ts +0 -3
  441. package/src/Operations.ts +0 -235
  442. package/src/OperationsRepo.ts +0 -16
  443. package/src/QueueMaker/SQLQueue.ts +0 -190
  444. package/src/QueueMaker/memQueue.ts +0 -131
  445. package/src/QueueMaker/sbqueue.ts +0 -134
  446. package/src/QueueMaker/service.ts +0 -17
  447. package/src/RequestContext.ts +0 -63
  448. package/src/RequestFiberSet.ts +0 -105
  449. package/src/Store/ContextMapContainer.ts +0 -20
  450. package/src/Store/index.test.ts.bak +0 -112
  451. package/src/Store/service.ts +0 -184
  452. package/src/Store.ts +0 -1
  453. package/src/adapters/SQL.ts +0 -1
  454. package/src/adapters/ServiceBus.ts +0 -209
  455. package/src/adapters/index.ts +0 -0
  456. package/src/adapters/logger.ts +0 -3
  457. package/src/adapters/memQueue.ts +0 -21
  458. package/src/api/internal/auth.ts +0 -68
  459. package/src/api/middlewares.ts +0 -10
  460. package/src/api/routing/middleware/middleware.ts +0 -128
  461. package/src/api/routing/middleware.ts +0 -6
  462. package/src/api/routing.ts +0 -597
  463. package/src/api/setupRequest.ts +0 -84
  464. package/tsconfig.json.bak +0 -12
  465. /package/dist/{adapters/SQL.d.ts → SQL.d.ts} +0 -0
  466. /package/dist/{api/codec.d.ts → codec.d.ts} +0 -0
  467. /package/dist/{api/internal → internal}/health.d.ts +0 -0
  468. /package/dist/{api/middlewares.d.ts → middlewares.d.ts} +0 -0
  469. /package/dist/{api/routing → routing}/tsort.d.ts +0 -0
  470. /package/dist/{api/util.d.ts → util.d.ts} +0 -0
  471. /package/src/{api/codec.ts → codec.ts} +0 -0
  472. /package/src/{api/internal → internal}/health.ts +0 -0
  473. /package/src/{api/routing → routing}/tsort.ts +0 -0
  474. /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
 
@@ -156,14 +166,13 @@ it("works with repo", () =>
156
166
 
157
167
  expectTypeOf(smtArr).toEqualTypeOf<readonly Something[]>()
158
168
 
159
- console.log(" $$$$$$")
160
- console.log(Struct.pick(["id", "displayName"]))
161
169
  expect(q1).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["id", "displayName"])))
162
170
  expect(q2).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["displayName"])))
163
171
  })
164
172
  .pipe(
165
173
  Effect.provide(Layer.mergeAll(SomethingRepo.Test, SomeService.Default)),
166
174
  setupRequestContextFromCurrent(),
175
+ Effect.scoped,
167
176
  Effect.runPromise
168
177
  ))
169
178
 
@@ -235,18 +244,19 @@ it("collect", () =>
235
244
  .pipe(
236
245
  Effect.provide(Layer.mergeAll(SomethingRepo.Test, SomeService.Default)),
237
246
  setupRequestContextFromCurrent(),
247
+ Effect.scoped,
238
248
  Effect.runPromise
239
249
  ))
240
250
 
241
- class Person extends S.ExtendedTaggedClass<Person, Person.Encoded>()("person", {
251
+ class Person extends S.TaggedClass<Person, Person.Encoded>()("person", {
242
252
  id: S.String,
243
253
  surname: S.String
244
254
  }) {}
245
- class Animal extends S.ExtendedTaggedClass<Animal, Animal.Encoded>()("animal", {
255
+ class Animal extends S.TaggedClass<Animal, Animal.Encoded>()("animal", {
246
256
  id: S.String,
247
257
  surname: S.String
248
258
  }) {}
249
- class Test extends S.ExtendedTaggedClass<Test, Test.Encoded>()("test", {
259
+ class Test extends S.TaggedClass<Test, Test.Encoded>()("test", {
250
260
  id: S.String
251
261
  }) {}
252
262
 
@@ -281,7 +291,7 @@ it(
281
291
  expect(result).toEqual([])
282
292
  expect(result2).toEqual([])
283
293
  })
284
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
294
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
285
295
  )
286
296
 
287
297
  it(
@@ -379,7 +389,6 @@ it(
379
389
  // eslint-disable-next-line unused-imports/no-unused-vars
380
390
  const query5 = make<Union>().pipe(
381
391
  where("id", "bla"),
382
- // @ts-expect-error cannot project over fields that are not in common between the union members (you must refine the union first)
383
392
  project(S.Struct({ id: S.String, a: S.Unknown }))
384
393
  )
385
394
  console.log(query5)
@@ -467,7 +476,7 @@ it(
467
476
 
468
477
  expect([]).toEqual([])
469
478
  })
470
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
479
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
471
480
  )
472
481
 
473
482
  it(
@@ -510,7 +519,7 @@ it(
510
519
 
511
520
  expect([]).toEqual([])
512
521
  })
513
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
522
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
514
523
  )
515
524
 
516
525
  it(
@@ -521,8 +530,8 @@ it(
521
530
  const schema = S.Struct({
522
531
  id: S.String,
523
532
  createdAt: S.Date.pipe(
524
- S.withDecodingDefault(() => new Date().toISOString()),
525
- S.withConstructorDefault(() => Option.some(new Date()))
533
+ S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
534
+ S.withConstructorDefault(Effect.sync(() => new Date()))
526
535
  )
527
536
  })
528
537
  const repo = yield* makeRepo(
@@ -534,8 +543,8 @@ it(
534
543
  const outputSchema = S.Struct({
535
544
  id: S.Literal("123"),
536
545
  createdAt: S.Date.pipe(
537
- S.withDecodingDefault(() => new Date().toISOString()),
538
- S.withConstructorDefault(() => Option.some(new Date()))
546
+ S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
547
+ S.withConstructorDefault(Effect.sync(() => new Date()))
539
548
  )
540
549
  })
541
550
 
@@ -543,9 +552,347 @@ it(
543
552
 
544
553
  expect(result).toEqual([])
545
554
  })
546
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
555
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
547
556
  )
548
557
 
558
+ it(
559
+ "project with encodeKeys in projection maps encoded keys",
560
+ () =>
561
+ Effect
562
+ .gen(function*() {
563
+ const schema = S.Struct({
564
+ id: S.String,
565
+ a: S.Number
566
+ })
567
+
568
+ const repo = yield* makeRepo(
569
+ "test",
570
+ schema,
571
+ {
572
+ makeInitial: Effect.sync(() => [{ id: "1", a: 1 }])
573
+ }
574
+ )
575
+
576
+ const outputSchema = S.Struct({ b: S.Number }).pipe(S.encodeKeys({ b: "a" }))
577
+
578
+ const result = yield* repo.query(project(outputSchema))
579
+
580
+ expect(result).toStrictEqual([{ b: 1 }])
581
+ })
582
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
583
+ )
584
+
585
+ it("projectComputed sets computed IR and forces project mode", () => {
586
+ const baseSchema = S.Struct({
587
+ id: S.String,
588
+ items: S.Array(S.Struct({
589
+ state: S.Struct({
590
+ _tag: S.String
591
+ })
592
+ }))
593
+ })
594
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
595
+ projectComputed(
596
+ S.Struct({
597
+ pickedCount: S.NonNegativeInt
598
+ }),
599
+ computed({
600
+ pickedCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").count(where("state._tag", "Picked"))
601
+ })
602
+ )
603
+ )
604
+ const interpreted = toFilter(query, baseSchema)
605
+ expect(interpreted.mode).toBe("project")
606
+ expect(interpreted.select).toEqual([
607
+ {
608
+ key: "pickedCount",
609
+ computed: {
610
+ _tag: "relation-count",
611
+ path: "items",
612
+ filter: [{ t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }]
613
+ }
614
+ }
615
+ ])
616
+ expect(interpreted.computed?.["pickedCount"]?._tag).toBe("relation-count")
617
+ expect(interpreted.computed?.["pickedCount"]?.path).toBe("items")
618
+ expect((interpreted.computed?.["pickedCount"] as { filter: unknown } | undefined)?.filter).toEqual([
619
+ { t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
620
+ ])
621
+ })
622
+
623
+ it("projectComputed validates extra computed keys", () => {
624
+ const baseSchema = S.Struct({
625
+ id: S.String,
626
+ items: S.Array(S.Struct({ value: S.Number }))
627
+ })
628
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
629
+ projectComputed(
630
+ S.Struct({ id: S.String }),
631
+ // @ts-expect-error extra computed keys are rejected statically; keep runtime guard covered
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("projectComputed constrains projection schema to encoded repo fields and computed outputs", () => {
641
+ const baseSchema = S.Struct({
642
+ id: S.String,
643
+ name: S.String,
644
+ items: S.Array(S.Struct({ articleId: S.String, weight: S.Number }))
645
+ })
646
+ type Encoded = S.Codec.Encoded<typeof baseSchema>
647
+
648
+ make<Encoded>().pipe(
649
+ projectComputed(
650
+ S.Struct({
651
+ id: S.String,
652
+ itemCount: S.Number,
653
+ hasItems: S.Boolean,
654
+ articleIds: S.Array(S.String)
655
+ }),
656
+ ({ relation }) => ({
657
+ itemCount: relation("items").count(),
658
+ hasItems: relation("items").any(),
659
+ articleIds: relation("items").collect("articleId")
660
+ })
661
+ )
662
+ )
663
+
664
+ make<Encoded>().pipe(
665
+ projectComputed(S.Struct({ missingField: S.String }), computed({}))
666
+ )
667
+
668
+ make<Encoded>().pipe(
669
+ projectComputed(
670
+ S.Struct({ name: S.Number }),
671
+ computed({})
672
+ )
673
+ )
674
+
675
+ make<Encoded>().pipe(
676
+ projectComputed(
677
+ S.Struct({ itemCount: S.String }),
678
+ computed({ itemCount: relation<Encoded>("items").count() })
679
+ )
680
+ )
681
+
682
+ make<Encoded>().pipe(
683
+ projectComputed(
684
+ S.Struct({ itemCount: S.Number }),
685
+ // @ts-expect-error extra computed keys must exist in the projection schema
686
+ computed({
687
+ itemCount: relation<Encoded>("items").count(),
688
+ extraCount: relation<Encoded>("items").count()
689
+ })
690
+ )
691
+ )
692
+ })
693
+
694
+ it("projectComputed supports union projection schemas", () => {
695
+ const baseSchema = S.Union([
696
+ S.Struct({
697
+ _tag: S.Literal("open"),
698
+ id: S.String,
699
+ items: S.Array(S.Struct({ articleId: S.String, weight: S.Number }))
700
+ }),
701
+ S.Struct({
702
+ _tag: S.Literal("closed"),
703
+ id: S.String,
704
+ closedAt: S.String
705
+ })
706
+ ])
707
+ type Encoded = S.Codec.Encoded<typeof baseSchema>
708
+ const query = make<Encoded>().pipe(
709
+ projectComputed(
710
+ S.Union([
711
+ S.Struct({
712
+ _tag: S.Literal("open"),
713
+ id: S.String,
714
+ articleCount: S.Number,
715
+ weight: S.Number,
716
+ articleIds: S.Array(S.String)
717
+ }),
718
+ S.Struct({
719
+ _tag: S.Literal("closed"),
720
+ id: S.String,
721
+ closedAt: S.String
722
+ })
723
+ ]),
724
+ computed({
725
+ articleCount: relation<Encoded>("items").count(),
726
+ weight: relation<Encoded>("items").sum("weight"),
727
+ articleIds: relation<Encoded>("items").collect("articleId")
728
+ })
729
+ )
730
+ )
731
+
732
+ const interpreted = toFilter(query, baseSchema)
733
+ expect(interpreted.computed && Object.keys(interpreted.computed).toSorted()).toEqual([
734
+ "articleCount",
735
+ "articleIds",
736
+ "weight"
737
+ ])
738
+ })
739
+
740
+ it("projection schema with computed fields fails without computed map", () => {
741
+ const baseSchema = S.Struct({
742
+ id: S.String,
743
+ items: S.Array(S.Struct({ value: S.Number }))
744
+ })
745
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
746
+ projectComputed(S.Struct({ pickedCount: S.NonNegativeInt }), computed({}))
747
+ )
748
+ expect(() => toFilter(query, baseSchema)).toThrowError("Missing computed projections for schema keys")
749
+ })
750
+
751
+ it("projectComputed.every emits relation-every IR", () => {
752
+ const baseSchema = S.Struct({
753
+ id: S.String,
754
+ items: S.Array(S.Struct({ state: S.Struct({ _tag: S.String }) }))
755
+ })
756
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
757
+ projectComputed(
758
+ S.Struct({ allPicked: S.Boolean }),
759
+ computed({
760
+ allPicked: relation<S.Codec.Encoded<typeof baseSchema>>("items").every(where("state._tag", "Picked"))
761
+ })
762
+ )
763
+ )
764
+ const interpreted = toFilter(query, baseSchema)
765
+ expect(interpreted.computed?.["allPicked"]?._tag).toBe("relation-every")
766
+ expect(interpreted.computed?.["allPicked"]?.path).toBe("items")
767
+ expect((interpreted.computed?.["allPicked"] as { filter: unknown } | undefined)?.filter).toEqual([
768
+ { t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
769
+ ])
770
+ })
771
+
772
+ it("projectComputed.distinctCount emits relation-distinct-count IR with field", () => {
773
+ const baseSchema = S.Struct({
774
+ id: S.String,
775
+ items: S.Array(S.Struct({ rowId: S.String, state: S.Struct({ _tag: S.String }) }))
776
+ })
777
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
778
+ projectComputed(
779
+ S.Struct({ positionCount: S.NonNegativeInt }),
780
+ computed({
781
+ positionCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").distinctCount(
782
+ "rowId",
783
+ where("state._tag", "neq", "cancelled")
784
+ )
785
+ })
786
+ )
787
+ )
788
+ const interpreted = toFilter(query, baseSchema)
789
+ const ir = interpreted.computed?.["positionCount"]
790
+ expect(ir?._tag).toBe("relation-distinct-count")
791
+ expect((ir as { field: string } | undefined)?.field).toBe("rowId")
792
+ expect((ir as { filter: unknown } | undefined)?.filter).toEqual([
793
+ { t: "where", path: "items.-1.state._tag", op: "neq", value: "cancelled" }
794
+ ])
795
+ })
796
+
797
+ it("projectComputed.sum emits relation-sum IR with field", () => {
798
+ const baseSchema = S.Struct({
799
+ id: S.String,
800
+ items: S.Array(S.Struct({ weight: S.Number }))
801
+ })
802
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
803
+ projectComputed(
804
+ S.Struct({ totalWeight: S.Number }),
805
+ computed({ totalWeight: relation<S.Codec.Encoded<typeof baseSchema>>("items").sum("weight") })
806
+ )
807
+ )
808
+ const interpreted = toFilter(query, baseSchema)
809
+ const ir = interpreted.computed?.["totalWeight"]
810
+ expect(ir?._tag).toBe("relation-sum")
811
+ expect((ir as { field: string } | undefined)?.field).toBe("weight")
812
+ expect((ir as { filter: unknown } | undefined)?.filter).toEqual([])
813
+ })
814
+
815
+ it("projectComputed.collect / collectDistinct emit relation-collect IR", () => {
816
+ const baseSchema = S.Struct({
817
+ id: S.String,
818
+ items: S.Array(S.Struct({ articleId: S.String }))
819
+ })
820
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
821
+ projectComputed(
822
+ S.Struct({
823
+ all: S.Array(S.String),
824
+ distinct: S.Array(S.String)
825
+ }),
826
+ computed({
827
+ all: relation<S.Codec.Encoded<typeof baseSchema>>("items").collect("articleId"),
828
+ distinct: relation<S.Codec.Encoded<typeof baseSchema>>("items").collectDistinct("articleId")
829
+ })
830
+ )
831
+ )
832
+ const interpreted = toFilter(query, baseSchema)
833
+ const all = interpreted.computed?.["all"]
834
+ const distinct = interpreted.computed?.["distinct"]
835
+ expect(all?._tag).toBe("relation-collect")
836
+ expect((all as { distinct: boolean } | undefined)?.distinct).toBe(false)
837
+ expect(distinct?._tag).toBe("relation-collect")
838
+ expect((distinct as { distinct: boolean } | undefined)?.distinct).toBe(true)
839
+ })
840
+
841
+ it("projectComputed.sumExpr emits relation-sum-expr IR", () => {
842
+ const baseSchema = S.Struct({
843
+ id: S.String,
844
+ items: S.Array(S.Struct({
845
+ weight: S.Number,
846
+ tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
847
+ }))
848
+ })
849
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
850
+ projectComputed(
851
+ S.Struct({ total: S.Number }),
852
+ computed({
853
+ total: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExpr(
854
+ expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
855
+ )
856
+ })
857
+ )
858
+ )
859
+ const interpreted = toFilter(query, baseSchema)
860
+ const ir = interpreted.computed?.["total"]
861
+ expect(ir?._tag).toBe("relation-sum-expr")
862
+ expect((ir as { expression: unknown } | undefined)?.expression).toEqual({
863
+ _tag: "mul",
864
+ left: { _tag: "field", field: "weight" },
865
+ right: { _tag: "field", field: "tradeUnit.amount" }
866
+ })
867
+ })
868
+
869
+ it("projectComputed.sumExprBy emits relation-sum-expr-by IR", () => {
870
+ const baseSchema = S.Struct({
871
+ id: S.String,
872
+ items: S.Array(S.Struct({
873
+ weight: S.Number,
874
+ tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
875
+ }))
876
+ })
877
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
878
+ projectComputed(
879
+ S.Struct({
880
+ totals: S.Array(S.Struct({ unit: S.String, total: S.Number }))
881
+ }),
882
+ computed({
883
+ totals: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExprBy(
884
+ expr.mul(expr.field("weight"), expr.field("tradeUnit.amount")),
885
+ { unit: "tradeUnit.unit" }
886
+ )
887
+ })
888
+ )
889
+ )
890
+ const interpreted = toFilter(query, baseSchema)
891
+ const ir = interpreted.computed?.["totals"]
892
+ expect(ir?._tag).toBe("relation-sum-expr-by")
893
+ expect((ir as { unit: string } | undefined)?.unit).toBe("tradeUnit.unit")
894
+ })
895
+
549
896
  it(
550
897
  "doesn't mess when refining fields",
551
898
  () =>
@@ -553,7 +900,7 @@ it(
553
900
  .gen(function*() {
554
901
  const schema = S.Struct({
555
902
  id: S.String,
556
- literals: S.Literal("a", "b", "c")
903
+ literals: S.Literals(["a", "b", "c"])
557
904
  })
558
905
 
559
906
  type Schema = typeof schema.Type
@@ -573,7 +920,7 @@ it(
573
920
 
574
921
  expect(result).toEqual([])
575
922
  })
576
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
923
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
577
924
  )
578
925
 
579
926
  it(
@@ -583,7 +930,7 @@ it(
583
930
  .gen(function*() {
584
931
  const schema = S.Struct({
585
932
  id: S.String,
586
- literals: S.Union([S.Literal("a", "b", "c"), S.Null])
933
+ literals: S.Union([S.Literals(["a", "b", "c"]), S.Null])
587
934
  })
588
935
 
589
936
  type Schema = typeof schema.Type
@@ -617,7 +964,7 @@ it(
617
964
 
618
965
  expect(result).toEqual([])
619
966
  })
620
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
967
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
621
968
  )
622
969
 
623
970
  it(
@@ -661,7 +1008,7 @@ it(
661
1008
 
662
1009
  expect(result).toEqual([])
663
1010
  })
664
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
1011
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
665
1012
  )
666
1013
 
667
1014
  it("remove null from one constituent of a tagged union", () =>
@@ -674,7 +1021,7 @@ it("remove null from one constituent of a tagged union", () =>
674
1021
 
675
1022
  class BB extends S.Class<BB>("BB")({
676
1023
  id: S.Literal("BB"),
677
- b: S.NullOr(S.Number)
1024
+ b: S.NullOr(S.Finite)
678
1025
  }) {}
679
1026
 
680
1027
  type Union = AA | BB
@@ -710,7 +1057,7 @@ it("remove null from one constituent of a tagged union", () =>
710
1057
  })[]
711
1058
  >()
712
1059
  })
713
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1060
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
714
1061
 
715
1062
  it("refine 3", () =>
716
1063
  Effect
@@ -748,7 +1095,7 @@ it("refine 3", () =>
748
1095
  const resQuer1 = yield* repo.query(where("id", "AA"))
749
1096
  expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
750
1097
  })
751
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1098
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
752
1099
 
753
1100
  it("my test", () =>
754
1101
  Effect
@@ -766,7 +1113,7 @@ it("my test", () =>
766
1113
  )
767
1114
  expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
768
1115
  })
769
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1116
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
770
1117
 
771
1118
  it("refine inner without imposing a projection", () =>
772
1119
  Effect
@@ -810,7 +1157,7 @@ it("refine inner without imposing a projection", () =>
810
1157
  where("union._tag", "AA"),
811
1158
  // But if I wanna the whole Data as output ignoring the inner refinement
812
1159
  // I wanna be able to do so
813
- project(S.Struct(Data.pick("union")))
1160
+ project(Data.mapFields(Struct.pick(["union"])))
814
1161
  )
815
1162
 
816
1163
  expectTypeOf(query2).toEqualTypeOf<
@@ -841,7 +1188,7 @@ it("refine inner without imposing a projection", () =>
841
1188
  }[]
842
1189
  >()
843
1190
  })
844
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1191
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
845
1192
 
846
1193
  it("does not allow string queries on arrays", () =>
847
1194
  Effect
@@ -876,7 +1223,7 @@ it("does not allow string queries on arrays", () =>
876
1223
  expectTypeOf(good3).toEqualTypeOf<QueryWhere<Some, Some>>()
877
1224
  expectTypeOf(good4).toEqualTypeOf<QueryWhere<Some, Some>>()
878
1225
  })
879
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1226
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
880
1227
 
881
1228
  it("test array.length", () =>
882
1229
  Effect
@@ -917,7 +1264,7 @@ it("test array.length", () =>
917
1264
  QueryWhere<Something, Something>
918
1265
  >()
919
1266
  })
920
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1267
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
921
1268
 
922
1269
  it("distribution over union", () =>
923
1270
  Effect
@@ -941,7 +1288,7 @@ it("distribution over union", () =>
941
1288
  })[]
942
1289
  >()
943
1290
  })
944
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1291
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
945
1292
 
946
1293
  it("refine nested union", () =>
947
1294
  Effect
@@ -982,7 +1329,158 @@ it("refine nested union", () =>
982
1329
  }[]
983
1330
  >()
984
1331
  })
985
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1332
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1333
+
1334
+ it("find with transformed id", () =>
1335
+ Effect
1336
+ .gen(function*() {
1337
+ const ConfiguratorId = S.NonEmptyString255
1338
+
1339
+ class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
1340
+ configuratorId: ConfiguratorId,
1341
+ label: S.NonEmptyString50
1342
+ }) {}
1343
+
1344
+ const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
1345
+ S.decodeTo(
1346
+ S.toType(PreconfigurationId),
1347
+ SchemaTransformation.transformOrFail({
1348
+ decode: Effect.fnUntraced(function*(value) {
1349
+ const values = value.split("_")
1350
+ const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
1351
+ const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
1352
+ values.join("_")
1353
+ )
1354
+ return new PreconfigurationId({ configuratorId, label })
1355
+ }),
1356
+ encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
1357
+ })
1358
+ ),
1359
+ S.revealCodec
1360
+ )
1361
+
1362
+ const Preconfiguration = S.Struct({
1363
+ id: PreconfigurationIdFromString,
1364
+ name: S.String
1365
+ })
1366
+
1367
+ const repo = yield* makeRepo("Preconfiguration", Preconfiguration, { idKey: "id" as const })
1368
+
1369
+ const id = new PreconfigurationId({
1370
+ configuratorId: S.NonEmptyString255("myConfigurator"),
1371
+ label: S.NonEmptyString50("myLabel")
1372
+ })
1373
+ const item = { id, name: "test preconfig" }
1374
+
1375
+ yield* repo.saveAndPublish([item])
1376
+
1377
+ const found = yield* repo.find(id)
1378
+ expect(Option.isSome(found)).toBe(true)
1379
+ expect(Option.getOrThrow(found).name).toBe("test preconfig")
1380
+ expect(Option.getOrThrow(found).id).toEqual(id)
1381
+
1382
+ const notFound = yield* repo.find(
1383
+ new PreconfigurationId({
1384
+ configuratorId: S.NonEmptyString255("other"),
1385
+ label: S.NonEmptyString50("nope")
1386
+ })
1387
+ )
1388
+ expect(Option.isNone(notFound)).toBe(true)
1389
+ })
1390
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1391
+
1392
+ it("find with transformed id in tagged union", () =>
1393
+ Effect
1394
+ .gen(function*() {
1395
+ const ConfiguratorId = S.NonEmptyString255
1396
+
1397
+ class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
1398
+ configuratorId: ConfiguratorId,
1399
+ label: S.NonEmptyString50
1400
+ }) {}
1401
+
1402
+ const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
1403
+ S.decodeTo(
1404
+ S.toType(PreconfigurationId),
1405
+ SchemaTransformation.transformOrFail({
1406
+ decode: Effect.fnUntraced(function*(value) {
1407
+ const values = value.split("_")
1408
+ const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
1409
+ const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
1410
+ values.join("_")
1411
+ )
1412
+ return new PreconfigurationId({ configuratorId, label })
1413
+ }),
1414
+ encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
1415
+ })
1416
+ ),
1417
+ S.revealCodec
1418
+ )
1419
+
1420
+ class Draft extends S.TaggedClass<Draft>()("Draft", {
1421
+ id: PreconfigurationIdFromString,
1422
+ name: S.String
1423
+ }) {}
1424
+
1425
+ class Published extends S.TaggedClass<Published>()("Published", {
1426
+ id: PreconfigurationIdFromString,
1427
+ name: S.String,
1428
+ publishedAt: S.String
1429
+ }) {}
1430
+
1431
+ class Archived extends S.TaggedClass<Archived>()("Archived", {
1432
+ id: PreconfigurationIdFromString,
1433
+ name: S.String,
1434
+ archivedAt: S.String
1435
+ }) {}
1436
+
1437
+ const Preconfiguration = S.Union([Draft, Published, Archived])
1438
+
1439
+ const repo = yield* makeRepo("Preconfiguration", Preconfiguration, {})
1440
+
1441
+ const id1 = new PreconfigurationId({
1442
+ configuratorId: S.NonEmptyString255("conf1"),
1443
+ label: S.NonEmptyString50("draft1")
1444
+ })
1445
+ const id2 = new PreconfigurationId({
1446
+ configuratorId: S.NonEmptyString255("conf2"),
1447
+ label: S.NonEmptyString50("pub1")
1448
+ })
1449
+ const id3 = new PreconfigurationId({
1450
+ configuratorId: S.NonEmptyString255("conf3"),
1451
+ label: S.NonEmptyString50("arch1")
1452
+ })
1453
+
1454
+ const draft = new Draft({ id: id1, name: "my draft" })
1455
+ const published = new Published({ id: id2, name: "my published", publishedAt: "2024-01-01" })
1456
+ const archived = new Archived({ id: id3, name: "my archived", archivedAt: "2024-06-01" })
1457
+
1458
+ yield* repo.saveAndPublish([draft, published, archived])
1459
+
1460
+ // find each by their PreconfigurationId instance
1461
+ const foundDraft = yield* repo.find(id1)
1462
+ expect(Option.isSome(foundDraft)).toBe(true)
1463
+ expect(Option.getOrThrow(foundDraft)._tag).toBe("Draft")
1464
+ expect(Option.getOrThrow(foundDraft).name).toBe("my draft")
1465
+
1466
+ const foundPublished = yield* repo.find(id2)
1467
+ expect(Option.isSome(foundPublished)).toBe(true)
1468
+ expect(Option.getOrThrow(foundPublished)._tag).toBe("Published")
1469
+
1470
+ const foundArchived = yield* repo.find(id3)
1471
+ expect(Option.isSome(foundArchived)).toBe(true)
1472
+ expect(Option.getOrThrow(foundArchived)._tag).toBe("Archived")
1473
+
1474
+ // not found
1475
+ const notFound = yield* repo.find(
1476
+ new PreconfigurationId({
1477
+ configuratorId: S.NonEmptyString255("nope"),
1478
+ label: S.NonEmptyString50("nope")
1479
+ })
1480
+ )
1481
+ expect(Option.isNone(notFound)).toBe(true)
1482
+ })
1483
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
986
1484
 
987
1485
  it("refine union with nested union", () =>
988
1486
  Effect
@@ -1097,4 +1595,470 @@ it("refine union with nested union", () =>
1097
1595
  })[]
1098
1596
  >()
1099
1597
  })
1100
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1598
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1599
+
1600
+ // ---------------------------------------------------------------------------
1601
+ // memFilter: computed projection execution (in-memory) and code filter coverage
1602
+ // ---------------------------------------------------------------------------
1603
+
1604
+ const computedBaseSchema = S.Struct({
1605
+ id: S.String,
1606
+ status: S.Literals(["active", "archived"]),
1607
+ items: S.Array(S.Struct({
1608
+ id: S.String,
1609
+ tag: S.Literals(["a", "b", "c"]),
1610
+ qty: S.Finite,
1611
+ note: S.String
1612
+ }))
1613
+ })
1614
+ type ComputedBase = S.Codec.Encoded<typeof computedBaseSchema>
1615
+
1616
+ const computedRows: ComputedBase[] = [
1617
+ {
1618
+ id: "r1",
1619
+ status: "active",
1620
+ items: [
1621
+ { id: "i1", tag: "a", qty: 10, note: "alpha" },
1622
+ { id: "i2", tag: "a", qty: 20, note: "alpha" },
1623
+ { id: "i3", tag: "b", qty: 5, note: "beta" }
1624
+ ]
1625
+ },
1626
+ { id: "r2", status: "active", items: [] },
1627
+ {
1628
+ id: "r3",
1629
+ status: "archived",
1630
+ items: [
1631
+ { id: "i4", tag: "b", qty: 7, note: "gamma" },
1632
+ { id: "i5", tag: "b", qty: 7, note: "gamma" },
1633
+ { id: "i6", tag: "c", qty: 3, note: "delta" }
1634
+ ]
1635
+ }
1636
+ ]
1637
+
1638
+ it("memFilter: relation-count with filter", () => {
1639
+ const q = make<ComputedBase>().pipe(
1640
+ projectComputed(
1641
+ S.Struct({ id: S.String, aCount: S.NonNegativeInt }),
1642
+ computed({
1643
+ aCount: relation<ComputedBase>("items").count(where("tag", "a"))
1644
+ })
1645
+ )
1646
+ )
1647
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1648
+ { id: "r1", aCount: 2 },
1649
+ { id: "r2", aCount: 0 },
1650
+ { id: "r3", aCount: 0 }
1651
+ ])
1652
+ })
1653
+
1654
+ it("memFilter: relation-any / every with filter", () => {
1655
+ const q = make<ComputedBase>().pipe(
1656
+ projectComputed(
1657
+ S.Struct({
1658
+ id: S.String,
1659
+ hasA: S.Boolean,
1660
+ allB: S.Boolean
1661
+ }),
1662
+ computed({
1663
+ hasA: relation<ComputedBase>("items").any(where("tag", "a")),
1664
+ allB: relation<ComputedBase>("items").every(where("tag", "b"))
1665
+ })
1666
+ )
1667
+ )
1668
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1669
+ { id: "r1", hasA: true, allB: false },
1670
+ // empty array: any → false, every → true (JS Array.every on [] is true)
1671
+ { id: "r2", hasA: false, allB: true },
1672
+ { id: "r3", hasA: false, allB: false }
1673
+ ])
1674
+ })
1675
+
1676
+ it("memFilter: relation-distinct-count with filter", () => {
1677
+ const q = make<ComputedBase>().pipe(
1678
+ projectComputed(
1679
+ S.Struct({ id: S.String, distinctNotes: S.NonNegativeInt }),
1680
+ computed({
1681
+ distinctNotes: relation<ComputedBase>("items").distinctCount("note", where("tag", "neq", "c"))
1682
+ })
1683
+ )
1684
+ )
1685
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1686
+ { id: "r1", distinctNotes: 2 }, // alpha, beta
1687
+ { id: "r2", distinctNotes: 0 },
1688
+ { id: "r3", distinctNotes: 1 } // gamma (delta filtered out)
1689
+ ])
1690
+ })
1691
+
1692
+ it("memFilter: relation-sum with filter", () => {
1693
+ const q = make<ComputedBase>().pipe(
1694
+ projectComputed(
1695
+ S.Struct({ id: S.String, totalQty: S.Finite }),
1696
+ computed({
1697
+ totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
1698
+ })
1699
+ )
1700
+ )
1701
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1702
+ { id: "r1", totalQty: 35 },
1703
+ { id: "r2", totalQty: 0 },
1704
+ { id: "r3", totalQty: 14 }
1705
+ ])
1706
+ })
1707
+
1708
+ it("memFilter: relation-collect / collectDistinct with filter", () => {
1709
+ const q = make<ComputedBase>().pipe(
1710
+ projectComputed(
1711
+ S.Struct({
1712
+ id: S.String,
1713
+ notes: S.Array(S.String),
1714
+ distinctNotes: S.Array(S.String)
1715
+ }),
1716
+ computed({
1717
+ notes: relation<ComputedBase>("items").collect("note", where("tag", "neq", "c")),
1718
+ distinctNotes: relation<ComputedBase>("items").collectDistinct("note", where("tag", "neq", "c"))
1719
+ })
1720
+ )
1721
+ )
1722
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1723
+ { id: "r1", notes: ["alpha", "alpha", "beta"], distinctNotes: ["alpha", "beta"] },
1724
+ { id: "r2", notes: [], distinctNotes: [] },
1725
+ { id: "r3", notes: ["gamma", "gamma"], distinctNotes: ["gamma"] }
1726
+ ])
1727
+ })
1728
+
1729
+ it("memFilter: computed projection with multi-statement relation filter", () => {
1730
+ const q = make<ComputedBase>().pipe(
1731
+ projectComputed(
1732
+ S.Struct({ id: S.String, hits: S.NonNegativeInt }),
1733
+ computed({
1734
+ hits: relation<ComputedBase>("items").count(
1735
+ flow(
1736
+ where("tag", "a"),
1737
+ and("qty", "gt", 10)
1738
+ )
1739
+ )
1740
+ })
1741
+ )
1742
+ )
1743
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1744
+ { id: "r1", hits: 1 }, // only i2 (a, qty 20)
1745
+ { id: "r2", hits: 0 },
1746
+ { id: "r3", hits: 0 }
1747
+ ])
1748
+ })
1749
+
1750
+ it("memFilter: relation-sum-expr / sum-expr-by / sum-expr-normalized", () => {
1751
+ const schema = S.Struct({
1752
+ id: S.String,
1753
+ items: S.Array(S.Struct({
1754
+ weight: S.Finite,
1755
+ tradeUnit: S.Struct({ amount: S.Finite, unit: S.String })
1756
+ }))
1757
+ })
1758
+ type Row = S.Codec.Encoded<typeof schema>
1759
+ const rows: Row[] = [
1760
+ {
1761
+ id: "r1",
1762
+ items: [
1763
+ { weight: 2, tradeUnit: { amount: 5, unit: "kg" } },
1764
+ { weight: 4, tradeUnit: { amount: 1000, unit: "g" } },
1765
+ { weight: 3, tradeUnit: { amount: 1, unit: "kg" } }
1766
+ ]
1767
+ },
1768
+ { id: "r2", items: [] }
1769
+ ]
1770
+ const weighted = expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
1771
+ const q = make<Row>().pipe(
1772
+ projectComputed(
1773
+ S.Struct({
1774
+ id: S.String,
1775
+ totalRaw: S.Finite,
1776
+ totalsByUnit: S.Array(S.Struct({ unit: S.String, total: S.Finite })),
1777
+ totalKg: S.Finite
1778
+ }),
1779
+ computed({
1780
+ totalRaw: relation<Row>("items").sumExpr(weighted, where("weight", "gte", 0)),
1781
+ totalsByUnit: relation<Row>("items").sumExprBy(weighted, { unit: "tradeUnit.unit" }, where("weight", "gte", 0)),
1782
+ totalKg: relation<Row>("items").sumExprNormalized(weighted, {
1783
+ unit: "tradeUnit.unit",
1784
+ toBase: "kg",
1785
+ factors: { g: 0.001 }
1786
+ }, where("weight", "gte", 0))
1787
+ })
1788
+ )
1789
+ )
1790
+ expect(memFilter(toFilter(q, schema))(rows)).toEqual([
1791
+ {
1792
+ id: "r1",
1793
+ totalRaw: 4013,
1794
+ totalsByUnit: [{ unit: "kg", total: 13 }, { unit: "g", total: 4000 }],
1795
+ totalKg: 17
1796
+ },
1797
+ { id: "r2", totalRaw: 0, totalsByUnit: [], totalKg: 0 }
1798
+ ])
1799
+ })
1800
+
1801
+ it("memFilter: computed projection combined with root where filter", () => {
1802
+ const q = make<ComputedBase>().pipe(
1803
+ where("id", "neq", "r3"),
1804
+ projectComputed(
1805
+ S.Struct({ id: S.String, totalQty: S.Finite }),
1806
+ computed({
1807
+ totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
1808
+ })
1809
+ )
1810
+ )
1811
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1812
+ { id: "r1", totalQty: 35 },
1813
+ { id: "r2", totalQty: 0 }
1814
+ ])
1815
+ })
1816
+
1817
+ it("memFilter: computed projection with order/limit/skip applied to base rows", () => {
1818
+ const q = make<ComputedBase>().pipe(
1819
+ order("id", "DESC"),
1820
+ page({ skip: 1, take: 1 }),
1821
+ projectComputed(
1822
+ S.Struct({ id: S.String, total: S.NonNegativeInt }),
1823
+ computed({
1824
+ total: relation<ComputedBase>("items").count(where("qty", "gte", 0))
1825
+ })
1826
+ )
1827
+ )
1828
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1829
+ { id: "r2", total: 0 }
1830
+ ])
1831
+ })
1832
+
1833
+ it("memFilter: computed projection - relation missing on row returns empty value", () => {
1834
+ const partial: ComputedBase[] = [
1835
+ { id: "x1" } as ComputedBase,
1836
+ { id: "x2", status: "active", items: undefined as unknown as ComputedBase["items"] }
1837
+ ]
1838
+ const q = make<ComputedBase>().pipe(
1839
+ projectComputed(
1840
+ S.Struct({
1841
+ id: S.String,
1842
+ c: S.NonNegativeInt,
1843
+ s: S.Finite,
1844
+ any_: S.Boolean,
1845
+ every_: S.Boolean,
1846
+ coll: S.Array(S.String)
1847
+ }),
1848
+ computed({
1849
+ c: relation<ComputedBase>("items").count(where("tag", "a")),
1850
+ s: relation<ComputedBase>("items").sum("qty", where("tag", "a")),
1851
+ any_: relation<ComputedBase>("items").any(where("tag", "a")),
1852
+ every_: relation<ComputedBase>("items").every(where("tag", "a")),
1853
+ coll: relation<ComputedBase>("items").collect("note", where("tag", "a"))
1854
+ })
1855
+ )
1856
+ )
1857
+ expect(memFilter(toFilter(q, computedBaseSchema))(partial)).toEqual([
1858
+ { id: "x1", c: 0, s: 0, any_: false, every_: true, coll: [] },
1859
+ { id: "x2", c: 0, s: 0, any_: false, every_: true, coll: [] }
1860
+ ])
1861
+ })
1862
+
1863
+ it("memFilter: rejects extra computed keys not in projection schema", () => {
1864
+ const q = make<ComputedBase>().pipe(
1865
+ projectComputed(
1866
+ S.Struct({ id: S.String }),
1867
+ // @ts-expect-error extra computed keys are rejected statically; keep runtime guard covered
1868
+ computed({
1869
+ bogus: relation<ComputedBase>("items").count(where("tag", "a"))
1870
+ })
1871
+ )
1872
+ )
1873
+ expect(() => toFilter(q, computedBaseSchema)).toThrowError(
1874
+ "Computed projection keys must exist in projection schema"
1875
+ )
1876
+ })
1877
+
1878
+ // ---------------------------------------------------------------------------
1879
+ // memFilter: code filter (where/and/or/scopes) execution coverage
1880
+ // ---------------------------------------------------------------------------
1881
+
1882
+ type CFRow = {
1883
+ readonly id: string
1884
+ readonly tag: "x" | "y" | "z"
1885
+ readonly qty: number
1886
+ readonly desc: string
1887
+ readonly tags: ReadonlyArray<string>
1888
+ readonly nested: { readonly kind: "k1" | "k2"; readonly v: number }
1889
+ }
1890
+
1891
+ const cfRows: CFRow[] = [
1892
+ { id: "1", tag: "x", qty: 10, desc: "Hello World", tags: ["red", "green"], nested: { kind: "k1", v: 1 } },
1893
+ { id: "2", tag: "y", qty: 20, desc: "Goodbye", tags: ["blue"], nested: { kind: "k2", v: 5 } },
1894
+ { id: "3", tag: "z", qty: 0, desc: "Hello again", tags: ["red", "blue", "green"], nested: { kind: "k1", v: 9 } },
1895
+ { id: "4", tag: "y", qty: 30, desc: "World cup", tags: [], nested: { kind: "k2", v: 0 } }
1896
+ ]
1897
+
1898
+ const runCF = (q: any) => (memFilter(toFilter(q))(cfRows) as unknown as readonly CFRow[]).map((_) => _.id)
1899
+
1900
+ it("codeFilter: where + and chain", () => {
1901
+ const q = make<CFRow>().pipe(
1902
+ where("tag", "y"),
1903
+ and("qty", "gt", 25)
1904
+ )
1905
+ expect(runCF(q)).toEqual(["4"])
1906
+ })
1907
+
1908
+ it("codeFilter: where + or chain", () => {
1909
+ const q = make<CFRow>().pipe(
1910
+ where("tag", "x"),
1911
+ or("tag", "z")
1912
+ )
1913
+ expect(runCF(q).sort()).toEqual(["1", "3"])
1914
+ })
1915
+
1916
+ it("codeFilter: nested scope precedence (a AND (b OR c))", () => {
1917
+ const q = make<CFRow>().pipe(
1918
+ where("tag", "y"),
1919
+ and(
1920
+ where("qty", "gt", 25),
1921
+ or("desc", "contains", "good")
1922
+ )
1923
+ )
1924
+ // tag=y AND (qty>25 OR desc contains "good") → row 2 (Goodbye) and row 4 (qty 30)
1925
+ expect(runCF(q).sort()).toEqual(["2", "4"])
1926
+ })
1927
+
1928
+ it("codeFilter: contains/startsWith/endsWith are case-insensitive", () => {
1929
+ expect(runCF(make<CFRow>().pipe(where("desc", "contains", "WORLD"))).sort()).toEqual(["1", "4"])
1930
+ expect(runCF(make<CFRow>().pipe(where("desc", "startsWith", "hello"))).sort()).toEqual(["1", "3"])
1931
+ expect(runCF(make<CFRow>().pipe(where("desc", "endsWith", "AGAIN")))).toEqual(["3"])
1932
+ })
1933
+
1934
+ it("codeFilter: array includes / includes-any / includes-all", () => {
1935
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes", "red"))).sort()).toEqual(["1", "3"])
1936
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes-any", ["blue", "green"]))).sort()).toEqual([
1937
+ "1",
1938
+ "2",
1939
+ "3"
1940
+ ])
1941
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes-all", ["red", "blue"])))).toEqual(["3"])
1942
+ })
1943
+
1944
+ it("codeFilter: in / notIn", () => {
1945
+ expect(runCF(make<CFRow>().pipe(where("tag", "in", ["x", "z"]))).sort()).toEqual(["1", "3"])
1946
+ expect(runCF(make<CFRow>().pipe(where("tag", "notIn", ["x", "z"]))).sort()).toEqual(["2", "4"])
1947
+ })
1948
+
1949
+ it("codeFilter: gt / gte / lt / lte / neq", () => {
1950
+ expect(runCF(make<CFRow>().pipe(where("qty", "gt", 10))).sort()).toEqual(["2", "4"])
1951
+ expect(runCF(make<CFRow>().pipe(where("qty", "gte", 10))).sort()).toEqual(["1", "2", "4"])
1952
+ expect(runCF(make<CFRow>().pipe(where("qty", "lt", 10)))).toEqual(["3"])
1953
+ expect(runCF(make<CFRow>().pipe(where("qty", "lte", 10))).sort()).toEqual(["1", "3"])
1954
+ expect(runCF(make<CFRow>().pipe(where("qty", "neq", 0))).sort()).toEqual(["1", "2", "4"])
1955
+ })
1956
+
1957
+ it("codeFilter: nested path access through dot notation", () => {
1958
+ expect(runCF(make<CFRow>().pipe(where("nested.kind", "k1"))).sort()).toEqual(["1", "3"])
1959
+ expect(
1960
+ runCF(
1961
+ make<CFRow>().pipe(
1962
+ where("nested.kind", "k2"),
1963
+ and("nested.v", "gt", 0)
1964
+ )
1965
+ )
1966
+ )
1967
+ .toEqual(["2"])
1968
+ })
1969
+
1970
+ it("codeFilter: array length predicates", () => {
1971
+ expect(runCF(make<CFRow>().pipe(where("tags.length", 0)))).toEqual(["4"])
1972
+ expect(runCF(make<CFRow>().pipe(where("tags.length", "gte", 2))).sort()).toEqual(["1", "3"])
1973
+ })
1974
+
1975
+ it("codeFilter: order + skip + limit applied after filter", () => {
1976
+ const q = make<CFRow>().pipe(
1977
+ where("tag", "neq", "z"),
1978
+ order("qty", "DESC"),
1979
+ page({ skip: 1, take: 2 })
1980
+ )
1981
+ expect(runCF(q)).toEqual(["2", "1"])
1982
+ })
1983
+
1984
+ // ─────────────────────────────────────────────────────────────────────────────
1985
+ // memFilter: aggregate (GROUP BY) support
1986
+ // ─────────────────────────────────────────────────────────────────────────────
1987
+
1988
+ it("memFilter: agg-count-when groups rows and counts conditionally", () => {
1989
+ type Row = { city: string; status: string }
1990
+ const rows: Row[] = [
1991
+ { city: "NYC", status: "active" },
1992
+ { city: "NYC", status: "inactive" },
1993
+ { city: "NYC", status: "active" },
1994
+ { city: "LA", status: "active" }
1995
+ ]
1996
+ const result = memFilter<Row>({
1997
+ t: {} as Row,
1998
+ select: [
1999
+ { key: "city", path: "city" },
2000
+ {
2001
+ key: "activeCount",
2002
+ aggregate: {
2003
+ _tag: "agg-count-when",
2004
+ filter: [{ t: "where", path: "status", op: "eq", value: "active" }]
2005
+ }
2006
+ },
2007
+ { key: "total", aggregate: { _tag: "agg-count" } }
2008
+ ] as any
2009
+ })(rows as any) as any[]
2010
+
2011
+ expect(result.length).toBe(2)
2012
+ const nyc = result.find((r: any) => r.city === "NYC")!
2013
+ expect(nyc.activeCount).toBe(2)
2014
+ expect(nyc.total).toBe(3)
2015
+ const la = result.find((r: any) => r.city === "LA")!
2016
+ expect(la.activeCount).toBe(1)
2017
+ expect(la.total).toBe(1)
2018
+ })
2019
+
2020
+ it("memFilter: agg-sum / agg-min / agg-max aggregate numerics", () => {
2021
+ type Row = { dept: string; salary: number }
2022
+ const rows: Row[] = [
2023
+ { dept: "eng", salary: 100 },
2024
+ { dept: "eng", salary: 200 },
2025
+ { dept: "hr", salary: 50 }
2026
+ ]
2027
+ const result = memFilter<Row>({
2028
+ t: {} as Row,
2029
+ select: [
2030
+ { key: "dept", path: "dept" },
2031
+ { key: "total", aggregate: { _tag: "agg-sum", field: "salary" } },
2032
+ { key: "min", aggregate: { _tag: "agg-min", field: "salary" } },
2033
+ { key: "max", aggregate: { _tag: "agg-max", field: "salary" } }
2034
+ ] as any
2035
+ })(rows as any) as any[]
2036
+
2037
+ expect(result.length).toBe(2)
2038
+ const eng = result.find((r: any) => r.dept === "eng")!
2039
+ expect(eng.total).toBe(300)
2040
+ expect(eng.min).toBe(100)
2041
+ expect(eng.max).toBe(200)
2042
+ const hr = result.find((r: any) => r.dept === "hr")!
2043
+ expect(hr.total).toBe(50)
2044
+ })
2045
+
2046
+ it("memFilter: aggregate with nested path grouping", () => {
2047
+ type Row = { address: { city: string }; value: number }
2048
+ const rows: Row[] = [
2049
+ { address: { city: "NYC" }, value: 10 },
2050
+ { address: { city: "NYC" }, value: 20 },
2051
+ { address: { city: "LA" }, value: 5 }
2052
+ ]
2053
+ const result = memFilter<Row>({
2054
+ t: {} as Row,
2055
+ select: [
2056
+ { key: "city", path: "address.city" },
2057
+ { key: "count", aggregate: { _tag: "agg-count" } }
2058
+ ] as any
2059
+ })(rows as any) as any[]
2060
+
2061
+ expect(result.length).toBe(2)
2062
+ expect(result.find((r: any) => r.city === "NYC")!.count).toBe(2)
2063
+ expect(result.find((r: any) => r.city === "LA")!.count).toBe(1)
2064
+ })