@effect-app/infra 4.0.0-beta.31 → 4.0.0-beta.310

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 (473) hide show
  1. package/CHANGELOG.md +2361 -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 +253 -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 +472 -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.map +1 -1
  109. package/dist/middlewares.d.ts.map +1 -0
  110. package/dist/{api/middlewares.js → middlewares.js} +1 -1
  111. package/dist/{adapters/mongo-client.d.ts → mongo-client.d.ts} +3 -2
  112. package/dist/mongo-client.d.ts.map +1 -0
  113. package/dist/mongo-client.js +15 -0
  114. package/dist/otel.d.ts +75 -0
  115. package/dist/otel.d.ts.map +1 -0
  116. package/dist/otel.js +84 -0
  117. package/dist/rateLimit.d.ts +11 -3
  118. package/dist/rateLimit.d.ts.map +1 -1
  119. package/dist/rateLimit.js +7 -12
  120. package/dist/{adapters/redis-client.d.ts → redis-client.d.ts} +5 -2
  121. package/dist/redis-client.d.ts.map +1 -0
  122. package/dist/redis-client.js +98 -0
  123. package/dist/{api/reportError.d.ts → reportError.d.ts} +1 -1
  124. package/dist/reportError.d.ts.map +1 -0
  125. package/dist/reportError.js +28 -0
  126. package/dist/{api/routing → routing}/middleware/RouterMiddleware.d.ts +4 -3
  127. package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
  128. package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
  129. package/dist/routing/middleware/middleware.d.ts +48 -0
  130. package/dist/routing/middleware/middleware.d.ts.map +1 -0
  131. package/dist/routing/middleware/middleware.js +152 -0
  132. package/dist/{api/routing → routing}/middleware.d.ts +0 -1
  133. package/dist/routing/middleware.d.ts.map +1 -0
  134. package/dist/{api/routing → routing}/middleware.js +1 -2
  135. package/dist/routing/schema/jwt.d.ts.map +1 -0
  136. package/dist/routing/schema/jwt.js +13 -0
  137. package/dist/routing/tsort.d.ts.map +1 -0
  138. package/dist/routing/tsort.js +51 -0
  139. package/dist/{api/routing → routing}/utils.d.ts +3 -3
  140. package/dist/routing/utils.d.ts.map +1 -0
  141. package/dist/routing/utils.js +45 -0
  142. package/dist/routing.d.ts +182 -0
  143. package/dist/routing.d.ts.map +1 -0
  144. package/dist/routing.js +256 -0
  145. package/dist/test.d.ts +2 -2
  146. package/dist/test.d.ts.map +1 -1
  147. package/dist/test.js +2 -2
  148. package/dist/util.d.ts.map +1 -0
  149. package/dist/util.js +14 -0
  150. package/docs/cluster-storage.md +26 -0
  151. package/docs/workflow-engine.md +262 -0
  152. package/examples/query.ts +47 -39
  153. package/package.json +38 -352
  154. package/src/CUPS.ts +53 -14
  155. package/src/ClusterCosmos.ts +1118 -0
  156. package/src/ClusterServiceBus.ts +242 -0
  157. package/src/{api/ContextProvider.ts → ContextProvider.ts} +20 -17
  158. package/src/Emailer/Sendgrid.ts +83 -60
  159. package/src/Emailer/fake.ts +4 -4
  160. package/src/MainFiberSet.ts +32 -13
  161. package/src/QueueMaker/errors.ts +3 -1
  162. package/src/{adapters/SQL → SQL}/Model.ts +42 -41
  163. package/src/SQL.ts +1 -0
  164. package/src/Store/Cosmos/query.ts +218 -54
  165. package/src/Store/Cosmos.ts +529 -365
  166. package/src/Store/Disk.ts +112 -70
  167. package/src/Store/Memory.ts +365 -99
  168. package/src/Store/SQL/Pg.ts +389 -0
  169. package/src/Store/SQL/query.ts +603 -0
  170. package/src/Store/SQL.ts +766 -0
  171. package/src/Store/codeFilter.ts +8 -6
  172. package/src/Store/index.ts +25 -10
  173. package/src/Store/utils.ts +32 -25
  174. package/src/WorkflowEngineCosmos.ts +749 -0
  175. package/src/WorkflowEngineSqlite.ts +813 -0
  176. package/src/arbs.ts +5 -3
  177. package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
  178. package/src/errorReporter.ts +68 -78
  179. package/src/fileUtil.ts +1 -1
  180. package/src/index.ts +2 -1
  181. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  182. package/src/internal/auth.ts +272 -0
  183. package/src/{api/internal → internal}/events.ts +22 -13
  184. package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
  185. package/src/logger/jsonLogger.ts +5 -3
  186. package/src/logger/logFmtLogger.ts +3 -3
  187. package/src/logger/shared.ts +5 -4
  188. package/src/middlewares.ts +10 -0
  189. package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
  190. package/src/otel.ts +177 -0
  191. package/src/rateLimit.ts +34 -23
  192. package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
  193. package/src/{api/reportError.ts → reportError.ts} +3 -2
  194. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
  195. package/src/routing/middleware/middleware.ts +196 -0
  196. package/src/routing/middleware.ts +4 -0
  197. package/src/{api/routing → routing}/schema/jwt.ts +2 -1
  198. package/src/{api/routing → routing}/utils.ts +2 -1
  199. package/src/routing.ts +823 -0
  200. package/src/test.ts +4 -4
  201. package/src/vitest.ts +1 -1
  202. package/test/auth.test.ts +101 -0
  203. package/test/cluster-cosmos.test.ts +612 -0
  204. package/test/cluster-servicebus.test.ts +180 -0
  205. package/test/cluster-sqlite.test.ts +205 -0
  206. package/test/contextProvider.test.ts +15 -12
  207. package/test/controller.test.ts +28 -32
  208. package/test/cosmos-query.test.ts +228 -0
  209. package/test/dist/auth.test.d.ts.map +1 -0
  210. package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
  211. package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
  212. package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
  213. package/test/dist/contextProvider.test.d.ts.map +1 -1
  214. package/test/dist/controller.test.d.ts.map +1 -1
  215. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  216. package/test/dist/fixtures.d.ts +29 -11
  217. package/test/dist/fixtures.d.ts.map +1 -1
  218. package/test/dist/fixtures.js +17 -10
  219. package/test/dist/query.test.d.ts.map +1 -1
  220. package/test/dist/rawQuery.test.d.ts.map +1 -1
  221. package/test/dist/repository-ext.test.d.ts.map +1 -0
  222. package/test/dist/requires.test.d.ts.map +1 -1
  223. package/test/dist/router-generator.test.d.ts.map +1 -0
  224. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  225. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  226. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  227. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  228. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  229. package/test/dist/sql-store.test.d.ts.map +1 -0
  230. package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
  231. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  232. package/test/fixtures.ts +16 -9
  233. package/test/layerUtils.test.ts +2 -2
  234. package/test/query.test.ts +1130 -41
  235. package/test/rawQuery.test.ts +340 -22
  236. package/test/repository-ext.test.ts +426 -0
  237. package/test/requires.test.ts +10 -5
  238. package/test/router-generator.test.ts +187 -0
  239. package/test/routing-interruptibility.test.ts +66 -0
  240. package/test/rpc-context-map-streaming.test.ts +262 -0
  241. package/test/rpc-e2e-invalidation.test.ts +418 -0
  242. package/test/rpc-multi-middleware.test.ts +85 -10
  243. package/test/rpc-stream-fullstack.test.ts +304 -0
  244. package/test/sql-store.test.ts +1711 -0
  245. package/test/validateSample.test.ts +26 -14
  246. package/test/workflow-engine-cosmos.test.ts +345 -0
  247. package/test/workflow-engine-sqlite.test.ts +297 -0
  248. package/tsconfig.examples.json +1 -1
  249. package/tsconfig.json +9 -2
  250. package/tsconfig.src.json +35 -35
  251. package/tsconfig.test.json +2 -2
  252. package/dist/Emailer/service.d.ts +0 -55
  253. package/dist/Emailer/service.d.ts.map +0 -1
  254. package/dist/Emailer/service.js +0 -6
  255. package/dist/Emailer.d.ts +0 -2
  256. package/dist/Emailer.d.ts.map +0 -1
  257. package/dist/Emailer.js +0 -2
  258. package/dist/Model/Repository/ext.d.ts +0 -41
  259. package/dist/Model/Repository/ext.d.ts.map +0 -1
  260. package/dist/Model/Repository/ext.js +0 -65
  261. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  262. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  263. package/dist/Model/Repository/internal/internal.js +0 -316
  264. package/dist/Model/Repository/legacy.d.ts +0 -19
  265. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  266. package/dist/Model/Repository/legacy.js +0 -2
  267. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  268. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  269. package/dist/Model/Repository/makeRepo.js +0 -24
  270. package/dist/Model/Repository/service.d.ts +0 -89
  271. package/dist/Model/Repository/service.d.ts.map +0 -1
  272. package/dist/Model/Repository/service.js +0 -2
  273. package/dist/Model/Repository/validation.d.ts +0 -42
  274. package/dist/Model/Repository/validation.d.ts.map +0 -1
  275. package/dist/Model/Repository/validation.js +0 -32
  276. package/dist/Model/Repository.d.ts +0 -6
  277. package/dist/Model/Repository.d.ts.map +0 -1
  278. package/dist/Model/Repository.js +0 -6
  279. package/dist/Model/dsl.d.ts +0 -32
  280. package/dist/Model/dsl.d.ts.map +0 -1
  281. package/dist/Model/dsl.js +0 -44
  282. package/dist/Model/filter/filterApi.d.ts +0 -30
  283. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  284. package/dist/Model/filter/filterApi.js +0 -2
  285. package/dist/Model/filter/types/errors.d.ts +0 -29
  286. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  287. package/dist/Model/filter/types/errors.js +0 -2
  288. package/dist/Model/filter/types/fields.d.ts +0 -15
  289. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  290. package/dist/Model/filter/types/fields.js +0 -2
  291. package/dist/Model/filter/types/path/common.d.ts +0 -316
  292. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  293. package/dist/Model/filter/types/path/common.js +0 -2
  294. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  295. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  296. package/dist/Model/filter/types/path/eager.js +0 -31
  297. package/dist/Model/filter/types/path/index.d.ts +0 -4
  298. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  299. package/dist/Model/filter/types/path/index.js +0 -3
  300. package/dist/Model/filter/types/utils.d.ts +0 -79
  301. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  302. package/dist/Model/filter/types/utils.js +0 -2
  303. package/dist/Model/filter/types/validator.d.ts +0 -30
  304. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  305. package/dist/Model/filter/types/validator.js +0 -2
  306. package/dist/Model/filter/types.d.ts +0 -5
  307. package/dist/Model/filter/types.d.ts.map +0 -1
  308. package/dist/Model/filter/types.js +0 -7
  309. package/dist/Model/query/dsl.d.ts +0 -248
  310. package/dist/Model/query/dsl.d.ts.map +0 -1
  311. package/dist/Model/query/dsl.js +0 -104
  312. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  313. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  314. package/dist/Model/query/new-kid-interpreter.js +0 -165
  315. package/dist/Model/query.d.ts +0 -15
  316. package/dist/Model/query.d.ts.map +0 -1
  317. package/dist/Model/query.js +0 -3
  318. package/dist/Model.d.ts +0 -4
  319. package/dist/Model.d.ts.map +0 -1
  320. package/dist/Model.js +0 -4
  321. package/dist/Operations.d.ts +0 -55
  322. package/dist/Operations.d.ts.map +0 -1
  323. package/dist/Operations.js +0 -102
  324. package/dist/OperationsRepo.d.ts +0 -41
  325. package/dist/OperationsRepo.d.ts.map +0 -1
  326. package/dist/OperationsRepo.js +0 -14
  327. package/dist/QueueMaker/SQLQueue.d.ts +0 -16
  328. package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
  329. package/dist/QueueMaker/SQLQueue.js +0 -130
  330. package/dist/QueueMaker/memQueue.d.ts +0 -11
  331. package/dist/QueueMaker/memQueue.d.ts.map +0 -1
  332. package/dist/QueueMaker/memQueue.js +0 -76
  333. package/dist/QueueMaker/sbqueue.d.ts +0 -12
  334. package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
  335. package/dist/QueueMaker/sbqueue.js +0 -91
  336. package/dist/QueueMaker/service.d.ts +0 -11
  337. package/dist/QueueMaker/service.d.ts.map +0 -1
  338. package/dist/QueueMaker/service.js +0 -4
  339. package/dist/RequestContext.d.ts +0 -63
  340. package/dist/RequestContext.d.ts.map +0 -1
  341. package/dist/RequestContext.js +0 -49
  342. package/dist/RequestFiberSet.d.ts +0 -36
  343. package/dist/RequestFiberSet.d.ts.map +0 -1
  344. package/dist/RequestFiberSet.js +0 -78
  345. package/dist/Store/ContextMapContainer.d.ts +0 -14
  346. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  347. package/dist/Store/ContextMapContainer.js +0 -16
  348. package/dist/Store/service.d.ts +0 -108
  349. package/dist/Store/service.d.ts.map +0 -1
  350. package/dist/Store/service.js +0 -71
  351. package/dist/Store.d.ts +0 -2
  352. package/dist/Store.d.ts.map +0 -1
  353. package/dist/Store.js +0 -2
  354. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  355. package/dist/adapters/SQL/Model.js +0 -478
  356. package/dist/adapters/SQL.d.ts.map +0 -1
  357. package/dist/adapters/ServiceBus.d.ts +0 -58
  358. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  359. package/dist/adapters/ServiceBus.js +0 -99
  360. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  361. package/dist/adapters/cosmos-client.js +0 -9
  362. package/dist/adapters/index.d.ts +0 -2
  363. package/dist/adapters/index.d.ts.map +0 -1
  364. package/dist/adapters/index.js +0 -2
  365. package/dist/adapters/logger.d.ts +0 -9
  366. package/dist/adapters/logger.d.ts.map +0 -1
  367. package/dist/adapters/logger.js +0 -3
  368. package/dist/adapters/memQueue.d.ts +0 -13
  369. package/dist/adapters/memQueue.d.ts.map +0 -1
  370. package/dist/adapters/memQueue.js +0 -20
  371. package/dist/adapters/mongo-client.d.ts.map +0 -1
  372. package/dist/adapters/mongo-client.js +0 -13
  373. package/dist/adapters/redis-client.d.ts.map +0 -1
  374. package/dist/adapters/redis-client.js +0 -94
  375. package/dist/api/ContextProvider.d.ts +0 -31
  376. package/dist/api/ContextProvider.d.ts.map +0 -1
  377. package/dist/api/ContextProvider.js +0 -38
  378. package/dist/api/codec.d.ts.map +0 -1
  379. package/dist/api/codec.js +0 -5
  380. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  381. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  382. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  383. package/dist/api/internal/auth.d.ts +0 -15
  384. package/dist/api/internal/auth.d.ts.map +0 -1
  385. package/dist/api/internal/auth.js +0 -47
  386. package/dist/api/internal/events.d.ts +0 -9
  387. package/dist/api/internal/events.d.ts.map +0 -1
  388. package/dist/api/internal/events.js +0 -42
  389. package/dist/api/internal/health.d.ts.map +0 -1
  390. package/dist/api/internal/health.js +0 -5
  391. package/dist/api/layerUtils.d.ts.map +0 -1
  392. package/dist/api/layerUtils.js +0 -16
  393. package/dist/api/middlewares.d.ts.map +0 -1
  394. package/dist/api/reportError.d.ts.map +0 -1
  395. package/dist/api/reportError.js +0 -27
  396. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  397. package/dist/api/routing/middleware/middleware.d.ts +0 -9
  398. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  399. package/dist/api/routing/middleware/middleware.js +0 -92
  400. package/dist/api/routing/middleware.d.ts.map +0 -1
  401. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  402. package/dist/api/routing/schema/jwt.js +0 -12
  403. package/dist/api/routing/tsort.d.ts.map +0 -1
  404. package/dist/api/routing/tsort.js +0 -51
  405. package/dist/api/routing/utils.d.ts.map +0 -1
  406. package/dist/api/routing/utils.js +0 -44
  407. package/dist/api/routing.d.ts +0 -138
  408. package/dist/api/routing.d.ts.map +0 -1
  409. package/dist/api/routing.js +0 -166
  410. package/dist/api/setupRequest.d.ts +0 -12
  411. package/dist/api/setupRequest.d.ts.map +0 -1
  412. package/dist/api/setupRequest.js +0 -44
  413. package/dist/api/util.d.ts.map +0 -1
  414. package/dist/api/util.js +0 -14
  415. package/eslint.config.mjs +0 -24
  416. package/src/Emailer/service.ts +0 -52
  417. package/src/Emailer.ts +0 -1
  418. package/src/Model/Repository/ext.ts +0 -283
  419. package/src/Model/Repository/internal/internal.ts +0 -577
  420. package/src/Model/Repository/legacy.ts +0 -27
  421. package/src/Model/Repository/makeRepo.ts +0 -139
  422. package/src/Model/Repository/service.ts +0 -627
  423. package/src/Model/Repository/validation.ts +0 -31
  424. package/src/Model/Repository.ts +0 -5
  425. package/src/Model/dsl.ts +0 -128
  426. package/src/Model/filter/filterApi.ts +0 -60
  427. package/src/Model/filter/types/errors.ts +0 -47
  428. package/src/Model/filter/types/fields.ts +0 -50
  429. package/src/Model/filter/types/path/common.ts +0 -404
  430. package/src/Model/filter/types/path/eager.ts +0 -298
  431. package/src/Model/filter/types/path/index.ts +0 -4
  432. package/src/Model/filter/types/utils.ts +0 -128
  433. package/src/Model/filter/types/validator.ts +0 -46
  434. package/src/Model/filter/types.ts +0 -6
  435. package/src/Model/query/dsl.ts +0 -2110
  436. package/src/Model/query/new-kid-interpreter.ts +0 -210
  437. package/src/Model/query.ts +0 -13
  438. package/src/Model.ts +0 -3
  439. package/src/Operations.ts +0 -235
  440. package/src/OperationsRepo.ts +0 -16
  441. package/src/QueueMaker/SQLQueue.ts +0 -189
  442. package/src/QueueMaker/memQueue.ts +0 -130
  443. package/src/QueueMaker/sbqueue.ts +0 -137
  444. package/src/QueueMaker/service.ts +0 -17
  445. package/src/RequestContext.ts +0 -63
  446. package/src/RequestFiberSet.ts +0 -105
  447. package/src/Store/ContextMapContainer.ts +0 -20
  448. package/src/Store/index.test.ts.bak +0 -112
  449. package/src/Store/service.ts +0 -184
  450. package/src/Store.ts +0 -1
  451. package/src/adapters/SQL.ts +0 -1
  452. package/src/adapters/ServiceBus.ts +0 -209
  453. package/src/adapters/index.ts +0 -0
  454. package/src/adapters/logger.ts +0 -3
  455. package/src/adapters/memQueue.ts +0 -21
  456. package/src/api/internal/auth.ts +0 -68
  457. package/src/api/middlewares.ts +0 -10
  458. package/src/api/routing/middleware/middleware.ts +0 -128
  459. package/src/api/routing/middleware.ts +0 -6
  460. package/src/api/routing.ts +0 -598
  461. package/src/api/setupRequest.ts +0 -84
  462. package/tsconfig.json.bak +0 -12
  463. /package/dist/{adapters/SQL.d.ts → SQL.d.ts} +0 -0
  464. /package/dist/{api/codec.d.ts → codec.d.ts} +0 -0
  465. /package/dist/{api/internal → internal}/health.d.ts +0 -0
  466. /package/dist/{api/middlewares.d.ts → middlewares.d.ts} +0 -0
  467. /package/dist/{api/routing → routing}/schema/jwt.d.ts +0 -0
  468. /package/dist/{api/routing → routing}/tsort.d.ts +0 -0
  469. /package/dist/{api/util.d.ts → util.d.ts} +0 -0
  470. /package/src/{api/codec.ts → codec.ts} +0 -0
  471. /package/src/{api/internal → internal}/health.ts +0 -0
  472. /package/src/{api/routing → routing}/tsort.ts +0 -0
  473. /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, type ProjectableFromDomain, 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,470 @@ 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)
