@effect-app/infra 4.0.0-beta.30 → 4.0.0-beta.301

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 +2291 -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 +31 -42
  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 +2 -2
  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} +5 -2
  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/routing/schema/jwt.d.ts.map +1 -0
  137. package/dist/routing/schema/jwt.js +13 -0
  138. package/dist/routing/tsort.d.ts.map +1 -0
  139. package/dist/routing/tsort.js +51 -0
  140. package/dist/{api/routing → routing}/utils.d.ts +3 -3
  141. package/dist/routing/utils.d.ts.map +1 -0
  142. package/dist/routing/utils.js +45 -0
  143. package/dist/routing.d.ts +182 -0
  144. package/dist/routing.d.ts.map +1 -0
  145. package/dist/routing.js +256 -0
  146. package/dist/test.d.ts +2 -2
  147. package/dist/test.d.ts.map +1 -1
  148. package/dist/test.js +2 -2
  149. package/dist/util.d.ts.map +1 -0
  150. package/dist/util.js +14 -0
  151. package/docs/cluster-storage.md +26 -0
  152. package/docs/workflow-engine.md +262 -0
  153. package/examples/query.ts +47 -39
  154. package/package.json +38 -352
  155. package/src/CUPS.ts +53 -14
  156. package/src/ClusterCosmos.ts +1118 -0
  157. package/src/ClusterServiceBus.ts +242 -0
  158. package/src/{api/ContextProvider.ts → ContextProvider.ts} +20 -17
  159. package/src/Emailer/Sendgrid.ts +83 -60
  160. package/src/Emailer/fake.ts +4 -4
  161. package/src/MainFiberSet.ts +32 -13
  162. package/src/QueueMaker/errors.ts +3 -1
  163. package/src/{adapters/SQL → SQL}/Model.ts +42 -41
  164. package/src/SQL.ts +1 -0
  165. package/src/Store/Cosmos/query.ts +218 -54
  166. package/src/Store/Cosmos.ts +529 -365
  167. package/src/Store/Disk.ts +112 -70
  168. package/src/Store/Memory.ts +365 -99
  169. package/src/Store/SQL/Pg.ts +363 -0
  170. package/src/Store/SQL/query.ts +603 -0
  171. package/src/Store/SQL.ts +746 -0
  172. package/src/Store/codeFilter.ts +8 -6
  173. package/src/Store/index.ts +25 -10
  174. package/src/Store/utils.ts +32 -25
  175. package/src/WorkflowEngineCosmos.ts +749 -0
  176. package/src/WorkflowEngineSqlite.ts +813 -0
  177. package/src/arbs.ts +5 -3
  178. package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
  179. package/src/errorReporter.ts +68 -78
  180. package/src/fileUtil.ts +1 -1
  181. package/src/index.ts +2 -1
  182. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  183. package/src/internal/auth.ts +272 -0
  184. package/src/{api/internal → internal}/events.ts +22 -13
  185. package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
  186. package/src/logger/jsonLogger.ts +5 -3
  187. package/src/logger/logFmtLogger.ts +3 -3
  188. package/src/logger/shared.ts +5 -4
  189. package/src/middlewares.ts +10 -0
  190. package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
  191. package/src/otel.ts +152 -0
  192. package/src/rateLimit.ts +34 -23
  193. package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
  194. package/src/{api/reportError.ts → reportError.ts} +3 -2
  195. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
  196. package/src/routing/middleware/middleware.ts +196 -0
  197. package/src/routing/middleware.ts +4 -0
  198. package/src/{api/routing → routing}/schema/jwt.ts +2 -1
  199. package/src/{api/routing → routing}/utils.ts +2 -1
  200. package/src/routing.ts +823 -0
  201. package/src/test.ts +4 -4
  202. package/src/vitest.ts +1 -1
  203. package/test/auth.test.ts +101 -0
  204. package/test/cluster-cosmos.test.ts +612 -0
  205. package/test/cluster-servicebus.test.ts +180 -0
  206. package/test/cluster-sqlite.test.ts +205 -0
  207. package/test/contextProvider.test.ts +15 -12
  208. package/test/controller.test.ts +28 -32
  209. package/test/cosmos-query.test.ts +228 -0
  210. package/test/dist/auth.test.d.ts.map +1 -0
  211. package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
  212. package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
  213. package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
  214. package/test/dist/contextProvider.test.d.ts.map +1 -1
  215. package/test/dist/controller.test.d.ts.map +1 -1
  216. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  217. package/test/dist/fixtures.d.ts +29 -11
  218. package/test/dist/fixtures.d.ts.map +1 -1
  219. package/test/dist/fixtures.js +17 -10
  220. package/test/dist/query.test.d.ts.map +1 -1
  221. package/test/dist/rawQuery.test.d.ts.map +1 -1
  222. package/test/dist/repository-ext.test.d.ts.map +1 -0
  223. package/test/dist/requires.test.d.ts.map +1 -1
  224. package/test/dist/router-generator.test.d.ts.map +1 -0
  225. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  226. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  227. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  228. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  229. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  230. package/test/dist/sql-store.test.d.ts.map +1 -0
  231. package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
  232. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  233. package/test/fixtures.ts +16 -9
  234. package/test/layerUtils.test.ts +2 -2
  235. package/test/query.test.ts +1007 -41
  236. package/test/rawQuery.test.ts +340 -22
  237. package/test/repository-ext.test.ts +178 -0
  238. package/test/requires.test.ts +10 -5
  239. package/test/router-generator.test.ts +187 -0
  240. package/test/routing-interruptibility.test.ts +66 -0
  241. package/test/rpc-context-map-streaming.test.ts +262 -0
  242. package/test/rpc-e2e-invalidation.test.ts +418 -0
  243. package/test/rpc-multi-middleware.test.ts +85 -10
  244. package/test/rpc-stream-fullstack.test.ts +304 -0
  245. package/test/sql-store.test.ts +1711 -0
  246. package/test/validateSample.test.ts +26 -14
  247. package/test/workflow-engine-cosmos.test.ts +345 -0
  248. package/test/workflow-engine-sqlite.test.ts +297 -0
  249. package/tsconfig.examples.json +1 -1
  250. package/tsconfig.json +3 -1
  251. package/tsconfig.src.json +35 -35
  252. package/tsconfig.test.json +2 -2
  253. package/dist/Emailer/service.d.ts +0 -55
  254. package/dist/Emailer/service.d.ts.map +0 -1
  255. package/dist/Emailer/service.js +0 -6
  256. package/dist/Emailer.d.ts +0 -2
  257. package/dist/Emailer.d.ts.map +0 -1
  258. package/dist/Emailer.js +0 -2
  259. package/dist/Model/Repository/ext.d.ts +0 -41
  260. package/dist/Model/Repository/ext.d.ts.map +0 -1
  261. package/dist/Model/Repository/ext.js +0 -65
  262. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  263. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  264. package/dist/Model/Repository/internal/internal.js +0 -316
  265. package/dist/Model/Repository/legacy.d.ts +0 -19
  266. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  267. package/dist/Model/Repository/legacy.js +0 -2
  268. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  269. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  270. package/dist/Model/Repository/makeRepo.js +0 -24
  271. package/dist/Model/Repository/service.d.ts +0 -89
  272. package/dist/Model/Repository/service.d.ts.map +0 -1
  273. package/dist/Model/Repository/service.js +0 -2
  274. package/dist/Model/Repository/validation.d.ts +0 -42
  275. package/dist/Model/Repository/validation.d.ts.map +0 -1
  276. package/dist/Model/Repository/validation.js +0 -32
  277. package/dist/Model/Repository.d.ts +0 -6
  278. package/dist/Model/Repository.d.ts.map +0 -1
  279. package/dist/Model/Repository.js +0 -6
  280. package/dist/Model/dsl.d.ts +0 -32
  281. package/dist/Model/dsl.d.ts.map +0 -1
  282. package/dist/Model/dsl.js +0 -44
  283. package/dist/Model/filter/filterApi.d.ts +0 -30
  284. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  285. package/dist/Model/filter/filterApi.js +0 -2
  286. package/dist/Model/filter/types/errors.d.ts +0 -29
  287. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  288. package/dist/Model/filter/types/errors.js +0 -2
  289. package/dist/Model/filter/types/fields.d.ts +0 -15
  290. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  291. package/dist/Model/filter/types/fields.js +0 -2
  292. package/dist/Model/filter/types/path/common.d.ts +0 -316
  293. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  294. package/dist/Model/filter/types/path/common.js +0 -2
  295. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  296. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  297. package/dist/Model/filter/types/path/eager.js +0 -31
  298. package/dist/Model/filter/types/path/index.d.ts +0 -4
  299. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  300. package/dist/Model/filter/types/path/index.js +0 -3
  301. package/dist/Model/filter/types/utils.d.ts +0 -79
  302. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  303. package/dist/Model/filter/types/utils.js +0 -2
  304. package/dist/Model/filter/types/validator.d.ts +0 -30
  305. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  306. package/dist/Model/filter/types/validator.js +0 -2
  307. package/dist/Model/filter/types.d.ts +0 -5
  308. package/dist/Model/filter/types.d.ts.map +0 -1
  309. package/dist/Model/filter/types.js +0 -7
  310. package/dist/Model/query/dsl.d.ts +0 -248
  311. package/dist/Model/query/dsl.d.ts.map +0 -1
  312. package/dist/Model/query/dsl.js +0 -104
  313. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  314. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  315. package/dist/Model/query/new-kid-interpreter.js +0 -165
  316. package/dist/Model/query.d.ts +0 -15
  317. package/dist/Model/query.d.ts.map +0 -1
  318. package/dist/Model/query.js +0 -3
  319. package/dist/Model.d.ts +0 -4
  320. package/dist/Model.d.ts.map +0 -1
  321. package/dist/Model.js +0 -4
  322. package/dist/Operations.d.ts +0 -55
  323. package/dist/Operations.d.ts.map +0 -1
  324. package/dist/Operations.js +0 -102
  325. package/dist/OperationsRepo.d.ts +0 -41
  326. package/dist/OperationsRepo.d.ts.map +0 -1
  327. package/dist/OperationsRepo.js +0 -14
  328. package/dist/QueueMaker/SQLQueue.d.ts +0 -16
  329. package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
  330. package/dist/QueueMaker/SQLQueue.js +0 -130
  331. package/dist/QueueMaker/memQueue.d.ts +0 -11
  332. package/dist/QueueMaker/memQueue.d.ts.map +0 -1
  333. package/dist/QueueMaker/memQueue.js +0 -76
  334. package/dist/QueueMaker/sbqueue.d.ts +0 -12
  335. package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
  336. package/dist/QueueMaker/sbqueue.js +0 -91
  337. package/dist/QueueMaker/service.d.ts +0 -11
  338. package/dist/QueueMaker/service.d.ts.map +0 -1
  339. package/dist/QueueMaker/service.js +0 -4
  340. package/dist/RequestContext.d.ts +0 -63
  341. package/dist/RequestContext.d.ts.map +0 -1
  342. package/dist/RequestContext.js +0 -49
  343. package/dist/RequestFiberSet.d.ts +0 -36
  344. package/dist/RequestFiberSet.d.ts.map +0 -1
  345. package/dist/RequestFiberSet.js +0 -78
  346. package/dist/Store/ContextMapContainer.d.ts +0 -14
  347. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  348. package/dist/Store/ContextMapContainer.js +0 -16
  349. package/dist/Store/service.d.ts +0 -108
  350. package/dist/Store/service.d.ts.map +0 -1
  351. package/dist/Store/service.js +0 -71
  352. package/dist/Store.d.ts +0 -2
  353. package/dist/Store.d.ts.map +0 -1
  354. package/dist/Store.js +0 -2
  355. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  356. package/dist/adapters/SQL/Model.js +0 -478
  357. package/dist/adapters/SQL.d.ts.map +0 -1
  358. package/dist/adapters/ServiceBus.d.ts +0 -58
  359. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  360. package/dist/adapters/ServiceBus.js +0 -99
  361. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  362. package/dist/adapters/cosmos-client.js +0 -9
  363. package/dist/adapters/index.d.ts +0 -2
  364. package/dist/adapters/index.d.ts.map +0 -1
  365. package/dist/adapters/index.js +0 -2
  366. package/dist/adapters/logger.d.ts +0 -9
  367. package/dist/adapters/logger.d.ts.map +0 -1
  368. package/dist/adapters/logger.js +0 -3
  369. package/dist/adapters/memQueue.d.ts +0 -13
  370. package/dist/adapters/memQueue.d.ts.map +0 -1
  371. package/dist/adapters/memQueue.js +0 -20
  372. package/dist/adapters/mongo-client.d.ts.map +0 -1
  373. package/dist/adapters/mongo-client.js +0 -13
  374. package/dist/adapters/redis-client.d.ts.map +0 -1
  375. package/dist/adapters/redis-client.js +0 -94
  376. package/dist/api/ContextProvider.d.ts +0 -31
  377. package/dist/api/ContextProvider.d.ts.map +0 -1
  378. package/dist/api/ContextProvider.js +0 -38
  379. package/dist/api/codec.d.ts.map +0 -1
  380. package/dist/api/codec.js +0 -5
  381. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  382. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  383. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  384. package/dist/api/internal/auth.d.ts +0 -15
  385. package/dist/api/internal/auth.d.ts.map +0 -1
  386. package/dist/api/internal/auth.js +0 -47
  387. package/dist/api/internal/events.d.ts +0 -9
  388. package/dist/api/internal/events.d.ts.map +0 -1
  389. package/dist/api/internal/events.js +0 -42
  390. package/dist/api/internal/health.d.ts.map +0 -1
  391. package/dist/api/internal/health.js +0 -5
  392. package/dist/api/layerUtils.d.ts.map +0 -1
  393. package/dist/api/layerUtils.js +0 -16
  394. package/dist/api/middlewares.d.ts.map +0 -1
  395. package/dist/api/reportError.d.ts.map +0 -1
  396. package/dist/api/reportError.js +0 -27
  397. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  398. package/dist/api/routing/middleware/middleware.d.ts +0 -9
  399. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  400. package/dist/api/routing/middleware/middleware.js +0 -92
  401. package/dist/api/routing/middleware.d.ts.map +0 -1
  402. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  403. package/dist/api/routing/schema/jwt.js +0 -12
  404. package/dist/api/routing/tsort.d.ts.map +0 -1
  405. package/dist/api/routing/tsort.js +0 -51
  406. package/dist/api/routing/utils.d.ts.map +0 -1
  407. package/dist/api/routing/utils.js +0 -44
  408. package/dist/api/routing.d.ts +0 -138
  409. package/dist/api/routing.d.ts.map +0 -1
  410. package/dist/api/routing.js +0 -166
  411. package/dist/api/setupRequest.d.ts +0 -12
  412. package/dist/api/setupRequest.d.ts.map +0 -1
  413. package/dist/api/setupRequest.js +0 -44
  414. package/dist/api/util.d.ts.map +0 -1
  415. package/dist/api/util.js +0 -14
  416. package/eslint.config.mjs +0 -24
  417. package/src/Emailer/service.ts +0 -52
  418. package/src/Emailer.ts +0 -1
  419. package/src/Model/Repository/ext.ts +0 -283
  420. package/src/Model/Repository/internal/internal.ts +0 -577
  421. package/src/Model/Repository/legacy.ts +0 -27
  422. package/src/Model/Repository/makeRepo.ts +0 -139
  423. package/src/Model/Repository/service.ts +0 -627
  424. package/src/Model/Repository/validation.ts +0 -31
  425. package/src/Model/Repository.ts +0 -5
  426. package/src/Model/dsl.ts +0 -128
  427. package/src/Model/filter/filterApi.ts +0 -60
  428. package/src/Model/filter/types/errors.ts +0 -47
  429. package/src/Model/filter/types/fields.ts +0 -50
  430. package/src/Model/filter/types/path/common.ts +0 -404
  431. package/src/Model/filter/types/path/eager.ts +0 -298
  432. package/src/Model/filter/types/path/index.ts +0 -4
  433. package/src/Model/filter/types/utils.ts +0 -128
  434. package/src/Model/filter/types/validator.ts +0 -46
  435. package/src/Model/filter/types.ts +0 -6
  436. package/src/Model/query/dsl.ts +0 -2110
  437. package/src/Model/query/new-kid-interpreter.ts +0 -210
  438. package/src/Model/query.ts +0 -13
  439. package/src/Model.ts +0 -3
  440. package/src/Operations.ts +0 -235
  441. package/src/OperationsRepo.ts +0 -16
  442. package/src/QueueMaker/SQLQueue.ts +0 -189
  443. package/src/QueueMaker/memQueue.ts +0 -130
  444. package/src/QueueMaker/sbqueue.ts +0 -137
  445. package/src/QueueMaker/service.ts +0 -17
  446. package/src/RequestContext.ts +0 -63
  447. package/src/RequestFiberSet.ts +0 -105
  448. package/src/Store/ContextMapContainer.ts +0 -20
  449. package/src/Store/index.test.ts.bak +0 -112
  450. package/src/Store/service.ts +0 -184
  451. package/src/Store.ts +0 -1
  452. package/src/adapters/SQL.ts +0 -1
  453. package/src/adapters/ServiceBus.ts +0 -209
  454. package/src/adapters/index.ts +0 -0
  455. package/src/adapters/logger.ts +0 -3
  456. package/src/adapters/memQueue.ts +0 -21
  457. package/src/api/internal/auth.ts +0 -68
  458. package/src/api/middlewares.ts +0 -10
  459. package/src/api/routing/middleware/middleware.ts +0 -128
  460. package/src/api/routing/middleware.ts +0 -6
  461. package/src/api/routing.ts +0 -598
  462. package/src/api/setupRequest.ts +0 -84
  463. package/tsconfig.json.bak +0 -12
  464. /package/dist/{adapters/SQL.d.ts → SQL.d.ts} +0 -0
  465. /package/dist/{api/codec.d.ts → codec.d.ts} +0 -0
  466. /package/dist/{api/internal → internal}/health.d.ts +0 -0
  467. /package/dist/{api/middlewares.d.ts → middlewares.d.ts} +0 -0
  468. /package/dist/{api/routing → routing}/schema/jwt.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
 
