@effect-app/infra 4.0.0-beta.25 → 4.0.0-beta.251

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 (467) hide show
  1. package/CHANGELOG.md +1900 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +30 -11
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +35 -14
  6. package/dist/ContextProvider.d.ts +34 -0
  7. package/dist/ContextProvider.d.ts.map +1 -0
  8. package/dist/ContextProvider.js +40 -0
  9. package/dist/Emailer/Sendgrid.d.ts +111 -147
  10. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  11. package/dist/Emailer/Sendgrid.js +24 -19
  12. package/dist/Emailer/fake.d.ts +2 -2
  13. package/dist/Emailer/fake.d.ts.map +1 -1
  14. package/dist/Emailer/fake.js +4 -4
  15. package/dist/MainFiberSet.d.ts +12 -9
  16. package/dist/MainFiberSet.d.ts.map +1 -1
  17. package/dist/MainFiberSet.js +10 -6
  18. package/dist/QueueMaker/SQLQueue.d.ts +8 -9
  19. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  20. package/dist/QueueMaker/SQLQueue.js +138 -120
  21. package/dist/QueueMaker/errors.d.ts +5 -3
  22. package/dist/QueueMaker/errors.d.ts.map +1 -1
  23. package/dist/QueueMaker/errors.js +4 -2
  24. package/dist/QueueMaker/memQueue.d.ts +10 -6
  25. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  26. package/dist/QueueMaker/memQueue.js +84 -68
  27. package/dist/QueueMaker/sbqueue.d.ts +9 -5
  28. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  29. package/dist/QueueMaker/sbqueue.js +60 -58
  30. package/dist/RequestFiberSet.d.ts +10 -7
  31. package/dist/RequestFiberSet.d.ts.map +1 -1
  32. package/dist/RequestFiberSet.js +13 -8
  33. package/dist/SQL/Model.d.ts +468 -0
  34. package/dist/SQL/Model.d.ts.map +1 -0
  35. package/dist/SQL/Model.js +469 -0
  36. package/dist/SQL.d.ts +2 -0
  37. package/dist/SQL.d.ts.map +1 -0
  38. package/dist/{adapters/SQL.js → SQL.js} +1 -1
  39. package/dist/ServiceBus.d.ts +61 -0
  40. package/dist/ServiceBus.d.ts.map +1 -0
  41. package/dist/ServiceBus.js +108 -0
  42. package/dist/Store/Cosmos/query.d.ts +15 -4
  43. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  44. package/dist/Store/Cosmos/query.js +179 -41
  45. package/dist/Store/Cosmos.d.ts +3 -3
  46. package/dist/Store/Cosmos.d.ts.map +1 -1
  47. package/dist/Store/Cosmos.js +344 -246
  48. package/dist/Store/Disk.d.ts +5 -5
  49. package/dist/Store/Disk.d.ts.map +1 -1
  50. package/dist/Store/Disk.js +78 -38
  51. package/dist/Store/Memory.d.ts +7 -10
  52. package/dist/Store/Memory.d.ts.map +1 -1
  53. package/dist/Store/Memory.js +326 -66
  54. package/dist/Store/SQL/Pg.d.ts +4 -0
  55. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  56. package/dist/Store/SQL/Pg.js +232 -0
  57. package/dist/Store/SQL/query.d.ts +49 -0
  58. package/dist/Store/SQL/query.d.ts.map +1 -0
  59. package/dist/Store/SQL/query.js +527 -0
  60. package/dist/Store/SQL.d.ts +21 -0
  61. package/dist/Store/SQL.d.ts.map +1 -0
  62. package/dist/Store/SQL.js +449 -0
  63. package/dist/Store/codeFilter.d.ts +5 -5
  64. package/dist/Store/codeFilter.d.ts.map +1 -1
  65. package/dist/Store/codeFilter.js +6 -3
  66. package/dist/Store/index.d.ts +7 -5
  67. package/dist/Store/index.d.ts.map +1 -1
  68. package/dist/Store/index.js +18 -5
  69. package/dist/Store/utils.d.ts +4 -3
  70. package/dist/Store/utils.d.ts.map +1 -1
  71. package/dist/Store/utils.js +5 -5
  72. package/dist/arbs.d.ts +2 -2
  73. package/dist/arbs.d.ts.map +1 -1
  74. package/dist/arbs.js +5 -3
  75. package/dist/codec.d.ts +5 -0
  76. package/dist/codec.d.ts.map +1 -0
  77. package/dist/codec.js +5 -0
  78. package/dist/cosmos-client.d.ts +16 -0
  79. package/dist/cosmos-client.d.ts.map +1 -0
  80. package/dist/cosmos-client.js +11 -0
  81. package/dist/errorReporter.d.ts +7 -5
  82. package/dist/errorReporter.d.ts.map +1 -1
  83. package/dist/errorReporter.js +23 -27
  84. package/dist/errors.d.ts +1 -1
  85. package/dist/fileUtil.d.ts +2 -2
  86. package/dist/fileUtil.d.ts.map +1 -1
  87. package/dist/fileUtil.js +2 -2
  88. package/dist/index.d.ts +3 -2
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +3 -2
  91. package/dist/internal/RequestContextMiddleware.d.ts +5 -0
  92. package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
  93. package/dist/internal/RequestContextMiddleware.js +45 -0
  94. package/dist/internal/auth.d.ts +53 -0
  95. package/dist/internal/auth.d.ts.map +1 -0
  96. package/dist/internal/auth.js +180 -0
  97. package/dist/internal/events.d.ts +11 -0
  98. package/dist/internal/events.d.ts.map +1 -0
  99. package/dist/internal/events.js +49 -0
  100. package/dist/internal/health.d.ts +3 -0
  101. package/dist/internal/health.d.ts.map +1 -0
  102. package/dist/internal/health.js +5 -0
  103. package/dist/layerUtils.d.ts +32 -0
  104. package/dist/layerUtils.d.ts.map +1 -0
  105. package/dist/layerUtils.js +17 -0
  106. package/dist/logger/jsonLogger.d.ts +2 -2
  107. package/dist/logger/jsonLogger.d.ts.map +1 -1
  108. package/dist/logger/jsonLogger.js +5 -3
  109. package/dist/logger/logFmtLogger.d.ts +2 -2
  110. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  111. package/dist/logger/logFmtLogger.js +3 -3
  112. package/dist/logger/shared.d.ts +3 -3
  113. package/dist/logger/shared.d.ts.map +1 -1
  114. package/dist/logger/shared.js +5 -5
  115. package/dist/logger.d.ts +1 -1
  116. package/dist/logger.d.ts.map +1 -1
  117. package/dist/memQueue.d.ts +15 -0
  118. package/dist/memQueue.d.ts.map +1 -0
  119. package/dist/memQueue.js +21 -0
  120. package/dist/middlewares.d.ts +10 -0
  121. package/dist/middlewares.d.ts.map +1 -0
  122. package/dist/{api/middlewares.js → middlewares.js} +1 -1
  123. package/dist/mongo-client.d.ts +11 -0
  124. package/dist/mongo-client.d.ts.map +1 -0
  125. package/dist/mongo-client.js +15 -0
  126. package/dist/otel.d.ts +75 -0
  127. package/dist/otel.d.ts.map +1 -0
  128. package/dist/otel.js +65 -0
  129. package/dist/rateLimit.d.ts +12 -4
  130. package/dist/rateLimit.d.ts.map +1 -1
  131. package/dist/rateLimit.js +7 -12
  132. package/dist/redis-client.d.ts +42 -0
  133. package/dist/redis-client.d.ts.map +1 -0
  134. package/dist/redis-client.js +98 -0
  135. package/dist/reportError.d.ts +4 -0
  136. package/dist/reportError.d.ts.map +1 -0
  137. package/dist/reportError.js +28 -0
  138. package/dist/routing/middleware/RouterMiddleware.d.ts +16 -0
  139. package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
  140. package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
  141. package/dist/routing/middleware/middleware.d.ts +48 -0
  142. package/dist/routing/middleware/middleware.d.ts.map +1 -0
  143. package/dist/routing/middleware/middleware.js +128 -0
  144. package/dist/routing/middleware.d.ts +3 -0
  145. package/dist/routing/middleware.d.ts.map +1 -0
  146. package/dist/{api/routing → routing}/middleware.js +1 -2
  147. package/dist/routing/schema/jwt.d.ts +4 -0
  148. package/dist/routing/schema/jwt.d.ts.map +1 -0
  149. package/dist/routing/schema/jwt.js +13 -0
  150. package/dist/routing/tsort.d.ts +8 -0
  151. package/dist/routing/tsort.d.ts.map +1 -0
  152. package/dist/routing/tsort.js +51 -0
  153. package/dist/routing/utils.d.ts +19 -0
  154. package/dist/routing/utils.d.ts.map +1 -0
  155. package/dist/routing/utils.js +45 -0
  156. package/dist/routing.d.ts +184 -0
  157. package/dist/routing.d.ts.map +1 -0
  158. package/dist/routing.js +236 -0
  159. package/dist/test.d.ts +3 -3
  160. package/dist/test.d.ts.map +1 -1
  161. package/dist/test.js +2 -2
  162. package/dist/util.d.ts +3 -0
  163. package/dist/util.d.ts.map +1 -0
  164. package/dist/util.js +14 -0
  165. package/dist/vitest.d.ts +1 -1
  166. package/examples/query.ts +47 -39
  167. package/package.json +119 -234
  168. package/src/CUPS.ts +52 -13
  169. package/src/{api/ContextProvider.ts → ContextProvider.ts} +19 -16
  170. package/src/Emailer/Sendgrid.ts +82 -59
  171. package/src/Emailer/fake.ts +3 -3
  172. package/src/MainFiberSet.ts +12 -10
  173. package/src/QueueMaker/SQLQueue.ts +153 -156
  174. package/src/QueueMaker/errors.ts +3 -1
  175. package/src/QueueMaker/memQueue.ts +113 -107
  176. package/src/QueueMaker/sbqueue.ts +78 -90
  177. package/src/RequestFiberSet.ts +13 -8
  178. package/src/{adapters/SQL → SQL}/Model.ts +42 -41
  179. package/src/ServiceBus.ts +219 -0
  180. package/src/Store/Cosmos/query.ts +216 -52
  181. package/src/Store/Cosmos.ts +493 -353
  182. package/src/Store/Disk.ts +109 -69
  183. package/src/Store/Memory.ts +365 -96
  184. package/src/Store/SQL/Pg.ts +363 -0
  185. package/src/Store/SQL/query.ts +603 -0
  186. package/src/Store/SQL.ts +735 -0
  187. package/src/Store/codeFilter.ts +8 -5
  188. package/src/Store/index.ts +21 -6
  189. package/src/Store/utils.ts +26 -24
  190. package/src/arbs.ts +5 -3
  191. package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
  192. package/src/errorReporter.ts +66 -76
  193. package/src/fileUtil.ts +1 -1
  194. package/src/index.ts +2 -1
  195. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  196. package/src/internal/auth.ts +272 -0
  197. package/src/{api/internal → internal}/events.ts +22 -13
  198. package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
  199. package/src/logger/jsonLogger.ts +4 -2
  200. package/src/logger/logFmtLogger.ts +2 -2
  201. package/src/logger/shared.ts +5 -4
  202. package/src/{adapters/memQueue.ts → memQueue.ts} +5 -4
  203. package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
  204. package/src/otel.ts +152 -0
  205. package/src/rateLimit.ts +34 -23
  206. package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
  207. package/src/{api/reportError.ts → reportError.ts} +3 -2
  208. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
  209. package/src/{api/routing → routing}/middleware/middleware.ts +62 -17
  210. package/src/routing/middleware.ts +4 -0
  211. package/src/{api/routing → routing}/schema/jwt.ts +2 -1
  212. package/src/{api/routing → routing}/utils.ts +2 -1
  213. package/src/routing.ts +768 -0
  214. package/src/test.ts +2 -2
  215. package/test/auth.test.ts +101 -0
  216. package/test/contextProvider.test.ts +15 -12
  217. package/test/controller.test.ts +28 -32
  218. package/test/cosmos-query.test.ts +159 -0
  219. package/test/dist/auth.test.d.ts.map +1 -0
  220. package/test/dist/contextProvider.test.d.ts.map +1 -1
  221. package/test/dist/controller.test.d.ts.map +1 -1
  222. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  223. package/test/dist/date-query.test.d.ts.map +1 -0
  224. package/test/dist/fixtures.d.ts +30 -12
  225. package/test/dist/fixtures.d.ts.map +1 -1
  226. package/test/dist/fixtures.js +17 -10
  227. package/test/dist/query.test.d.ts.map +1 -1
  228. package/test/dist/rawQuery.test.d.ts.map +1 -1
  229. package/test/dist/repository-ext.test.d.ts.map +1 -0
  230. package/test/dist/requires.test.d.ts.map +1 -1
  231. package/test/dist/router-generator.test.d.ts.map +1 -0
  232. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  233. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  234. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  235. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  236. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  237. package/test/dist/sql-store.test.d.ts.map +1 -0
  238. package/test/fixtures.ts +16 -9
  239. package/test/layerUtils.test.ts +2 -2
  240. package/test/query.test.ts +903 -40
  241. package/test/rawQuery.test.ts +340 -22
  242. package/test/repository-ext.test.ts +62 -0
  243. package/test/requires.test.ts +10 -5
  244. package/test/router-generator.test.ts +187 -0
  245. package/test/routing-interruptibility.test.ts +66 -0
  246. package/test/rpc-context-map-streaming.test.ts +262 -0
  247. package/test/rpc-e2e-invalidation.test.ts +256 -0
  248. package/test/rpc-multi-middleware.test.ts +85 -10
  249. package/test/rpc-stream-fullstack.test.ts +304 -0
  250. package/test/sql-store.test.ts +1711 -0
  251. package/test/validateSample.test.ts +19 -14
  252. package/tsconfig.examples.json +1 -1
  253. package/tsconfig.json +2 -1
  254. package/tsconfig.json.bak +2 -2
  255. package/tsconfig.src.json +35 -35
  256. package/tsconfig.test.json +2 -2
  257. package/dist/Emailer/service.d.ts +0 -55
  258. package/dist/Emailer/service.d.ts.map +0 -1
  259. package/dist/Emailer/service.js +0 -6
  260. package/dist/Emailer.d.ts +0 -2
  261. package/dist/Emailer.d.ts.map +0 -1
  262. package/dist/Emailer.js +0 -2
  263. package/dist/Model/Repository/ext.d.ts +0 -41
  264. package/dist/Model/Repository/ext.d.ts.map +0 -1
  265. package/dist/Model/Repository/ext.js +0 -65
  266. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  267. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  268. package/dist/Model/Repository/internal/internal.js +0 -316
  269. package/dist/Model/Repository/legacy.d.ts +0 -19
  270. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  271. package/dist/Model/Repository/legacy.js +0 -2
  272. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  273. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  274. package/dist/Model/Repository/makeRepo.js +0 -24
  275. package/dist/Model/Repository/service.d.ts +0 -89
  276. package/dist/Model/Repository/service.d.ts.map +0 -1
  277. package/dist/Model/Repository/service.js +0 -2
  278. package/dist/Model/Repository/validation.d.ts +0 -42
  279. package/dist/Model/Repository/validation.d.ts.map +0 -1
  280. package/dist/Model/Repository/validation.js +0 -32
  281. package/dist/Model/Repository.d.ts +0 -6
  282. package/dist/Model/Repository.d.ts.map +0 -1
  283. package/dist/Model/Repository.js +0 -6
  284. package/dist/Model/dsl.d.ts +0 -32
  285. package/dist/Model/dsl.d.ts.map +0 -1
  286. package/dist/Model/dsl.js +0 -44
  287. package/dist/Model/filter/filterApi.d.ts +0 -30
  288. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  289. package/dist/Model/filter/filterApi.js +0 -2
  290. package/dist/Model/filter/types/errors.d.ts +0 -29
  291. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  292. package/dist/Model/filter/types/errors.js +0 -2
  293. package/dist/Model/filter/types/fields.d.ts +0 -15
  294. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  295. package/dist/Model/filter/types/fields.js +0 -2
  296. package/dist/Model/filter/types/path/common.d.ts +0 -316
  297. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  298. package/dist/Model/filter/types/path/common.js +0 -2
  299. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  300. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  301. package/dist/Model/filter/types/path/eager.js +0 -31
  302. package/dist/Model/filter/types/path/index.d.ts +0 -4
  303. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  304. package/dist/Model/filter/types/path/index.js +0 -3
  305. package/dist/Model/filter/types/utils.d.ts +0 -79
  306. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  307. package/dist/Model/filter/types/utils.js +0 -2
  308. package/dist/Model/filter/types/validator.d.ts +0 -30
  309. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  310. package/dist/Model/filter/types/validator.js +0 -2
  311. package/dist/Model/filter/types.d.ts +0 -5
  312. package/dist/Model/filter/types.d.ts.map +0 -1
  313. package/dist/Model/filter/types.js +0 -7
  314. package/dist/Model/query/dsl.d.ts +0 -248
  315. package/dist/Model/query/dsl.d.ts.map +0 -1
  316. package/dist/Model/query/dsl.js +0 -104
  317. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  318. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  319. package/dist/Model/query/new-kid-interpreter.js +0 -165
  320. package/dist/Model/query.d.ts +0 -15
  321. package/dist/Model/query.d.ts.map +0 -1
  322. package/dist/Model/query.js +0 -3
  323. package/dist/Model.d.ts +0 -4
  324. package/dist/Model.d.ts.map +0 -1
  325. package/dist/Model.js +0 -4
  326. package/dist/Operations.d.ts +0 -55
  327. package/dist/Operations.d.ts.map +0 -1
  328. package/dist/Operations.js +0 -102
  329. package/dist/OperationsRepo.d.ts +0 -41
  330. package/dist/OperationsRepo.d.ts.map +0 -1
  331. package/dist/OperationsRepo.js +0 -14
  332. package/dist/QueueMaker/service.d.ts +0 -11
  333. package/dist/QueueMaker/service.d.ts.map +0 -1
  334. package/dist/QueueMaker/service.js +0 -4
  335. package/dist/RequestContext.d.ts +0 -63
  336. package/dist/RequestContext.d.ts.map +0 -1
  337. package/dist/RequestContext.js +0 -49
  338. package/dist/Store/ContextMapContainer.d.ts +0 -14
  339. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  340. package/dist/Store/ContextMapContainer.js +0 -16
  341. package/dist/Store/service.d.ts +0 -108
  342. package/dist/Store/service.d.ts.map +0 -1
  343. package/dist/Store/service.js +0 -71
  344. package/dist/Store.d.ts +0 -2
  345. package/dist/Store.d.ts.map +0 -1
  346. package/dist/Store.js +0 -2
  347. package/dist/adapters/SQL/Model.d.ts +0 -479
  348. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  349. package/dist/adapters/SQL/Model.js +0 -478
  350. package/dist/adapters/SQL.d.ts +0 -2
  351. package/dist/adapters/SQL.d.ts.map +0 -1
  352. package/dist/adapters/ServiceBus.d.ts +0 -58
  353. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  354. package/dist/adapters/ServiceBus.js +0 -99
  355. package/dist/adapters/cosmos-client.d.ts +0 -14
  356. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  357. package/dist/adapters/cosmos-client.js +0 -9
  358. package/dist/adapters/index.d.ts +0 -2
  359. package/dist/adapters/index.d.ts.map +0 -1
  360. package/dist/adapters/index.js +0 -2
  361. package/dist/adapters/logger.d.ts +0 -9
  362. package/dist/adapters/logger.d.ts.map +0 -1
  363. package/dist/adapters/logger.js +0 -3
  364. package/dist/adapters/memQueue.d.ts +0 -13
  365. package/dist/adapters/memQueue.d.ts.map +0 -1
  366. package/dist/adapters/memQueue.js +0 -20
  367. package/dist/adapters/mongo-client.d.ts +0 -10
  368. package/dist/adapters/mongo-client.d.ts.map +0 -1
  369. package/dist/adapters/mongo-client.js +0 -13
  370. package/dist/adapters/redis-client.d.ts +0 -39
  371. package/dist/adapters/redis-client.d.ts.map +0 -1
  372. package/dist/adapters/redis-client.js +0 -94
  373. package/dist/api/ContextProvider.d.ts +0 -31
  374. package/dist/api/ContextProvider.d.ts.map +0 -1
  375. package/dist/api/ContextProvider.js +0 -38
  376. package/dist/api/codec.d.ts +0 -5
  377. package/dist/api/codec.d.ts.map +0 -1
  378. package/dist/api/codec.js +0 -5
  379. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  380. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  381. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  382. package/dist/api/internal/auth.d.ts +0 -15
  383. package/dist/api/internal/auth.d.ts.map +0 -1
  384. package/dist/api/internal/auth.js +0 -47
  385. package/dist/api/internal/events.d.ts +0 -9
  386. package/dist/api/internal/events.d.ts.map +0 -1
  387. package/dist/api/internal/events.js +0 -42
  388. package/dist/api/internal/health.d.ts +0 -3
  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 +0 -24
  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 +0 -10
  395. package/dist/api/middlewares.d.ts.map +0 -1
  396. package/dist/api/reportError.d.ts +0 -4
  397. package/dist/api/reportError.d.ts.map +0 -1
  398. package/dist/api/reportError.js +0 -27
  399. package/dist/api/routing/middleware/RouterMiddleware.d.ts +0 -15
  400. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  401. package/dist/api/routing/middleware/middleware.d.ts +0 -9
  402. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  403. package/dist/api/routing/middleware/middleware.js +0 -92
  404. package/dist/api/routing/middleware.d.ts +0 -4
  405. package/dist/api/routing/middleware.d.ts.map +0 -1
  406. package/dist/api/routing/schema/jwt.d.ts +0 -4
  407. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  408. package/dist/api/routing/schema/jwt.js +0 -12
  409. package/dist/api/routing/tsort.d.ts +0 -8
  410. package/dist/api/routing/tsort.d.ts.map +0 -1
  411. package/dist/api/routing/tsort.js +0 -51
  412. package/dist/api/routing/utils.d.ts +0 -19
  413. package/dist/api/routing/utils.d.ts.map +0 -1
  414. package/dist/api/routing/utils.js +0 -44
  415. package/dist/api/routing.d.ts +0 -138
  416. package/dist/api/routing.d.ts.map +0 -1
  417. package/dist/api/routing.js +0 -166
  418. package/dist/api/setupRequest.d.ts +0 -12
  419. package/dist/api/setupRequest.d.ts.map +0 -1
  420. package/dist/api/setupRequest.js +0 -44
  421. package/dist/api/util.d.ts +0 -3
  422. package/dist/api/util.d.ts.map +0 -1
  423. package/dist/api/util.js +0 -14
  424. package/eslint.config.mjs +0 -24
  425. package/src/Emailer/service.ts +0 -52
  426. package/src/Emailer.ts +0 -1
  427. package/src/Model/Repository/ext.ts +0 -283
  428. package/src/Model/Repository/internal/internal.ts +0 -577
  429. package/src/Model/Repository/legacy.ts +0 -27
  430. package/src/Model/Repository/makeRepo.ts +0 -139
  431. package/src/Model/Repository/service.ts +0 -627
  432. package/src/Model/Repository/validation.ts +0 -31
  433. package/src/Model/Repository.ts +0 -5
  434. package/src/Model/dsl.ts +0 -128
  435. package/src/Model/filter/filterApi.ts +0 -60
  436. package/src/Model/filter/types/errors.ts +0 -47
  437. package/src/Model/filter/types/fields.ts +0 -50
  438. package/src/Model/filter/types/path/common.ts +0 -404
  439. package/src/Model/filter/types/path/eager.ts +0 -298
  440. package/src/Model/filter/types/path/index.ts +0 -4
  441. package/src/Model/filter/types/utils.ts +0 -128
  442. package/src/Model/filter/types/validator.ts +0 -46
  443. package/src/Model/filter/types.ts +0 -6
  444. package/src/Model/query/dsl.ts +0 -2110
  445. package/src/Model/query/new-kid-interpreter.ts +0 -210
  446. package/src/Model/query.ts +0 -13
  447. package/src/Model.ts +0 -3
  448. package/src/Operations.ts +0 -235
  449. package/src/OperationsRepo.ts +0 -16
  450. package/src/QueueMaker/service.ts +0 -17
  451. package/src/RequestContext.ts +0 -63
  452. package/src/Store/ContextMapContainer.ts +0 -20
  453. package/src/Store/service.ts +0 -184
  454. package/src/Store.ts +0 -1
  455. package/src/adapters/ServiceBus.ts +0 -209
  456. package/src/adapters/index.ts +0 -0
  457. package/src/adapters/logger.ts +0 -3
  458. package/src/api/internal/auth.ts +0 -68
  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/src/{adapters/SQL.ts → SQL.ts} +0 -0
  463. /package/src/{api/codec.ts → codec.ts} +0 -0
  464. /package/src/{api/internal → internal}/health.ts +0 -0
  465. /package/src/{api/middlewares.ts → middlewares.ts} +0 -0
  466. /package/src/{api/routing → routing}/tsort.ts +0 -0
  467. /package/src/{api/util.ts → util.ts} +0 -0