556
+ )
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)
545
583
  )
546
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
+ // @ts-expect-error missingField is neither an encoded repo field nor a computed projection
666
+ projectComputed(S.Struct({ missingField: S.String }), computed({}))
667
+ )
668
+
669
+ // Key presence only (not Encoded value types) — name: number is still projectable.
670
+ make<Encoded>().pipe(
671
+ projectComputed(
672
+ S.Struct({ name: S.Number }),
673
+ computed({})
674
+ )
675
+ )
676
+
677
+ // Computed field *value* types are not yet constrained against the expression
678
+ // result (only key presence). Keeping this as a documentation call site.
679
+ make<Encoded>().pipe(
680
+ projectComputed(
681
+ S.Struct({ itemCount: S.String }),
682
+ computed({ itemCount: relation<Encoded>("items").count() })
683
+ )
684
+ )
685
+
686
+ make<Encoded>().pipe(
687
+ projectComputed(
688
+ S.Struct({ itemCount: S.Number }),
689
+ // @ts-expect-error extra computed keys must exist in the projection schema
690
+ computed({
691
+ itemCount: relation<Encoded>("items").count(),
692
+ extraCount: relation<Encoded>("items").count()
693
+ })
694
+ )
695
+ )
696
+ })
697
+
698
+ it("projectComputed supports union projection schemas", () => {
699
+ const baseSchema = S.Union([
700
+ S.Struct({
701
+ _tag: S.Literal("open"),
702
+ id: S.String,
703
+ items: S.Array(S.Struct({ articleId: S.String, weight: S.Number }))
704
+ }),
705
+ S.Struct({
706
+ _tag: S.Literal("closed"),
707
+ id: S.String,
708
+ closedAt: S.String
709
+ })
710
+ ])
711
+ type Encoded = S.Codec.Encoded<typeof baseSchema>
712
+ const query = make<Encoded>().pipe(
713
+ projectComputed(
714
+ S.Union([
715
+ S.Struct({
716
+ _tag: S.Literal("open"),
717
+ id: S.String,
718
+ articleCount: S.Number,
719
+ weight: S.Number,
720
+ articleIds: S.Array(S.String)
721
+ }),
722
+ S.Struct({
723
+ _tag: S.Literal("closed"),
724
+ id: S.String,
725
+ closedAt: S.String
726
+ })
727
+ ]),
728
+ computed({
729
+ articleCount: relation<Encoded>("items").count(),
730
+ weight: relation<Encoded>("items").sum("weight"),
731
+ articleIds: relation<Encoded>("items").collect("articleId")
732
+ })
733
+ )
734
+ )
735
+
736
+ const interpreted = toFilter(query, baseSchema)
737
+ expect(interpreted.computed && Object.keys(interpreted.computed).toSorted()).toEqual([
738
+ "articleCount",
739
+ "articleIds",
740
+ "weight"
741
+ ])
742
+ })
743
+
744
+ /**
745
+ * MACS-SCANNER-API-KS class of bug: domain cancel omits pack/print ownership
746
+ * (`activeRequest`); a hand-built overview projection that still requires it on
747
+ * every branch must fail at the `projectComputed` call site, not in prod decode.
748
+ */
749
+ it("projectComputed rejects state-owned fields required on the wrong tagged branch", () => {
750
+ const domain = S.Union([
751
+ S.Struct({
752
+ _tag: S.Literal("packing"),
753
+ id: S.String,
754
+ activeRequest: S.NullOr(S.String),
755
+ items: S.Array(S.Struct({ articleId: S.String }))
756
+ }),
757
+ S.Struct({
758
+ _tag: S.Literal("cancelled"),
759
+ id: S.String,
760
+ items: S.Array(S.Struct({ articleId: S.String }))
761
+ })
762
+ ])
763
+ type DomainEnc = S.Codec.Encoded<typeof domain>
764
+
765
+ // Good: activeRequest only on packing; cancel omits it; articleCount is computed.
766
+ make<DomainEnc>().pipe(
767
+ projectComputed(
768
+ S.Union([
769
+ S.Struct({
770
+ _tag: S.Literal("packing"),
771
+ id: S.String,
772
+ activeRequest: S.NullOr(S.String),
773
+ articleCount: S.Number
774
+ }),
775
+ S.Struct({
776
+ _tag: S.Literal("cancelled"),
777
+ id: S.String,
778
+ articleCount: S.Number
779
+ })
780
+ ]),
781
+ computed({
782
+ articleCount: relation<DomainEnc>("items").count()
783
+ })
784
+ )
785
+ )
786
+
787
+ make<DomainEnc>().pipe(
788
+ projectComputed(
789
+ S.Union([
790
+ S.Struct({
791
+ _tag: S.Literal("packing"),
792
+ id: S.String,
793
+ activeRequest: S.NullOr(S.String),
794
+ articleCount: S.Number
795
+ }),
796
+ S.Struct({
797
+ _tag: S.Literal("cancelled"),
798
+ id: S.String,
799
+ activeRequest: S.NullOr(S.String),
800
+ articleCount: S.Number
801
+ })
802
+ ]),
803
+ // @ts-expect-error activeRequest is not on domain cancelled — cannot project it there
804
+ computed({
805
+ articleCount: relation<DomainEnc>("items").count()
806
+ })
807
+ )
808
+ )
809
+ })
810
+
811
+ it("ProjectableFromDomain distributes over tagged union Encoded", () => {
812
+ type DomainEnc =
813
+ | { readonly _tag: "packing"; readonly id: string; readonly activeRequest: string | null }
814
+ | { readonly _tag: "cancelled"; readonly id: string }
815
+
816
+ type Good =
817
+ | { readonly _tag: "packing"; readonly id: string; readonly activeRequest: string | null; readonly n: number }
818
+ | { readonly _tag: "cancelled"; readonly id: string; readonly n: number }
819
+
820
+ type Bad =
821
+ | { readonly _tag: "packing"; readonly id: string; readonly activeRequest: string | null }
822
+ | { readonly _tag: "cancelled"; readonly id: string; readonly activeRequest: string | null }
823
+
824
+ type GoodCheck = ProjectableFromDomain<Good, DomainEnc, "n">
825
+ type BadCheck = ProjectableFromDomain<Bad, DomainEnc>
826
+
827
+ const _good: GoodCheck = undefined as unknown
828
+ // @ts-expect-error cancelled branch requires activeRequest not present on domain cancelled
829
+ const _bad: BadCheck = undefined as unknown
830
+ void _good
831
+ void _bad
832
+ })
833
+
834
+ it("ProjectableFromDomain allows dual same-tag domain variants", () => {
835
+ // EasyLife packing/closed: two domain shapes share `_tag` with different fields.
836
+ type DomainEnc =
837
+ | { readonly _tag: "packing"; readonly id: string; readonly packages: readonly string[] }
838
+ | { readonly _tag: "packing"; readonly id: string; readonly units: readonly string[] }
839
+ | { readonly _tag: "initial"; readonly id: string }
840
+
841
+ type Good =
842
+ | { readonly _tag: "packing"; readonly id: string; readonly packages: readonly string[] }
843
+ | { readonly _tag: "packing"; readonly id: string; readonly units: readonly string[] }
844
+
845
+ // Flat multi-tag + state-only field must still fail (batchId not on initial).
846
+ type BadFlat = {
847
+ readonly _tag: "packing" | "initial"
848
+ readonly id: string
849
+ readonly packages: readonly string[]
850
+ }
851
+
852
+ type GoodCheck = ProjectableFromDomain<Good, DomainEnc>
853
+ type BadFlatCheck = ProjectableFromDomain<BadFlat, DomainEnc>
854
+
855
+ const _good: GoodCheck = undefined as unknown
856
+ // @ts-expect-error packages is not on domain initial; multi-tag flat intersection rejects it
857
+ const _badFlat: BadFlatCheck = undefined as unknown
858
+ void _good
859
+ void _badFlat
860
+ })
861
+
862
+ it("projection schema with computed fields fails without computed map", () => {
863
+ const baseSchema = S.Struct({
864
+ id: S.String,
865
+ items: S.Array(S.Struct({ value: S.Number }))
866
+ })
867
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
868
+ // @ts-expect-error missing computed keys are rejected; also not projectable from domain alone
869
+ projectComputed(S.Struct({ pickedCount: S.NonNegativeInt }), computed({}))
870
+ )
871
+ expect(() => toFilter(query, baseSchema)).toThrowError("Missing computed projections for schema keys")
872
+ })
873
+
874
+ it("projectComputed.every emits relation-every IR", () => {
875
+ const baseSchema = S.Struct({
876
+ id: S.String,
877
+ items: S.Array(S.Struct({ state: S.Struct({ _tag: S.String }) }))
878
+ })
879
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
880
+ projectComputed(
881
+ S.Struct({ allPicked: S.Boolean }),
882
+ computed({
883
+ allPicked: relation<S.Codec.Encoded<typeof baseSchema>>("items").every(where("state._tag", "Picked"))
884
+ })
885
+ )
886
+ )
887
+ const interpreted = toFilter(query, baseSchema)
888
+ expect(interpreted.computed?.["allPicked"]?._tag).toBe("relation-every")
889
+ expect(interpreted.computed?.["allPicked"]?.path).toBe("items")
890
+ expect((interpreted.computed?.["allPicked"] as { filter: unknown } | undefined)?.filter).toEqual([
891
+ { t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
892
+ ])
893
+ })
894
+
895
+ it("projectComputed.distinctCount emits relation-distinct-count IR with field", () => {
896
+ const baseSchema = S.Struct({
897
+ id: S.String,
898
+ items: S.Array(S.Struct({ rowId: S.String, state: S.Struct({ _tag: S.String }) }))
899
+ })
900
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
901
+ projectComputed(
902
+ S.Struct({ positionCount: S.NonNegativeInt }),
903
+ computed({
904
+ positionCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").distinctCount(
905
+ "rowId",
906
+ where("state._tag", "neq", "cancelled")
907
+ )
908
+ })
909
+ )
910
+ )
911
+ const interpreted = toFilter(query, baseSchema)
912
+ const ir = interpreted.computed?.["positionCount"]
913
+ expect(ir?._tag).toBe("relation-distinct-count")
914
+ expect((ir as { field: string } | undefined)?.field).toBe("rowId")
915
+ expect((ir as { filter: unknown } | undefined)?.filter).toEqual([
916
+ { t: "where", path: "items.-1.state._tag", op: "neq", value: "cancelled" }
917
+ ])
918
+ })
919
+
920
+ it("projectComputed.sum emits relation-sum IR with field", () => {
921
+ const baseSchema = S.Struct({
922
+ id: S.String,
923
+ items: S.Array(S.Struct({ weight: S.Number }))
924
+ })
925
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
926
+ projectComputed(
927
+ S.Struct({ totalWeight: S.Number }),
928
+ computed({ totalWeight: relation<S.Codec.Encoded<typeof baseSchema>>("items").sum("weight") })
929
+ )
930
+ )
931
+ const interpreted = toFilter(query, baseSchema)
932
+ const ir = interpreted.computed?.["totalWeight"]
933
+ expect(ir?._tag).toBe("relation-sum")
934
+ expect((ir as { field: string } | undefined)?.field).toBe("weight")
935
+ expect((ir as { filter: unknown } | undefined)?.filter).toEqual([])
936
+ })
937
+
938
+ it("projectComputed.collect / collectDistinct emit relation-collect IR", () => {
939
+ const baseSchema = S.Struct({
940
+ id: S.String,
941
+ items: S.Array(S.Struct({ articleId: S.String }))
942
+ })
943
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
944
+ projectComputed(
945
+ S.Struct({
946
+ all: S.Array(S.String),
947
+ distinct: S.Array(S.String)
948
+ }),
949
+ computed({
950
+ all: relation<S.Codec.Encoded<typeof baseSchema>>("items").collect("articleId"),
951
+ distinct: relation<S.Codec.Encoded<typeof baseSchema>>("items").collectDistinct("articleId")
952
+ })
953
+ )
954
+ )
955
+ const interpreted = toFilter(query, baseSchema)
956
+ const all = interpreted.computed?.["all"]
957
+ const distinct = interpreted.computed?.["distinct"]
958
+ expect(all?._tag).toBe("relation-collect")
959
+ expect((all as { distinct: boolean } | undefined)?.distinct).toBe(false)
960
+ expect(distinct?._tag).toBe("relation-collect")
961
+ expect((distinct as { distinct: boolean } | undefined)?.distinct).toBe(true)
962
+ })
963
+
964
+ it("projectComputed.sumExpr emits relation-sum-expr IR", () => {
965
+ const baseSchema = S.Struct({
966
+ id: S.String,
967
+ items: S.Array(S.Struct({
968
+ weight: S.Number,
969
+ tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
970
+ }))
971
+ })
972
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
973
+ projectComputed(
974
+ S.Struct({ total: S.Number }),
975
+ computed({
976
+ total: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExpr(
977
+ expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
978
+ )
979
+ })
980
+ )
981
+ )
982
+ const interpreted = toFilter(query, baseSchema)
983
+ const ir = interpreted.computed?.["total"]
984
+ expect(ir?._tag).toBe("relation-sum-expr")
985
+ expect((ir as { expression: unknown } | undefined)?.expression).toEqual({
986
+ _tag: "mul",
987
+ left: { _tag: "field", field: "weight" },
988
+ right: { _tag: "field", field: "tradeUnit.amount" }
989
+ })
990
+ })
991
+
992
+ it("projectComputed.sumExprBy emits relation-sum-expr-by IR", () => {
993
+ const baseSchema = S.Struct({
994
+ id: S.String,
995
+ items: S.Array(S.Struct({
996
+ weight: S.Number,
997
+ tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
998
+ }))
999
+ })
1000
+ const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
1001
+ projectComputed(
1002
+ S.Struct({
1003
+ totals: S.Array(S.Struct({ unit: S.String, total: S.Number }))
1004
+ }),
1005
+ computed({
1006
+ totals: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExprBy(
1007
+ expr.mul(expr.field("weight"), expr.field("tradeUnit.amount")),
1008
+ { unit: "tradeUnit.unit" }
1009
+ )
1010
+ })
1011
+ )
1012
+ )
1013
+ const interpreted = toFilter(query, baseSchema)
1014
+ const ir = interpreted.computed?.["totals"]
1015
+ expect(ir?._tag).toBe("relation-sum-expr-by")
1016
+ expect((ir as { unit: string } | undefined)?.unit).toBe("tradeUnit.unit")
1017
+ })
1018
+
547
1019
  it(
548
1020
  "doesn't mess when refining fields",
549
1021
  () =>
@@ -551,7 +1023,7 @@ it(
551
1023
  .gen(function*() {
552
1024
  const schema = S.Struct({
553
1025
  id: S.String,
554
- literals: S.Literal("a", "b", "c")
1026
+ literals: S.Literals(["a", "b", "c"])
555
1027
  })
556
1028
 
557
1029
  type Schema = typeof schema.Type
@@ -571,7 +1043,7 @@ it(
571
1043
 
572
1044
  expect(result).toEqual([])
573
1045
  })
574
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
1046
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
575
1047
  )
576
1048
 
577
1049
  it(
@@ -581,7 +1053,7 @@ it(
581
1053
  .gen(function*() {
582
1054
  const schema = S.Struct({
583
1055
  id: S.String,
584
- literals: S.Union([S.Literal("a", "b", "c"), S.Null])
1056
+ literals: S.Union([S.Literals(["a", "b", "c"]), S.Null])
585
1057
  })
586
1058
 
587
1059
  type Schema = typeof schema.Type
@@ -615,7 +1087,7 @@ it(
615
1087
 
616
1088
  expect(result).toEqual([])
617
1089
  })
618
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
1090
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
619
1091
  )
620
1092
 
621
1093
  it(
@@ -659,7 +1131,7 @@ it(
659
1131
 
660
1132
  expect(result).toEqual([])
661
1133
  })
662
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise)
1134
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
663
1135
  )
664
1136
 
665
1137
  it("remove null from one constituent of a tagged union", () =>
@@ -672,7 +1144,7 @@ it("remove null from one constituent of a tagged union", () =>
672
1144
 
673
1145
  class BB extends S.Class<BB>("BB")({
674
1146
  id: S.Literal("BB"),
675
- b: S.NullOr(S.Number)
1147
+ b: S.NullOr(S.Finite)
676
1148
  }) {}
677
1149
 
678
1150
  type Union = AA | BB
@@ -708,7 +1180,7 @@ it("remove null from one constituent of a tagged union", () =>
708
1180
  })[]
709
1181
  >()
710
1182
  })
711
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1183
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
712
1184
 
713
1185
  it("refine 3", () =>
714
1186
  Effect
@@ -746,7 +1218,7 @@ it("refine 3", () =>
746
1218
  const resQuer1 = yield* repo.query(where("id", "AA"))
747
1219
  expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
748
1220
  })
749
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1221
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
750
1222
 
751
1223
  it("my test", () =>
752
1224
  Effect
@@ -764,7 +1236,7 @@ it("my test", () =>
764
1236
  )
765
1237
  expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
766
1238
  })