@@ -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(
@@ -377,7 +389,6 @@ it(
377
389
  // eslint-disable-next-line unused-imports/no-unused-vars
378
390
  const query5 = make<Union>().pipe(
379
391
  where("id", "bla"),
380
- // @ts-expect-error cannot project over fields that are not in common between the union members (you must refine the union first)
381
392
  project(S.Struct({ id: S.String, a: S.Unknown }))
382
393
  )
383
394
  console.log(query5)
@@ -465,7 +476,7 @@ it(
465
476
 
466
477
  expect([]).toEqual([])
467
478
  })
468
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
479
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
469
480
  )
470
481
 
471
482
  it(
@@ -508,7 +519,7 @@ it(
508
519
 
509
520
  expect([]).toEqual([])
510
521
  })
511
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
522
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
512
523
  )
513
524
 
514
525
  it(
@@ -519,8 +530,8 @@ it(
519
530
  const schema = S.Struct({
520
531
  id: S.String,
521
532
  createdAt: S.Date.pipe(
522
- S.withDecodingDefault(() => new Date().toISOString()),
523
- S.withConstructorDefault(() => Option.some(new Date()))
533
+ S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
534
+ S.withConstructorDefault(Effect.sync(() => new Date()))
524
535
  )
525
536
  })
526
537
  const repo = yield* makeRepo(
@@ -532,8 +543,8 @@ it(
532
543
  const outputSchema = S.Struct({
533
544
  id: S.Literal("123"),
534
545
  createdAt: S.Date.pipe(
535
- S.withDecodingDefault(() => new Date().toISOString()),
536
- S.withConstructorDefault(() => Option.some(new Date()))
546
+ S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
547
+ S.withConstructorDefault(Effect.sync(() => new Date()))
537
548
  )
538
549
  })
539
550
 
@@ -541,9 +552,347 @@ it(
541
552
 
542
553
  expect(result).toEqual([])
543
554
  })
544
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
555
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
545
556
  )
546
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
+
547
896
  it(
548
897
  "doesn't mess when refining fields",
549
898
  () =>
@@ -551,7 +900,7 @@ it(
551
900
  .gen(function*() {
552
901
  const schema = S.Struct({
553
902
  id: S.String,
554
- literals: S.Literal("a", "b", "c")
903
+ literals: S.Literals(["a", "b", "c"])
555
904
  })
556
905
 
557
906
  type Schema = typeof schema.Type
@@ -571,7 +920,7 @@ it(
571
920
 
572
921
  expect(result).toEqual([])
573
922
  })
574
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
923
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
575
924
  )
576
925
 
577
926
  it(
@@ -581,7 +930,7 @@ it(
581
930
  .gen(function*() {
582
931
  const schema = S.Struct({
583
932
  id: S.String,
584
- literals: S.Union([S.Literal("a", "b", "c"), S.Null])
933
+ literals: S.Union([S.Literals(["a", "b", "c"]), S.Null])
585
934
  })
586
935
 
587
936
  type Schema = typeof schema.Type
@@ -615,7 +964,7 @@ it(
615
964
 
616
965
  expect(result).toEqual([])
617
966
  })
618
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
967
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
619
968
  )
620
969
 
621
970
  it(
@@ -659,7 +1008,7 @@ it(
659
1008
 
660
1009
  expect(result).toEqual([])
661
1010
  })
662
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
1011
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
663
1012
  )
664
1013
 
665
1014
  it("remove null from one constituent of a tagged union", () =>
@@ -672,7 +1021,7 @@ it("remove null from one constituent of a tagged union", () =>
672
1021
 
673
1022
  class BB extends S.Class<BB>("BB")({
674
1023
  id: S.Literal("BB"),
675
- b: S.NullOr(S.Number)
1024
+ b: S.NullOr(S.Finite)
676
1025
  }) {}
677
1026
 
678
1027
  type Union = AA | BB
@@ -708,7 +1057,7 @@ it("remove null from one constituent of a tagged union", () =>
708
1057
  })[]
709
1058
  >()
710
1059
  })
711
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1060
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
712
1061
 
713
1062
  it("refine 3", () =>
714
1063
  Effect
@@ -746,7 +1095,7 @@ it("refine 3", () =>
746
1095
  const resQuer1 = yield* repo.query(where("id", "AA"))
747
1096
  expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
748
1097
  })
749
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1098
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
750
1099
 
751
1100
  it("my test", () =>
752
1101
  Effect
@@ -764,7 +1113,7 @@ it("my test", () =>
764
1113
  )
765
1114
  expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
766
1115
  })
767
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1116
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
768
1117
 
769
1118
  it("refine inner without imposing a projection", () =>
770
1119
  Effect
@@ -808,7 +1157,7 @@ it("refine inner without imposing a projection", () =>
808
1157
  where("union._tag", "AA"),
809
1158
  // But if I wanna the whole Data as output ignoring the inner refinement
810
1159
  // I wanna be able to do so
811
- project(S.Struct(Data.pick("union")))
1160
+ project(Data.mapFields(Struct.pick(["union"])))
812
1161
  )
813
1162
 
814
1163
  expectTypeOf(query2).toEqualTypeOf<
@@ -839,7 +1188,7 @@ it("refine inner without imposing a projection", () =>
839
1188
  }[]
840
1189
  >()
841
1190
  })
842
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1191
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
843
1192
 
844
1193
  it("does not allow string queries on arrays", () =>
845
1194
  Effect
@@ -874,7 +1223,7 @@ it("does not allow string queries on arrays", () =>
874
1223
  expectTypeOf(good3).toEqualTypeOf<QueryWhere<Some, Some>>()
875
1224
  expectTypeOf(good4).toEqualTypeOf<QueryWhere<Some, Some>>()
876
1225
  })
877
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1226
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
878
1227
 
879
1228
  it("test array.length", () =>
880
1229
  Effect
@@ -915,7 +1264,7 @@ it("test array.length", () =>
915
1264
  QueryWhere<Something, Something>
916
1265
  >()
917
1266
  })
918
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1267
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
919
1268
 
920
1269
  it("distribution over union", () =>
921
1270
  Effect
@@ -939,7 +1288,7 @@ it("distribution over union", () =>
939
1288
  })[]
940
1289
  >()
941
1290
  })
942
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1291
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
943
1292
 
944
1293
  it("refine nested union", () =>
945
1294
  Effect
@@ -980,7 +1329,158 @@ it("refine nested union", () =>
980
1329
  }[]
981
1330
  >()
982
1331
  })
983
- .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))
984
1484
 
985
1485
  it("refine union with nested union", () =>
986
1486
  Effect
@@ -1095,4 +1595,470 @@ it("refine union with nested union", () =>
1095
1595
  })[]
1096
1596
  >()
1097
1597
  })
1098
- .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
+ })