@@ -0,0 +1,256 @@
1
+ /**
2
+ * E2E tests for the invalidation key flow exercised end-to-end via the
3
+ * production wrap/unwrap path:
4
+ *
5
+ * server: routing.ts wraps command success with `CommandResponseWithMetaData`
6
+ * and handler-thrown failure with `CommandFailureWithMetaData`;
7
+ * routing wraps stream values into `{_tag:"value"|"metadata"|"done"}`
8
+ * chunks. `InvalidationSet.use(...)` inside a handler accumulates keys.
9
+ * client: apiClientFactory unwraps both envelopes and forwards keys to
10
+ * `InvalidationKeysFromServer`.
11
+ *
12
+ * Transport is real HTTP (NodeHttpServer on a loopback port) so the wire
13
+ * encoding is exercised too.
14
+ */
15
+ import { NodeHttpServer } from "@effect/platform-node"
16
+ import { expect, it } from "@effect/vitest"
17
+ import { ApiClientFactory, InvalidationKeysFromServer, makeInvalidationKeysService, makeRpcClient } from "effect-app/client"
18
+ import { HttpRouter, HttpServer } from "effect-app/http"
19
+ import { DefaultGenericMiddlewares } from "effect-app/middleware"
20
+ import { Invalidation, MiddlewareMaker } from "effect-app/rpc"
21
+ import * as S from "effect-app/Schema"
22
+ import { TaggedErrorClass } from "effect-app/Schema"
23
+ import * as Effect from "effect/Effect"
24
+ import * as Exit from "effect/Exit"
25
+ import * as Layer from "effect/Layer"
26
+ import * as Option from "effect/Option"
27
+ import * as Ref from "effect/Ref"
28
+ import * as Stream from "effect/Stream"
29
+ import { FetchHttpClient } from "effect/unstable/http"
30
+ import { RpcSerialization } from "effect/unstable/rpc"
31
+ import { createServer } from "http"
32
+ import { makeRouter } from "../src/routing.js"
33
+ import { DefaultGenericMiddlewaresLive } from "../src/routing/middleware.js"
34
+ import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, SomeElseMiddleware, SomeElseMiddlewareLive, SomeService, Test, TestLive } from "./fixtures.js"
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Middleware (mirrors AppMiddleware shape — same composite as other e2e tests).
38
+ // ---------------------------------------------------------------------------
39
+
40
+ class AppMiddleware extends MiddlewareMaker
41
+ .Tag<AppMiddleware>()("AppMiddleware", RequestContextMap)
42
+ .middleware(RequireRoles, Test)
43
+ .middleware(AllowAnonymous)
44
+ .middleware(SomeElseMiddleware)
45
+ .middleware(...DefaultGenericMiddlewares)
46
+ {
47
+ static Default = this.layer.pipe(
48
+ Layer.provide(
49
+ [
50
+ RequireRolesLive.pipe(Layer.provide(SomeService.Default)),
51
+ AllowAnonymousLive,
52
+ TestLive,
53
+ SomeElseMiddlewareLive,
54
+ DefaultGenericMiddlewaresLive
55
+ ] as const
56
+ )
57
+ )
58
+ }
59
+
60
+ const { Router, matchAll } = makeRouter(AppMiddleware.Default)
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Resources
64
+ // ---------------------------------------------------------------------------
65
+
66
+ const DynamicKey: Invalidation.InvalidationKey = ["dynamic", "key"]
67
+ const ExtraKey: Invalidation.InvalidationKey = ["extra", "key"]
68
+ const StreamKey: Invalidation.InvalidationKey = ["stream", "key"]
69
+
70
+ const { TaggedRequestFor } = makeRpcClient(AppMiddleware)
71
+ const Req = TaggedRequestFor("Inv")
72
+
73
+ class CmdBoom extends TaggedErrorClass<CmdBoom>()("CmdBoom", { reason: S.String }) {}
74
+
75
+ class DoNothing extends Req.Command<DoNothing>()("DoNothing", {}, {
76
+ allowAnonymous: true,
77
+ success: S.Void
78
+ }) {}
79
+
80
+ class DoWithDynamicKey extends Req.Command<DoWithDynamicKey>()("DoWithDynamicKey", {}, {
81
+ allowAnonymous: true,
82
+ success: S.String
83
+ }) {}
84
+
85
+ class DoWithBothKeys extends Req.Command<DoWithBothKeys>()("DoWithBothKeys", {}, {
86
+ allowAnonymous: true,
87
+ success: S.Number
88
+ }) {}
89
+
90
+ class DoAndFail extends Req.Command<DoAndFail>()("DoAndFail", {}, {
91
+ allowAnonymous: true,
92
+ success: S.Void,
93
+ error: CmdBoom
94
+ }) {}
95
+
96
+ class StreamWithKey extends Req.Command<StreamWithKey>()("StreamWithKey", {}, {
97
+ stream: true,
98
+ allowAnonymous: true,
99
+ success: S.Number
100
+ }) {}
101
+
102
+ const InvRsc = { DoNothing, DoWithDynamicKey, DoWithBothKeys, DoAndFail, StreamWithKey }
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Controllers / router
106
+ // ---------------------------------------------------------------------------
107
+
108
+ const router = Router(InvRsc)({
109
+ *effect(match) {
110
+ return match({
111
+ DoNothing: () => Effect.void,
112
+ DoWithDynamicKey: Effect.fnUntraced(function*() {
113
+ yield* Invalidation.InvalidationSet.use((_) => _.add(DynamicKey))
114
+ return "done"
115
+ }),
116
+ DoWithBothKeys: Effect.fnUntraced(function*() {
117
+ yield* Invalidation.InvalidationSet.use((_) => _.add(DynamicKey))
118
+ yield* Invalidation.InvalidationSet.use((_) => _.add(ExtraKey))
119
+ return 99
120
+ }),
121
+ DoAndFail: Effect.fnUntraced(function*() {
122
+ yield* Invalidation.InvalidationSet.use((_) => _.add(DynamicKey))
123
+ return yield* Effect.fail(new CmdBoom({ reason: "intentional failure" }))
124
+ }),
125
+ StreamWithKey: () =>
126
+ Stream.fromIterable([1, 2, 3]).pipe(
127
+ Stream.tap(() => Invalidation.InvalidationSet.use((_) => _.add(StreamKey)))
128
+ )
129
+ })
130
+ }
131
+ })
132
+
133
+ const RpcRouterLayer = matchAll({ router })
134
+
135
+ // ---------------------------------------------------------------------------
136
+ // HTTP wiring — fresh server on loopback per `it.live`.
137
+ // ---------------------------------------------------------------------------
138
+
139
+ const NodeServerLayer = NodeHttpServer.layer(() => createServer(), { port: 0 })
140
+
141
+ const ServerLayer = HttpRouter
142
+ .serve(RpcRouterLayer)
143
+ .pipe(
144
+ Layer.provide(NodeServerLayer),
145
+ Layer.provide(RpcSerialization.layerNdjson)
146
+ )
147
+
148
+ const ClientLayer = Layer
149
+ .unwrap(
150
+ Effect.gen(function*() {
151
+ const server = yield* HttpServer.HttpServer
152
+ const addr = server.address
153
+ if (addr._tag !== "TcpAddress") return yield* Effect.die(new Error("expected TcpAddress"))
154
+ const host = addr.hostname === "0.0.0.0" ? "127.0.0.1" : addr.hostname
155
+ const url = `http://${host}:${addr.port}`
156
+ return ApiClientFactory
157
+ .layer({ url, headers: Option.none() })
158
+ .pipe(Layer.provide(FetchHttpClient.layer))
159
+ })
160
+ )
161
+ .pipe(Layer.provide(NodeServerLayer))
162
+
163
+ const TestLayer = Layer.mergeAll(ServerLayer, ClientLayer)
164
+
165
+ // Helper: provide a fresh `InvalidationKeysFromServer` and capture forwarded keys.
166
+ const withCapture = <A, E, R>(eff: Effect.Effect<A, E, R>) =>
167
+ Effect.gen(function*() {
168
+ const ref = yield* Ref.make<ReadonlyArray<Invalidation.InvalidationKey>>([])
169
+ const svc = makeInvalidationKeysService(ref)
170
+ const result = yield* eff.pipe(Effect.provideService(InvalidationKeysFromServer, svc), Effect.exit)
171
+ return { result, keys: yield* Ref.get(ref) }
172
+ })
173
+
174
+ // ---------------------------------------------------------------------------
175
+ // Tests
176
+ // ---------------------------------------------------------------------------
177
+
178
+ it.live(
179
+ "command with no invalidation keys: caller sees raw payload, no keys forwarded",
180
+ Effect.fnUntraced(function*() {
181
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
182
+ const { result, keys } = yield* withCapture(client.DoNothing.handler())
183
+ expect(Exit.isSuccess(result)).toBe(true)
184
+ expect(keys).toStrictEqual([])
185
+ }, Effect.provide(TestLayer)),
186
+ { timeout: 10_000 }
187
+ )
188
+
189
+ it.live(
190
+ "command with dynamic InvalidationSet.use: payload + key forwarded",
191
+ Effect.fnUntraced(function*() {
192
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
193
+ const { result, keys } = yield* withCapture(client.DoWithDynamicKey.handler())
194
+ expect(Exit.isSuccess(result) && result.value).toBe("done")
195
+ expect(keys).toStrictEqual([DynamicKey])
196
+ }, Effect.provide(TestLayer)),
197
+ { timeout: 10_000 }
198
+ )
199
+
200
+ it.live(
201
+ "command accumulating multiple dynamic keys: all keys forwarded in order",
202
+ Effect.fnUntraced(function*() {
203
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
204
+ const { result, keys } = yield* withCapture(client.DoWithBothKeys.handler())
205
+ expect(Exit.isSuccess(result) && result.value).toBe(99)
206
+ expect(keys).toStrictEqual([DynamicKey, ExtraKey])
207
+ }, Effect.provide(TestLayer)),
208
+ { timeout: 10_000 }
209
+ )
210
+
211
+ it.live(
212
+ "per-request isolation: each command call starts with a fresh InvalidationSet",
213
+ Effect.fnUntraced(function*() {
214
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
215
+ const r1 = yield* withCapture(client.DoWithDynamicKey.handler())
216
+ const r2 = yield* withCapture(client.DoWithDynamicKey.handler())
217
+ // Each call must have exactly one key — no accumulation across calls
218
+ expect(r1.keys).toStrictEqual([DynamicKey])
219
+ expect(r2.keys).toStrictEqual([DynamicKey])
220
+ }, Effect.provide(TestLayer)),
221
+ { timeout: 10_000 }
222
+ )
223
+
224
+ it.live(
225
+ "command failure (V2): keys accumulated before fail still reach the client; original error re-thrown",
226
+ Effect.fnUntraced(function*() {
227
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
228
+ const { result, keys } = yield* withCapture(client.DoAndFail.handler())
229
+ expect(Exit.isFailure(result)).toBe(true)
230
+ if (Exit.isFailure(result)) {
231
+ const failures = (result.cause as any).reasons as ReadonlyArray<{ _tag: "Fail"; error: any }>
232
+ expect(failures[0]?.error?._tag).toBe("CmdBoom")
233
+ expect(failures[0]?.error?.reason).toBe("intentional failure")
234
+ }
235
+ expect(keys).toStrictEqual([DynamicKey])
236
+ }, Effect.provide(TestLayer)),
237
+ { timeout: 10_000 }
238
+ )
239
+
240
+ it.live(
241
+ "stream: per-chunk metadata drains keys mid-stream",
242
+ Effect.fnUntraced(function*() {
243
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
244
+ const ref = yield* Ref.make<ReadonlyArray<Invalidation.InvalidationKey>>([])
245
+ const svc = makeInvalidationKeysService(ref)
246
+ const values = yield* Stream.runCollect(client.StreamWithKey.handler()).pipe(
247
+ Effect.provideService(InvalidationKeysFromServer, svc)
248
+ )
249
+ const keys = yield* Ref.get(ref)
250
+ expect(values).toStrictEqual([1, 2, 3])
251
+ // Handler taps `InvalidationSet.use` once per emitted value; routing's V3 mid-stream
252
+ // metadata drain forwards each batch as it arrives.
253
+ expect(keys).toStrictEqual([StreamKey, StreamKey, StreamKey])
254
+ }, Effect.provide(TestLayer)),
255
+ { timeout: 10_000 }
256
+ )
@@ -1,19 +1,24 @@
1
1
  import { NodeHttpServer } from "@effect/platform-node"