767
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1239
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
768
1240
 
769
1241
  it("refine inner without imposing a projection", () =>
770
1242
  Effect
@@ -808,7 +1280,7 @@ it("refine inner without imposing a projection", () =>
808
1280
  where("union._tag", "AA"),
809
1281
  // But if I wanna the whole Data as output ignoring the inner refinement
810
1282
  // I wanna be able to do so
811
- project(S.Struct(Data.pick("union")))
1283
+ project(Data.mapFields(Struct.pick(["union"])))
812
1284
  )
813
1285
 
814
1286
  expectTypeOf(query2).toEqualTypeOf<
@@ -839,7 +1311,7 @@ it("refine inner without imposing a projection", () =>
839
1311
  }[]
840
1312
  >()
841
1313
  })
842
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1314
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
843
1315
 
844
1316
  it("does not allow string queries on arrays", () =>
845
1317
  Effect
@@ -874,7 +1346,7 @@ it("does not allow string queries on arrays", () =>
874
1346
  expectTypeOf(good3).toEqualTypeOf<QueryWhere<Some, Some>>()
875
1347
  expectTypeOf(good4).toEqualTypeOf<QueryWhere<Some, Some>>()
876
1348
  })
877
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1349
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
878
1350
 
879
1351
  it("test array.length", () =>
880
1352
  Effect
@@ -915,7 +1387,7 @@ it("test array.length", () =>
915
1387
  QueryWhere<Something, Something>
916
1388
  >()
917
1389
  })
918
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1390
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
919
1391
 
920
1392
  it("distribution over union", () =>
921
1393
  Effect
@@ -939,7 +1411,7 @@ it("distribution over union", () =>
939
1411
  })[]
940
1412
  >()
941
1413
  })
942
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1414
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
943
1415
 
944
1416
  it("refine nested union", () =>
945
1417
  Effect
@@ -980,7 +1452,158 @@ it("refine nested union", () =>
980
1452
  }[]
981
1453
  >()
982
1454
  })
983
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1455
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1456
+
1457
+ it("find with transformed id", () =>
1458
+ Effect
1459
+ .gen(function*() {
1460
+ const ConfiguratorId = S.NonEmptyString255
1461
+
1462
+ class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
1463
+ configuratorId: ConfiguratorId,
1464
+ label: S.NonEmptyString50
1465
+ }) {}
1466
+
1467
+ const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
1468
+ S.decodeTo(
1469
+ S.toType(PreconfigurationId),
1470
+ SchemaTransformation.transformOrFail({
1471
+ decode: Effect.fnUntraced(function*(value) {
1472
+ const values = value.split("_")
1473
+ const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
1474
+ const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
1475
+ values.join("_")
1476
+ )
1477
+ return new PreconfigurationId({ configuratorId, label })
1478
+ }),
1479
+ encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
1480
+ })
1481
+ ),
1482
+ S.revealCodec
1483
+ )
1484
+
1485
+ const Preconfiguration = S.Struct({
1486
+ id: PreconfigurationIdFromString,
1487
+ name: S.String
1488
+ })
1489
+
1490
+ const repo = yield* makeRepo("Preconfiguration", Preconfiguration, { idKey: "id" as const })
1491
+
1492
+ const id = new PreconfigurationId({
1493
+ configuratorId: S.NonEmptyString255("myConfigurator"),
1494
+ label: S.NonEmptyString50("myLabel")
1495
+ })
1496
+ const item = { id, name: "test preconfig" }
1497
+
1498
+ yield* repo.saveAndPublish([item])
1499
+
1500
+ const found = yield* repo.find(id)
1501
+ expect(Option.isSome(found)).toBe(true)
1502
+ expect(Option.getOrThrow(found).name).toBe("test preconfig")
1503
+ expect(Option.getOrThrow(found).id).toEqual(id)
1504
+
1505
+ const notFound = yield* repo.find(
1506
+ new PreconfigurationId({
1507
+ configuratorId: S.NonEmptyString255("other"),
1508
+ label: S.NonEmptyString50("nope")
1509
+ })
1510
+ )
1511
+ expect(Option.isNone(notFound)).toBe(true)
1512
+ })
1513
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1514
+
1515
+ it("find with transformed id in tagged union", () =>
1516
+ Effect
1517
+ .gen(function*() {
1518
+ const ConfiguratorId = S.NonEmptyString255
1519
+
1520
+ class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
1521
+ configuratorId: ConfiguratorId,
1522
+ label: S.NonEmptyString50
1523
+ }) {}
1524
+
1525
+ const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
1526
+ S.decodeTo(
1527
+ S.toType(PreconfigurationId),
1528
+ SchemaTransformation.transformOrFail({
1529
+ decode: Effect.fnUntraced(function*(value) {
1530
+ const values = value.split("_")
1531
+ const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
1532
+ const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
1533
+ values.join("_")
1534
+ )
1535
+ return new PreconfigurationId({ configuratorId, label })
1536
+ }),
1537
+ encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
1538
+ })
1539
+ ),
1540
+ S.revealCodec
1541
+ )
1542
+
1543
+ class Draft extends S.TaggedClass<Draft>()("Draft", {
1544
+ id: PreconfigurationIdFromString,
1545
+ name: S.String
1546
+ }) {}
1547
+
1548
+ class Published extends S.TaggedClass<Published>()("Published", {
1549
+ id: PreconfigurationIdFromString,
1550
+ name: S.String,
1551
+ publishedAt: S.String
1552
+ }) {}
1553
+
1554
+ class Archived extends S.TaggedClass<Archived>()("Archived", {
1555
+ id: PreconfigurationIdFromString,
1556
+ name: S.String,
1557
+ archivedAt: S.String
1558
+ }) {}
1559
+
1560
+ const Preconfiguration = S.Union([Draft, Published, Archived])
1561
+
1562
+ const repo = yield* makeRepo("Preconfiguration", Preconfiguration, {})
1563
+
1564
+ const id1 = new PreconfigurationId({
1565
+ configuratorId: S.NonEmptyString255("conf1"),
1566
+ label: S.NonEmptyString50("draft1")
1567
+ })
1568
+ const id2 = new PreconfigurationId({
1569
+ configuratorId: S.NonEmptyString255("conf2"),
1570
+ label: S.NonEmptyString50("pub1")
1571
+ })
1572
+ const id3 = new PreconfigurationId({
1573
+ configuratorId: S.NonEmptyString255("conf3"),
1574
+ label: S.NonEmptyString50("arch1")
1575
+ })
1576
+
1577
+ const draft = new Draft({ id: id1, name: "my draft" })
1578
+ const published = new Published({ id: id2, name: "my published", publishedAt: "2024-01-01" })
1579
+ const archived = new Archived({ id: id3, name: "my archived", archivedAt: "2024-06-01" })
1580
+
1581
+ yield* repo.saveAndPublish([draft, published, archived])
1582
+
1583
+ // find each by their PreconfigurationId instance
1584
+ const foundDraft = yield* repo.find(id1)
1585
+ expect(Option.isSome(foundDraft)).toBe(true)
1586
+ expect(Option.getOrThrow(foundDraft)._tag).toBe("Draft")
1587
+ expect(Option.getOrThrow(foundDraft).name).toBe("my draft")
1588
+
1589
+ const foundPublished = yield* repo.find(id2)
1590
+ expect(Option.isSome(foundPublished)).toBe(true)
1591
+ expect(Option.getOrThrow(foundPublished)._tag).toBe("Published")
1592
+
1593
+ const foundArchived = yield* repo.find(id3)
1594
+ expect(Option.isSome(foundArchived)).toBe(true)
1595
+ expect(Option.getOrThrow(foundArchived)._tag).toBe("Archived")
1596
+
1597
+ // not found
1598
+ const notFound = yield* repo.find(
1599
+ new PreconfigurationId({
1600
+ configuratorId: S.NonEmptyString255("nope"),
1601
+ label: S.NonEmptyString50("nope")
1602
+ })
1603
+ )
1604
+ expect(Option.isNone(notFound)).toBe(true)
1605
+ })
1606
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
984
1607
 