2
2
  import { expect, expectTypeOf, it } from "@effect/vitest"
3
- import { Console, Effect, Layer, Result } from "effect"
4
- import { S } from "effect-app"
5
3
  import { NotLoggedInError } from "effect-app/client"
4
+ import * as Context from "effect-app/Context"
6
5
  import { HttpRouter } from "effect-app/http"
7
6
  import { DefaultGenericMiddlewares } from "effect-app/middleware"
8
- import { MiddlewareMaker } from "effect-app/rpc"
9
- import { middlewareGroup } from "effect-app/rpc/MiddlewareMaker"
7
+ import * as RpcX from "effect-app/rpc"
8
+ import * as S from "effect-app/Schema"
9
+ import * as Console from "effect/Console"
10
+ import * as Effect from "effect/Effect"
11
+ import * as Layer from "effect/Layer"
12
+ import * as Ref from "effect/Ref"
13
+ import * as Result from "effect/Result"
10
14
  import { FetchHttpClient } from "effect/unstable/http"
11
- import { RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
15
+ import { Rpc, RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
12
16
  import { createServer } from "http"
13
- import { DefaultGenericMiddlewaresLive } from "../src/api/routing.js"
17
+ import { DefaultGenericMiddlewaresLive } from "../src/routing.js"
14
18
  import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive, UserProfile } from "./fixtures.js"
15
19
 
16
- const incomplete = MiddlewareMaker
20
+ const incomplete = RpcX
21
+ .MiddlewareMaker
17
22
  .Tag<middleware>()("MiddlewareMaker", RequestContextMap)
18
23
  .middleware(RequireRoles)
19
24
  .middleware(AllowAnonymous, Test)
@@ -21,7 +26,8 @@ const incomplete = MiddlewareMaker
21
26
  // this extension is allowed otherwise the error is quite obscure
22
27
  export class incompleteMiddleware extends incomplete {}
23
28
 
24
- class middleware extends MiddlewareMaker
29
+ class middleware extends RpcX
30
+ .MiddlewareMaker
25
31
  .Tag<middleware>()("MiddlewareMaker", RequestContextMap)
26
32
  .middleware(RequireRoles)
27
33
  .middleware(AllowAnonymous, Test)
@@ -29,7 +35,7 @@ class middleware extends MiddlewareMaker
29
35
  .middleware(...DefaultGenericMiddlewares)
30
36
  {}
31
37
 
32
- const UserRpcs = middlewareGroup(middleware)(
38
+ const UserRpcs = RpcX.MiddlewareMaker.middlewareGroup(middleware)(
33
39
  RpcGroup.make(
34
40
  middleware.rpc("getUser", {
35
41
  success: S.Literal("awesome")
@@ -56,7 +62,7 @@ const impl = UserRpcs
56
62
 
57
63
  expectTypeOf<Layer.Services<typeof impl>>().toEqualTypeOf<never>()
58
64
 
59
- const UserRpcsBad = middlewareGroup(middleware)(
65
+ const UserRpcsBad = RpcX.MiddlewareMaker.middlewareGroup(middleware)(
60
66
  RpcGroup.make(
61
67
  middleware.rpc("doSomethingElse", {
62
68
  success: S.Literal("also-awesome2"),
@@ -136,3 +142,72 @@ it.live(
136
142
  Effect.provide(RpcTestLayer)
137
143
  )
138
144
  )
145
+
146
+ // Per-request service isolation test
147
+
148
+ class PerRequestCounter extends Context.Service<PerRequestCounter>()(
149
+ "PerRequestCounter",
150
+ { make: Effect.sync(() => ({ a: 0 })) }
151
+ ) {
152
+ static Default = Layer.effect(this, this.make)
153
+ }
154
+
155
+ class GlobalCounter extends Context.Service<GlobalCounter, {
156
+ readonly ref: Ref.Ref<number>
157
+ }>()("GlobalCounter") {}
158
+
159
+ const CounterRpcs = RpcGroup.make(
160
+ Rpc.make("incrementA", {
161
+ success: S.Number
162
+ }),
163
+ Rpc.make("incrementB", {
164
+ success: S.Number
165
+ })
166
+ )
167
+
168
+ const counterImpl = CounterRpcs
169
+ .toLayer({
170
+ incrementA: Effect.fn(function*() {
171
+ const counter = yield* PerRequestCounter
172
+ counter.a++
173
+ const global = yield* GlobalCounter
174
+ yield* Ref.update(global.ref, (n) => n + 1)
175
+ return counter.a
176
+ }, Effect.provide(PerRequestCounter.Default)),
177
+ incrementB: Effect.fn(function*() {
178
+ const counter = yield* PerRequestCounter
179
+ counter.a++
180
+ const global = yield* GlobalCounter
181
+ yield* Ref.update(global.ref, (n) => n + 1)
182
+ return counter.a
183
+ }, Effect.provide(PerRequestCounter.Default))
184
+ })
185
+
186
+ const GlobalCounterLive = Layer.effect(
187
+ GlobalCounter,
188
+ Ref.make(0).pipe(Effect.map((ref) => ({ ref })))
189
+ )
190
+
191
+ const CounterTestLayer = counterImpl.pipe(Layer.provideMerge(GlobalCounterLive))
192
+
193
+ it.live(
194
+ "per-request service isolation with shared global counter",
195
+ Effect.fnUntraced(
196
+ function*() {
197
+ const client = yield* RpcTest.makeClient(CounterRpcs)
198
+ const global = yield* GlobalCounter
199
+
200
+ const r1 = yield* client.incrementA()
201
+ const r2 = yield* client.incrementB()
202
+
203
+ // per-request counter is fresh each time → both return 1
204
+ expect(r1).toBe(1)
205
+ expect(r2).toBe(1)
206
+
207
+ // global counter is shared across requests → accumulates to 2
208
+ const globalCount = yield* Ref.get(global.ref)
209
+ expect(globalCount).toBe(2)
210
+ },
211
+ Effect.provide(CounterTestLayer)
212
+ )
213
+ )
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Full-stack stream test exercising the entire wrapper:
3
+ * resources (TaggedRequestFor)
4
+ * → controllers (Router(...)({ effect }))
5
+ * → router (makeRouter / matchAll)
6
+ * → api ClientFactory (ApiClientFactory.makeFor)
7
+ *
8
+ * Server runs over real HTTP (NodeHttpServer on a loopback port). Client uses
9
+ * FetchHttpClient through ApiClientFactory. This covers the wrapper-level
10
+ * `Stream` request constructor end-to-end.
11
+ */
12
+ import { NodeHttpServer } from "@effect/platform-node"
13
+ import { expect, it } from "@effect/vitest"
14
+ import { ApiClientFactory, makeRpcClient } from "effect-app/client"
15
+ import { HttpRouter, HttpServer } from "effect-app/http"
16
+ import { DefaultGenericMiddlewares } from "effect-app/middleware"
17
+ import { MiddlewareMaker } from "effect-app/rpc"
18
+ import * as S from "effect-app/Schema"
19
+ import { TaggedErrorClass } from "effect-app/Schema"
20
+ import * as Effect from "effect/Effect"
21
+ import * as Exit from "effect/Exit"
22
+ import * as Layer from "effect/Layer"
23
+ import * as Option from "effect/Option"
24
+ import * as Stream from "effect/Stream"
25
+ import { FetchHttpClient } from "effect/unstable/http"
26
+ import { RpcSerialization } from "effect/unstable/rpc"
27
+ import { createServer } from "http"
28
+ import { makeRouter } from "../src/routing.js"
29
+ import { DefaultGenericMiddlewaresLive } from "../src/routing/middleware.js"
30
+ import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, SomeElseMiddleware, SomeElseMiddlewareLive, SomeService, Test, TestLive } from "./fixtures.js"
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Middleware (mirrors the boilerplate AppMiddleware shape).
34
+ // ---------------------------------------------------------------------------
35
+
36
+ class AppMiddleware extends MiddlewareMaker
37
+ .Tag<AppMiddleware>()("AppMiddleware", RequestContextMap)
38
+ .middleware(RequireRoles, Test)
39
+ .middleware(AllowAnonymous)
40
+ .middleware(SomeElseMiddleware)
41
+ .middleware(...DefaultGenericMiddlewares)
42
+ {
43
+ static Default = this.layer.pipe(
44
+ Layer.provide(
45
+ [
46
+ RequireRolesLive.pipe(Layer.provide(SomeService.Default)),
47
+ AllowAnonymousLive,
48
+ TestLive,
49
+ SomeElseMiddlewareLive,
50
+ DefaultGenericMiddlewaresLive
51
+ ] as const
52
+ )
53
+ )
54
+ }
55
+
56
+ const { Router, matchAll } = makeRouter(AppMiddleware.Default)
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Resources — Stream with and without payload.
60
+ // ---------------------------------------------------------------------------
61
+
62
+ const { TaggedRequestFor } = makeRpcClient(AppMiddleware)
63
+ const Req = TaggedRequestFor("Streamy")
64
+
65
+ class StreamTicks extends Req.Command<StreamTicks>()("StreamTicks", {}, {
66
+ stream: true,
67
+ allowAnonymous: true,
68
+ success: S.Number
69
+ }) {}
70
+
71
+ class StreamCountTo extends Req.Command<StreamCountTo>()("StreamCountTo", {
72
+ to: S.Number
73
+ }, {
74
+ stream: true,
75
+ allowAnonymous: true,
76
+ success: S.Number
77
+ }) {}
78
+
79
+ class StreamRealtime extends Req.Command<StreamRealtime>()("StreamRealtime", {}, {
80
+ stream: true,
81
+ allowAnonymous: true,
82
+ success: S.Number
83
+ }) {}
84
+
85
+ class StreamBoom extends TaggedErrorClass<StreamBoom>()("StreamBoom", { reason: S.String }) {}
86
+
87
+ class StreamFailStream extends Req.Command<StreamFailStream>()("StreamFailStream", {}, {
88
+ stream: true,
89
+ allowAnonymous: true,
90
+ success: S.Number,
91
+ error: StreamBoom
92
+ }) {}
93
+
94
+ class StreamNoSuccess extends Req.Command<StreamNoSuccess>()("StreamNoSuccess", {}, {
95
+ stream: true,
96
+ allowAnonymous: true
97
+ }) {}
98
+
99
+ // Defaults to allowAnonymous: false → AllowAnonymous middleware fails with NotLoggedInError
100
+ // when the request lacks the `x-user` header.
101
+ class StreamRequiresAuth extends Req.Command<StreamRequiresAuth>()("StreamRequiresAuth", {}, {
102
+ stream: true,
103
+ success: S.Number
104
+ }) {}
105
+
106
+ class CommandRequiresAuth extends Req.Command<CommandRequiresAuth>()("CommandRequiresAuth", {}, {
107
+ success: S.Number
108
+ }) {}
109
+
110
+ class QueryRequiresAuth extends Req.Query<QueryRequiresAuth>()("QueryRequiresAuth", {}, {
111
+ success: S.Number
112
+ }) {}
113
+
114
+ const StreamyRsc = {
115
+ StreamTicks,
116
+ StreamCountTo,
117
+ StreamRealtime,
118
+ StreamFailStream,
119
+ StreamNoSuccess,
120
+ StreamRequiresAuth,
121
+ CommandRequiresAuth,
122
+ QueryRequiresAuth
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // Controllers / router — Stream impls returned from the match callback.
127
+ // ---------------------------------------------------------------------------
128
+
129
+ const router = Router(StreamyRsc)({
130
+ *effect(match) {
131
+ return match({
132
+ StreamTicks: () => Stream.fromIterable([10, 20, 30]),
133
+ StreamCountTo: ({ to }: { readonly to: number }) =>
134
+ Effect
135
+ .gen(function*() {
136
+ return Stream.range(1, to)
137
+ })
138
+ .pipe(Stream.unwrap),
139
+ // emits 3 values 100ms apart so the test can prove element-by-element
140
+ // delivery rather than a single batched response
141
+ StreamRealtime: () =>
142
+ Stream.fromIterable([1, 2, 3]).pipe(
143
+ Stream.mapEffect((n) => Effect.sleep("100 millis").pipe(Effect.as(n)))
144
+ ),
145
+ StreamFailStream: () => Stream.fail(new StreamBoom({ reason: "from-stream" })),
146
+ StreamNoSuccess: () => Stream.empty,
147
+ // handlers below are unreachable when middleware-auth fails; bodies exist
148
+ // only so the resource type-checks
149
+ StreamRequiresAuth: () => Stream.fromIterable([1, 2, 3]),
150
+ CommandRequiresAuth: () => Effect.succeed(1),
151
+ QueryRequiresAuth: () => Effect.succeed(1)
152
+ })
153
+ }
154
+ })
155
+
156
+ const RpcRouterLayer = matchAll({ router })
157
+
158
+ // ---------------------------------------------------------------------------
159
+ // HTTP wiring — real server on a loopback port + FetchHttpClient on the client.
160
+ // ---------------------------------------------------------------------------
161
+
162
+ // Server binds an ephemeral port (port: 0). The actual URL is read from the
163
+ // `HttpServer` service after binding, then fed into `ApiClientFactory.layer` so
164
+ // each `it.live` scope gets a fresh server without colliding on a fixed port.
165
+ const NodeServerLayer = NodeHttpServer.layer(() => createServer(), { port: 0 })
166
+
167
+ const ServerLayer = HttpRouter
168
+ .serve(RpcRouterLayer)
169
+ .pipe(
170
+ Layer.provide(NodeServerLayer),
171
+ Layer.provide(RpcSerialization.layerNdjson)
172
+ )
173
+
174
+ const ClientLayer = Layer
175
+ .unwrap(
176
+ Effect.gen(function*() {
177
+ const server = yield* HttpServer.HttpServer
178
+ const addr = server.address
179
+ if (addr._tag !== "TcpAddress") return yield* Effect.die(new Error("expected TcpAddress"))
180
+ const host = addr.hostname === "0.0.0.0" ? "127.0.0.1" : addr.hostname
181
+ const url = `http://${host}:${addr.port}`
182
+ return ApiClientFactory
183
+ .layer({ url, headers: Option.none() })
184
+ .pipe(Layer.provide(FetchHttpClient.layer))
185
+ })
186
+ )
187
+ .pipe(Layer.provide(NodeServerLayer))
188
+
189
+ const TestLayer = Layer.mergeAll(ServerLayer, ClientLayer)
190
+
191
+ // ---------------------------------------------------------------------------
192
+ // Tests
193
+ // ---------------------------------------------------------------------------
194
+
195
+ it.live(
196
+ "stream resource without input: ApiClientFactory client emits all values",
197
+ Effect.fnUntraced(function*() {
198
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
199
+ const values = yield* Stream.runCollect(client.StreamTicks.handler())
200
+ expect(values).toStrictEqual([10, 20, 30])
201
+ }, Effect.provide(TestLayer)),
202
+ { timeout: 10_000 }
203
+ )
204
+
205
+ it.live(
206
+ "stream resource with input: payload drives the emitted values",
207
+ Effect.fnUntraced(function*() {
208
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
209
+ const values = yield* Stream.runCollect(client.StreamCountTo.handler({ to: 4 }))
210
+ expect(values).toStrictEqual([1, 2, 3, 4])
211
+ }, Effect.provide(TestLayer)),
212
+ { timeout: 10_000 }
213
+ )
214
+
215
+ it.live(
216
+ "stream resource is delivered element-by-element in real time (not batched)",
217
+ Effect.fnUntraced(function*() {
218
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
219
+ const start = Date.now()
220
+ const arrivals = yield* Stream.runCollect(
221
+ client.StreamRealtime.handler().pipe(
222
+ Stream.map((n) => ({ n, at: Date.now() - start }))
223
+ )
224
+ )
225
+ expect(arrivals.map((_) => _.n)).toStrictEqual([1, 2, 3])
226
+ // server emits each value 100ms after the previous one. If the response
227
+ // were batched, deltas would be ~0ms. Allow generous slack for CI jitter
228
+ // but require clear separation between consecutive arrivals.
229
+ const delta1 = arrivals[1]!.at - arrivals[0]!.at
230
+ const delta2 = arrivals[2]!.at - arrivals[1]!.at
231
+ expect(delta1).toBeGreaterThan(50)
232
+ expect(delta2).toBeGreaterThan(50)
233
+ // first element should not be withheld until the whole stream completes
234
+ expect(arrivals[0]!.at).toBeLessThan(arrivals[2]!.at - 50)
235
+ }, Effect.provide(TestLayer)),
236
+ { timeout: 10_000 }
237
+ )
238
+
239
+ it.live(
240
+ "stream handler returning Stream.fail surfaces as failing stream on client",
241
+ Effect.fnUntraced(function*() {
242
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
243
+ const exit = yield* Stream.runCollect(client.StreamFailStream.handler()).pipe(Effect.exit)
244
+ expect(Exit.isFailure(exit)).toBe(true)
245
+ if (Exit.isFailure(exit)) {
246
+ const failures = (exit.cause as any).reasons as ReadonlyArray<{ _tag: "Fail"; error: StreamBoom }>
247
+ expect(failures.length).toBeGreaterThan(0)
248
+ expect(failures[0]!.error._tag).toBe("StreamBoom")
249
+ expect(failures[0]!.error.reason).toBe("from-stream")
250
+ }
251
+ }, Effect.provide(TestLayer)),
252
+ { timeout: 10_000 }
253
+ )
254
+
255
+ it.live(
256
+ "stream resource without `success` exposes handler as a Stream on the client",
257
+ Effect.fnUntraced(function*() {
258
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
259
+ const exit = yield* Stream.runCollect(client.StreamNoSuccess.handler()).pipe(Effect.exit)
260
+ expect(Exit.isSuccess(exit)).toBe(true)
261
+ }, Effect.provide(TestLayer)),
262
+ { timeout: 10_000 }
263
+ )
264
+
265
+ const expectNotLoggedIn = (exit: Exit.Exit<unknown, unknown>) => {
266
+ expect(Exit.isFailure(exit)).toBe(true)
267
+ if (!Exit.isFailure(exit)) return
268
+ const failures = (exit.cause as any).reasons as ReadonlyArray<{ _tag: "Fail"; error: any }>
269
+ expect(failures.length).toBeGreaterThan(0)
270
+ // The bug surfaces here as a SchemaError ("Expected never | { _tag: 'error', ... }")
271
+ // because the middleware-thrown NotLoggedInError doesn't match the
272
+ // wire StreamFailureChunk / CommandFailureWithMetaData wrapping.
273
+ expect(failures[0]!.error?._tag).toBe("NotLoggedInError")
274
+ }
275
+
276
+ it.live(
277
+ "stream resource: middleware-emitted NotLoggedInError surfaces cleanly on the client",
278
+ Effect.fnUntraced(function*() {
279
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
280
+ const exit = yield* Stream.runCollect(client.StreamRequiresAuth.handler()).pipe(Effect.exit)
281
+ expectNotLoggedIn(exit)
282
+ }, Effect.provide(TestLayer)),
283
+ { timeout: 10_000 }
284
+ )
285
+
286
+ it.live(
287
+ "command resource: middleware-emitted NotLoggedInError surfaces cleanly on the client",
288
+ Effect.fnUntraced(function*() {
289
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
290
+ const exit = yield* client.CommandRequiresAuth.handler().pipe(Effect.exit)
291
+ expectNotLoggedIn(exit)
292
+ }, Effect.provide(TestLayer)),
293
+ { timeout: 10_000 }
294
+ )
295
+
296
+ it.live(
297
+ "query resource: middleware-emitted NotLoggedInError surfaces cleanly on the client",
298
+ Effect.fnUntraced(function*() {
299
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
300
+ const exit = yield* client.QueryRequiresAuth.handler().pipe(Effect.exit)
301
+ expectNotLoggedIn(exit)
302
+ }, Effect.provide(TestLayer)),
303
+ { timeout: 10_000 }
304
+ )