985
1608
  it("refine union with nested union", () =>
986
1609
  Effect
@@ -1095,4 +1718,470 @@ it("refine union with nested union", () =>
1095
1718
  })[]
1096
1719
  >()
1097
1720
  })
1098
- .pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
1721
+ .pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
1722
+
1723
+ // ---------------------------------------------------------------------------
1724
+ // memFilter: computed projection execution (in-memory) and code filter coverage
1725
+ // ---------------------------------------------------------------------------
1726
+
1727
+ const computedBaseSchema = S.Struct({
1728
+ id: S.String,
1729
+ status: S.Literals(["active", "archived"]),
1730
+ items: S.Array(S.Struct({
1731
+ id: S.String,
1732
+ tag: S.Literals(["a", "b", "c"]),
1733
+ qty: S.Finite,
1734
+ note: S.String
1735
+ }))
1736
+ })
1737
+ type ComputedBase = S.Codec.Encoded<typeof computedBaseSchema>
1738
+
1739
+ const computedRows: ComputedBase[] = [
1740
+ {
1741
+ id: "r1",
1742
+ status: "active",
1743
+ items: [
1744
+ { id: "i1", tag: "a", qty: 10, note: "alpha" },
1745
+ { id: "i2", tag: "a", qty: 20, note: "alpha" },
1746
+ { id: "i3", tag: "b", qty: 5, note: "beta" }
1747
+ ]
1748
+ },
1749
+ { id: "r2", status: "active", items: [] },
1750
+ {
1751
+ id: "r3",
1752
+ status: "archived",
1753
+ items: [
1754
+ { id: "i4", tag: "b", qty: 7, note: "gamma" },
1755
+ { id: "i5", tag: "b", qty: 7, note: "gamma" },
1756
+ { id: "i6", tag: "c", qty: 3, note: "delta" }
1757
+ ]
1758
+ }
1759
+ ]
1760
+
1761
+ it("memFilter: relation-count with filter", () => {
1762
+ const q = make<ComputedBase>().pipe(
1763
+ projectComputed(
1764
+ S.Struct({ id: S.String, aCount: S.NonNegativeInt }),
1765
+ computed({
1766
+ aCount: relation<ComputedBase>("items").count(where("tag", "a"))
1767
+ })
1768
+ )
1769
+ )
1770
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1771
+ { id: "r1", aCount: 2 },
1772
+ { id: "r2", aCount: 0 },
1773
+ { id: "r3", aCount: 0 }
1774
+ ])
1775
+ })
1776
+
1777
+ it("memFilter: relation-any / every with filter", () => {
1778
+ const q = make<ComputedBase>().pipe(
1779
+ projectComputed(
1780
+ S.Struct({
1781
+ id: S.String,
1782
+ hasA: S.Boolean,
1783
+ allB: S.Boolean
1784
+ }),
1785
+ computed({
1786
+ hasA: relation<ComputedBase>("items").any(where("tag", "a")),
1787
+ allB: relation<ComputedBase>("items").every(where("tag", "b"))
1788
+ })
1789
+ )
1790
+ )
1791
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1792
+ { id: "r1", hasA: true, allB: false },
1793
+ // empty array: any → false, every → true (JS Array.every on [] is true)
1794
+ { id: "r2", hasA: false, allB: true },
1795
+ { id: "r3", hasA: false, allB: false }
1796
+ ])
1797
+ })
1798
+
1799
+ it("memFilter: relation-distinct-count with filter", () => {
1800
+ const q = make<ComputedBase>().pipe(
1801
+ projectComputed(
1802
+ S.Struct({ id: S.String, distinctNotes: S.NonNegativeInt }),
1803
+ computed({
1804
+ distinctNotes: relation<ComputedBase>("items").distinctCount("note", where("tag", "neq", "c"))
1805
+ })
1806
+ )
1807
+ )
1808
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1809
+ { id: "r1", distinctNotes: 2 }, // alpha, beta
1810
+ { id: "r2", distinctNotes: 0 },
1811
+ { id: "r3", distinctNotes: 1 } // gamma (delta filtered out)
1812
+ ])
1813
+ })
1814
+
1815
+ it("memFilter: relation-sum with filter", () => {
1816
+ const q = make<ComputedBase>().pipe(
1817
+ projectComputed(
1818
+ S.Struct({ id: S.String, totalQty: S.Finite }),
1819
+ computed({
1820
+ totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
1821
+ })
1822
+ )
1823
+ )
1824
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1825
+ { id: "r1", totalQty: 35 },
1826
+ { id: "r2", totalQty: 0 },
1827
+ { id: "r3", totalQty: 14 }
1828
+ ])
1829
+ })
1830
+
1831
+ it("memFilter: relation-collect / collectDistinct with filter", () => {
1832
+ const q = make<ComputedBase>().pipe(
1833
+ projectComputed(
1834
+ S.Struct({
1835
+ id: S.String,
1836
+ notes: S.Array(S.String),
1837
+ distinctNotes: S.Array(S.String)
1838
+ }),
1839
+ computed({
1840
+ notes: relation<ComputedBase>("items").collect("note", where("tag", "neq", "c")),
1841
+ distinctNotes: relation<ComputedBase>("items").collectDistinct("note", where("tag", "neq", "c"))
1842
+ })
1843
+ )
1844
+ )
1845
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1846
+ { id: "r1", notes: ["alpha", "alpha", "beta"], distinctNotes: ["alpha", "beta"] },
1847
+ { id: "r2", notes: [], distinctNotes: [] },
1848
+ { id: "r3", notes: ["gamma", "gamma"], distinctNotes: ["gamma"] }
1849
+ ])
1850
+ })
1851
+
1852
+ it("memFilter: computed projection with multi-statement relation filter", () => {
1853
+ const q = make<ComputedBase>().pipe(
1854
+ projectComputed(
1855
+ S.Struct({ id: S.String, hits: S.NonNegativeInt }),
1856
+ computed({
1857
+ hits: relation<ComputedBase>("items").count(
1858
+ flow(
1859
+ where("tag", "a"),
1860
+ and("qty", "gt", 10)
1861
+ )
1862
+ )
1863
+ })
1864
+ )
1865
+ )
1866
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1867
+ { id: "r1", hits: 1 }, // only i2 (a, qty 20)
1868
+ { id: "r2", hits: 0 },
1869
+ { id: "r3", hits: 0 }
1870
+ ])
1871
+ })
1872
+
1873
+ it("memFilter: relation-sum-expr / sum-expr-by / sum-expr-normalized", () => {
1874
+ const schema = S.Struct({
1875
+ id: S.String,
1876
+ items: S.Array(S.Struct({
1877
+ weight: S.Finite,
1878
+ tradeUnit: S.Struct({ amount: S.Finite, unit: S.String })
1879
+ }))
1880
+ })
1881
+ type Row = S.Codec.Encoded<typeof schema>
1882
+ const rows: Row[] = [
1883
+ {
1884
+ id: "r1",
1885
+ items: [
1886
+ { weight: 2, tradeUnit: { amount: 5, unit: "kg" } },
1887
+ { weight: 4, tradeUnit: { amount: 1000, unit: "g" } },
1888
+ { weight: 3, tradeUnit: { amount: 1, unit: "kg" } }
1889
+ ]
1890
+ },
1891
+ { id: "r2", items: [] }
1892
+ ]
1893
+ const weighted = expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
1894
+ const q = make<Row>().pipe(
1895
+ projectComputed(
1896
+ S.Struct({
1897
+ id: S.String,
1898
+ totalRaw: S.Finite,
1899
+ totalsByUnit: S.Array(S.Struct({ unit: S.String, total: S.Finite })),
1900
+ totalKg: S.Finite
1901
+ }),
1902
+ computed({
1903
+ totalRaw: relation<Row>("items").sumExpr(weighted, where("weight", "gte", 0)),
1904
+ totalsByUnit: relation<Row>("items").sumExprBy(weighted, { unit: "tradeUnit.unit" }, where("weight", "gte", 0)),
1905
+ totalKg: relation<Row>("items").sumExprNormalized(weighted, {
1906
+ unit: "tradeUnit.unit",
1907
+ toBase: "kg",
1908
+ factors: { g: 0.001 }
1909
+ }, where("weight", "gte", 0))
1910
+ })
1911
+ )
1912
+ )
1913
+ expect(memFilter(toFilter(q, schema))(rows)).toEqual([
1914
+ {
1915
+ id: "r1",
1916
+ totalRaw: 4013,
1917
+ totalsByUnit: [{ unit: "kg", total: 13 }, { unit: "g", total: 4000 }],
1918
+ totalKg: 17
1919
+ },
1920
+ { id: "r2", totalRaw: 0, totalsByUnit: [], totalKg: 0 }
1921
+ ])
1922
+ })
1923
+
1924
+ it("memFilter: computed projection combined with root where filter", () => {
1925
+ const q = make<ComputedBase>().pipe(
1926
+ where("id", "neq", "r3"),
1927
+ projectComputed(
1928
+ S.Struct({ id: S.String, totalQty: S.Finite }),
1929
+ computed({
1930
+ totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
1931
+ })
1932
+ )
1933
+ )
1934
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1935
+ { id: "r1", totalQty: 35 },
1936
+ { id: "r2", totalQty: 0 }
1937
+ ])
1938
+ })
1939
+
1940
+ it("memFilter: computed projection with order/limit/skip applied to base rows", () => {
1941
+ const q = make<ComputedBase>().pipe(
1942
+ order("id", "DESC"),
1943
+ page({ skip: 1, take: 1 }),
1944
+ projectComputed(
1945
+ S.Struct({ id: S.String, total: S.NonNegativeInt }),
1946
+ computed({
1947
+ total: relation<ComputedBase>("items").count(where("qty", "gte", 0))
1948
+ })
1949
+ )
1950
+ )
1951
+ expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
1952
+ { id: "r2", total: 0 }
1953
+ ])
1954
+ })
1955
+
1956
+ it("memFilter: computed projection - relation missing on row returns empty value", () => {
1957
+ const partial: ComputedBase[] = [
1958
+ { id: "x1" } as ComputedBase,
1959
+ { id: "x2", status: "active", items: undefined as unknown as ComputedBase["items"] }
1960
+ ]
1961
+ const q = make<ComputedBase>().pipe(
1962
+ projectComputed(
1963
+ S.Struct({
1964
+ id: S.String,
1965
+ c: S.NonNegativeInt,
1966
+ s: S.Finite,
1967
+ any_: S.Boolean,
1968
+ every_: S.Boolean,
1969
+ coll: S.Array(S.String)
1970
+ }),
1971
+ computed({
1972
+ c: relation<ComputedBase>("items").count(where("tag", "a")),
1973
+ s: relation<ComputedBase>("items").sum("qty", where("tag", "a")),
1974
+ any_: relation<ComputedBase>("items").any(where("tag", "a")),
1975
+ every_: relation<ComputedBase>("items").every(where("tag", "a")),
1976
+ coll: relation<ComputedBase>("items").collect("note", where("tag", "a"))
1977
+ })
1978
+ )
1979
+ )
1980
+ expect(memFilter(toFilter(q, computedBaseSchema))(partial)).toEqual([
1981
+ { id: "x1", c: 0, s: 0, any_: false, every_: true, coll: [] },
1982
+ { id: "x2", c: 0, s: 0, any_: false, every_: true, coll: [] }
1983
+ ])
1984
+ })
1985
+
1986
+ it("memFilter: rejects extra computed keys not in projection schema", () => {
1987
+ const q = make<ComputedBase>().pipe(
1988
+ projectComputed(
1989
+ S.Struct({ id: S.String }),
1990
+ // @ts-expect-error extra computed keys are rejected statically; keep runtime guard covered
1991
+ computed({
1992
+ bogus: relation<ComputedBase>("items").count(where("tag", "a"))
1993
+ })
1994
+ )
1995
+ )
1996
+ expect(() => toFilter(q, computedBaseSchema)).toThrowError(
1997
+ "Computed projection keys must exist in projection schema"
1998
+ )
1999
+ })
2000
+
2001
+ // ---------------------------------------------------------------------------
2002
+ // memFilter: code filter (where/and/or/scopes) execution coverage
2003
+ // ---------------------------------------------------------------------------
2004
+
2005
+ type CFRow = {
2006
+ readonly id: string
2007
+ readonly tag: "x" | "y" | "z"
2008
+ readonly qty: number
2009
+ readonly desc: string
2010
+ readonly tags: ReadonlyArray<string>
2011
+ readonly nested: { readonly kind: "k1" | "k2"; readonly v: number }
2012
+ }
2013
+
2014
+ const cfRows: CFRow[] = [
2015
+ { id: "1", tag: "x", qty: 10, desc: "Hello World", tags: ["red", "green"], nested: { kind: "k1", v: 1 } },
2016
+ { id: "2", tag: "y", qty: 20, desc: "Goodbye", tags: ["blue"], nested: { kind: "k2", v: 5 } },
2017
+ { id: "3", tag: "z", qty: 0, desc: "Hello again", tags: ["red", "blue", "green"], nested: { kind: "k1", v: 9 } },
2018
+ { id: "4", tag: "y", qty: 30, desc: "World cup", tags: [], nested: { kind: "k2", v: 0 } }
2019
+ ]
2020
+
2021
+ const runCF = (q: any) => (memFilter(toFilter(q))(cfRows) as unknown as readonly CFRow[]).map((_) => _.id)
2022
+
2023
+ it("codeFilter: where + and chain", () => {
2024
+ const q = make<CFRow>().pipe(
2025
+ where("tag", "y"),
2026
+ and("qty", "gt", 25)
2027
+ )
2028
+ expect(runCF(q)).toEqual(["4"])
2029
+ })
2030
+
2031
+ it("codeFilter: where + or chain", () => {
2032
+ const q = make<CFRow>().pipe(
2033
+ where("tag", "x"),
2034
+ or("tag", "z")
2035
+ )
2036
+ expect(runCF(q).sort()).toEqual(["1", "3"])
2037
+ })
2038
+
2039
+ it("codeFilter: nested scope precedence (a AND (b OR c))", () => {
2040
+ const q = make<CFRow>().pipe(
2041
+ where("tag", "y"),
2042
+ and(
2043
+ where("qty", "gt", 25),
2044
+ or("desc", "contains", "good")
2045
+ )
2046
+ )
2047
+ // tag=y AND (qty>25 OR desc contains "good") → row 2 (Goodbye) and row 4 (qty 30)
2048
+ expect(runCF(q).sort()).toEqual(["2", "4"])
2049
+ })
2050
+
2051
+ it("codeFilter: contains/startsWith/endsWith are case-insensitive", () => {
2052
+ expect(runCF(make<CFRow>().pipe(where("desc", "contains", "WORLD"))).sort()).toEqual(["1", "4"])
2053
+ expect(runCF(make<CFRow>().pipe(where("desc", "startsWith", "hello"))).sort()).toEqual(["1", "3"])
2054
+ expect(runCF(make<CFRow>().pipe(where("desc", "endsWith", "AGAIN")))).toEqual(["3"])
2055
+ })
2056
+
2057
+ it("codeFilter: array includes / includes-any / includes-all", () => {
2058
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes", "red"))).sort()).toEqual(["1", "3"])
2059
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes-any", ["blue", "green"]))).sort()).toEqual([
2060
+ "1",
2061
+ "2",
2062
+ "3"
2063
+ ])
2064
+ expect(runCF(make<CFRow>().pipe(where("tags", "includes-all", ["red", "blue"])))).toEqual(["3"])
2065
+ })
2066
+
2067
+ it("codeFilter: in / notIn", () => {
2068
+ expect(runCF(make<CFRow>().pipe(where("tag", "in", ["x", "z"]))).sort()).toEqual(["1", "3"])
2069
+ expect(runCF(make<CFRow>().pipe(where("tag", "notIn", ["x", "z"]))).sort()).toEqual(["2", "4"])
2070
+ })
2071
+
2072
+ it("codeFilter: gt / gte / lt / lte / neq", () => {
2073
+ expect(runCF(make<CFRow>().pipe(where("qty", "gt", 10))).sort()).toEqual(["2", "4"])
2074
+ expect(runCF(make<CFRow>().pipe(where("qty", "gte", 10))).sort()).toEqual(["1", "2", "4"])
2075
+ expect(runCF(make<CFRow>().pipe(where("qty", "lt", 10)))).toEqual(["3"])
2076
+ expect(runCF(make<CFRow>().pipe(where("qty", "lte", 10))).sort()).toEqual(["1", "3"])
2077
+ expect(runCF(make<CFRow>().pipe(where("qty", "neq", 0))).sort()).toEqual(["1", "2", "4"])
2078
+ })
2079
+
2080
+ it("codeFilter: nested path access through dot notation", () => {
2081
+ expect(runCF(make<CFRow>().pipe(where("nested.kind", "k1"))).sort()).toEqual(["1", "3"])
2082
+ expect(
2083
+ runCF(
2084
+ make<CFRow>().pipe(
2085
+ where("nested.kind", "k2"),
2086
+ and("nested.v", "gt", 0)
2087
+ )
2088
+ )
2089
+ )
2090
+ .toEqual(["2"])
2091
+ })
2092
+
2093
+ it("codeFilter: array length predicates", () => {
2094
+ expect(runCF(make<CFRow>().pipe(where("tags.length", 0)))).toEqual(["4"])
2095
+ expect(runCF(make<CFRow>().pipe(where("tags.length", "gte", 2))).sort()).toEqual(["1", "3"])
2096
+ })
2097
+
2098
+ it("codeFilter: order + skip + limit applied after filter", () => {
2099
+ const q = make<CFRow>().pipe(
2100
+ where("tag", "neq", "z"),
2101
+ order("qty", "DESC"),
2102
+ page({ skip: 1, take: 2 })
2103
+ )
2104
+ expect(runCF(q)).toEqual(["2", "1"])
2105
+ })
2106
+
2107
+ // ─────────────────────────────────────────────────────────────────────────────
2108
+ // memFilter: aggregate (GROUP BY) support
2109
+ // ─────────────────────────────────────────────────────────────────────────────
2110
+
2111
+ it("memFilter: agg-count-when groups rows and counts conditionally", () => {
2112
+ type Row = { city: string; status: string }
2113
+ const rows: Row[] = [
2114
+ { city: "NYC", status: "active" },
2115
+ { city: "NYC", status: "inactive" },
2116
+ { city: "NYC", status: "active" },
2117
+ { city: "LA", status: "active" }
2118
+ ]
2119
+ const result = memFilter<Row>({
2120
+ t: {} as Row,
2121
+ select: [
2122
+ { key: "city", path: "city" },
2123
+ {
2124
+ key: "activeCount",
2125
+ aggregate: {
2126
+ _tag: "agg-count-when",
2127
+ filter: [{ t: "where", path: "status", op: "eq", value: "active" }]
2128
+ }
2129
+ },
2130
+ { key: "total", aggregate: { _tag: "agg-count" } }
2131
+ ] as any
2132
+ })(rows as any) as any[]
2133
+
2134
+ expect(result.length).toBe(2)
2135
+ const nyc = result.find((r: any) => r.city === "NYC")!
2136
+ expect(nyc.activeCount).toBe(2)
2137
+ expect(nyc.total).toBe(3)
2138
+ const la = result.find((r: any) => r.city === "LA")!
2139
+ expect(la.activeCount).toBe(1)
2140
+ expect(la.total).toBe(1)
2141
+ })
2142
+
2143
+ it("memFilter: agg-sum / agg-min / agg-max aggregate numerics", () => {
2144
+ type Row = { dept: string; salary: number }
2145
+ const rows: Row[] = [
2146
+ { dept: "eng", salary: 100 },
2147
+ { dept: "eng", salary: 200 },
2148
+ { dept: "hr", salary: 50 }
2149
+ ]
2150
+ const result = memFilter<Row>({
2151
+ t: {} as Row,
2152
+ select: [
2153
+ { key: "dept", path: "dept" },
2154
+ { key: "total", aggregate: { _tag: "agg-sum", field: "salary" } },
2155
+ { key: "min", aggregate: { _tag: "agg-min", field: "salary" } },
2156
+ { key: "max", aggregate: { _tag: "agg-max", field: "salary" } }
2157
+ ] as any
2158
+ })(rows as any) as any[]
2159
+
2160
+ expect(result.length).toBe(2)
2161
+ const eng = result.find((r: any) => r.dept === "eng")!
2162
+ expect(eng.total).toBe(300)
2163
+ expect(eng.min).toBe(100)
2164
+ expect(eng.max).toBe(200)
2165
+ const hr = result.find((r: any) => r.dept === "hr")!
2166
+ expect(hr.total).toBe(50)
2167
+ })
2168
+
2169
+ it("memFilter: aggregate with nested path grouping", () => {
2170
+ type Row = { address: { city: string }; value: number }
2171
+ const rows: Row[] = [
2172
+ { address: { city: "NYC" }, value: 10 },
2173
+ { address: { city: "NYC" }, value: 20 },
2174
+ { address: { city: "LA" }, value: 5 }
2175
+ ]
2176
+ const result = memFilter<Row>({
2177
+ t: {} as Row,
2178
+ select: [
2179
+ { key: "city", path: "address.city" },
2180
+ { key: "count", aggregate: { _tag: "agg-count" } }
2181
+ ] as any
2182
+ })(rows as any) as any[]
2183
+
2184
+ expect(result.length).toBe(2)
2185
+ expect(result.find((r: any) => r.city === "NYC")!.count).toBe(2)
2186
+ expect(result.find((r: any) => r.city === "LA")!.count).toBe(1)
2187
+ })