@effect-app/infra 4.0.0-beta.32 → 4.0.0-beta.321

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 (480) hide show
  1. package/CHANGELOG.md +2469 -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 +36 -15
  6. package/dist/ClusterCosmos.d.ts +70 -0
  7. package/dist/ClusterCosmos.d.ts.map +1 -0
  8. package/dist/ClusterCosmos.js +576 -0
  9. package/dist/ClusterServiceBus.d.ts +67 -0
  10. package/dist/ClusterServiceBus.d.ts.map +1 -0
  11. package/dist/ClusterServiceBus.js +84 -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 +16 -4
  33. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  34. package/dist/Store/Cosmos/query.js +238 -42
  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 +392 -256
  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 +87 -42
  41. package/dist/Store/Memory.d.ts +7 -10
  42. package/dist/Store/Memory.d.ts.map +1 -1
  43. package/dist/Store/Memory.js +346 -76
  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 +257 -0
  47. package/dist/Store/SQL/query.d.ts +53 -0
  48. package/dist/Store/SQL/query.d.ts.map +1 -0
  49. package/dist/Store/SQL/query.js +656 -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 +478 -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 +71 -22
  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/jsonDocument.d.ts +13 -0
  60. package/dist/Store/jsonDocument.d.ts.map +1 -0
  61. package/dist/Store/jsonDocument.js +31 -0
  62. package/dist/Store/utils.d.ts +21 -2
  63. package/dist/Store/utils.d.ts.map +1 -1
  64. package/dist/Store/utils.js +253 -10
  65. package/dist/WorkflowEngineCosmos.d.ts +29 -0
  66. package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
  67. package/dist/WorkflowEngineCosmos.js +542 -0
  68. package/dist/WorkflowEngineSqlite.d.ts +24 -0
  69. package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
  70. package/dist/WorkflowEngineSqlite.js +550 -0
  71. package/dist/arbs.d.ts +6 -3
  72. package/dist/arbs.d.ts.map +1 -1
  73. package/dist/arbs.js +16 -4
  74. package/dist/codec.d.ts.map +1 -0
  75. package/dist/codec.js +5 -0
  76. package/dist/{adapters/cosmos-client.d.ts → cosmos-client.d.ts} +4 -2
  77. package/dist/cosmos-client.d.ts.map +1 -0
  78. package/dist/cosmos-client.js +11 -0
  79. package/dist/errorReporter.d.ts +6 -4
  80. package/dist/errorReporter.d.ts.map +1 -1
  81. package/dist/errorReporter.js +23 -27
  82. package/dist/fileUtil.d.ts +1 -1
  83. package/dist/fileUtil.d.ts.map +1 -1
  84. package/dist/fileUtil.js +2 -2
  85. package/dist/index.d.ts +2 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +3 -2
  88. package/dist/internal/RequestContextMiddleware.d.ts +5 -0
  89. package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
  90. package/dist/internal/RequestContextMiddleware.js +45 -0
  91. package/dist/internal/auth.d.ts +53 -0
  92. package/dist/internal/auth.d.ts.map +1 -0
  93. package/dist/internal/auth.js +180 -0
  94. package/dist/internal/events.d.ts +11 -0
  95. package/dist/internal/events.d.ts.map +1 -0
  96. package/dist/internal/events.js +49 -0
  97. package/dist/internal/health.d.ts.map +1 -0
  98. package/dist/internal/health.js +5 -0
  99. package/dist/{api/layerUtils.d.ts → layerUtils.d.ts} +14 -6
  100. package/dist/layerUtils.d.ts.map +1 -0
  101. package/dist/layerUtils.js +17 -0
  102. package/dist/logger/jsonLogger.d.ts +1 -1
  103. package/dist/logger/jsonLogger.d.ts.map +1 -1
  104. package/dist/logger/jsonLogger.js +5 -3
  105. package/dist/logger/logFmtLogger.d.ts +1 -1
  106. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  107. package/dist/logger/logFmtLogger.js +3 -3
  108. package/dist/logger/shared.d.ts +2 -2
  109. package/dist/logger/shared.d.ts.map +1 -1
  110. package/dist/logger/shared.js +6 -6
  111. package/dist/logger.d.ts +4 -4
  112. package/dist/logger.d.ts.map +1 -1
  113. package/dist/middlewares.d.ts.map +1 -0
  114. package/dist/{api/middlewares.js → middlewares.js} +1 -1
  115. package/dist/{adapters/mongo-client.d.ts → mongo-client.d.ts} +3 -2
  116. package/dist/mongo-client.d.ts.map +1 -0
  117. package/dist/mongo-client.js +15 -0
  118. package/dist/otel.d.ts +75 -0
  119. package/dist/otel.d.ts.map +1 -0
  120. package/dist/otel.js +84 -0
  121. package/dist/rateLimit.d.ts +11 -3
  122. package/dist/rateLimit.d.ts.map +1 -1
  123. package/dist/rateLimit.js +7 -12
  124. package/dist/{adapters/redis-client.d.ts → redis-client.d.ts} +5 -2
  125. package/dist/redis-client.d.ts.map +1 -0
  126. package/dist/redis-client.js +98 -0
  127. package/dist/{api/reportError.d.ts → reportError.d.ts} +1 -1
  128. package/dist/reportError.d.ts.map +1 -0
  129. package/dist/reportError.js +28 -0
  130. package/dist/{api/routing → routing}/middleware/RouterMiddleware.d.ts +4 -3
  131. package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
  132. package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
  133. package/dist/routing/middleware/middleware.d.ts +48 -0
  134. package/dist/routing/middleware/middleware.d.ts.map +1 -0
  135. package/dist/routing/middleware/middleware.js +152 -0
  136. package/dist/{api/routing → routing}/middleware.d.ts +0 -1
  137. package/dist/routing/middleware.d.ts.map +1 -0
  138. package/dist/{api/routing → routing}/middleware.js +1 -2
  139. package/dist/routing/schema/jwt.d.ts.map +1 -0
  140. package/dist/routing/schema/jwt.js +12 -0
  141. package/dist/routing/tsort.d.ts.map +1 -0
  142. package/dist/routing/tsort.js +51 -0
  143. package/dist/{api/routing → routing}/utils.d.ts +3 -3
  144. package/dist/routing/utils.d.ts.map +1 -0
  145. package/dist/routing/utils.js +45 -0
  146. package/dist/routing.d.ts +182 -0
  147. package/dist/routing.d.ts.map +1 -0
  148. package/dist/routing.js +256 -0
  149. package/dist/test.d.ts +2 -2
  150. package/dist/test.d.ts.map +1 -1
  151. package/dist/test.js +5 -5
  152. package/dist/util.d.ts.map +1 -0
  153. package/dist/util.js +14 -0
  154. package/docs/cluster-storage.md +26 -0
  155. package/docs/workflow-engine.md +262 -0
  156. package/examples/query.ts +49 -41
  157. package/package.json +38 -352
  158. package/src/CUPS.ts +54 -15
  159. package/src/ClusterCosmos.ts +1154 -0
  160. package/src/ClusterServiceBus.ts +244 -0
  161. package/src/{api/ContextProvider.ts → ContextProvider.ts} +20 -17
  162. package/src/Emailer/Sendgrid.ts +83 -60
  163. package/src/Emailer/fake.ts +4 -4
  164. package/src/MainFiberSet.ts +32 -13
  165. package/src/QueueMaker/errors.ts +3 -1
  166. package/src/{adapters/SQL → SQL}/Model.ts +42 -41
  167. package/src/SQL.ts +1 -0
  168. package/src/Store/Cosmos/query.ts +275 -55
  169. package/src/Store/Cosmos.ts +537 -367
  170. package/src/Store/Disk.ts +137 -74
  171. package/src/Store/Memory.ts +402 -113
  172. package/src/Store/SQL/Pg.ts +402 -0
  173. package/src/Store/SQL/query.ts +748 -0
  174. package/src/Store/SQL.ts +786 -0
  175. package/src/Store/codeFilter.ts +78 -28
  176. package/src/Store/index.ts +25 -10
  177. package/src/Store/jsonDocument.ts +43 -0
  178. package/src/Store/utils.ts +307 -30
  179. package/src/WorkflowEngineCosmos.ts +749 -0
  180. package/src/WorkflowEngineSqlite.ts +813 -0
  181. package/src/arbs.ts +20 -5
  182. package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
  183. package/src/errorReporter.ts +68 -78
  184. package/src/fileUtil.ts +1 -1
  185. package/src/index.ts +2 -1
  186. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  187. package/src/internal/auth.ts +272 -0
  188. package/src/{api/internal → internal}/events.ts +22 -13
  189. package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
  190. package/src/logger/jsonLogger.ts +5 -3
  191. package/src/logger/logFmtLogger.ts +3 -3
  192. package/src/logger/shared.ts +6 -5
  193. package/src/middlewares.ts +10 -0
  194. package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
  195. package/src/otel.ts +177 -0
  196. package/src/rateLimit.ts +35 -24
  197. package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
  198. package/src/{api/reportError.ts → reportError.ts} +3 -2
  199. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
  200. package/src/routing/middleware/middleware.ts +196 -0
  201. package/src/routing/middleware.ts +4 -0
  202. package/src/{api/routing → routing}/schema/jwt.ts +2 -2
  203. package/src/{api/routing → routing}/utils.ts +2 -1
  204. package/src/routing.ts +823 -0
  205. package/src/test.ts +6 -6
  206. package/src/vitest.ts +1 -1
  207. package/test/auth.test.ts +101 -0
  208. package/test/cluster-cosmos.test.ts +612 -0
  209. package/test/cluster-servicebus.test.ts +180 -0
  210. package/test/cluster-sqlite.test.ts +207 -0
  211. package/test/contextProvider.test.ts +15 -12
  212. package/test/controller.test.ts +28 -32
  213. package/test/cosmos-query.test.ts +323 -0
  214. package/test/dist/auth.test.d.ts.map +1 -0
  215. package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
  216. package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
  217. package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
  218. package/test/dist/contextProvider.test.d.ts.map +1 -1
  219. package/test/dist/controller.test.d.ts.map +1 -1
  220. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  221. package/test/dist/fixtures.d.ts +29 -11
  222. package/test/dist/fixtures.d.ts.map +1 -1
  223. package/test/dist/fixtures.js +17 -10
  224. package/test/dist/json-lower.test.d.ts.map +1 -0
  225. package/test/dist/query.test.d.ts.map +1 -1
  226. package/test/dist/rawQuery.test.d.ts.map +1 -1
  227. package/test/dist/repository-ext.test.d.ts.map +1 -0
  228. package/test/dist/requires.test.d.ts.map +1 -1
  229. package/test/dist/router-generator.test.d.ts.map +1 -0
  230. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  231. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  232. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  233. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  234. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  235. package/test/dist/sql-store.test.d.ts.map +1 -0
  236. package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
  237. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  238. package/test/fixtures.ts +16 -9
  239. package/test/json-lower.test.ts +115 -0
  240. package/test/layerUtils.test.ts +2 -2
  241. package/test/query.test.ts +1384 -46
  242. package/test/rawQuery.test.ts +340 -22
  243. package/test/repository-ext.test.ts +456 -0
  244. package/test/requires.test.ts +10 -5
  245. package/test/router-generator.test.ts +187 -0
  246. package/test/routing-interruptibility.test.ts +66 -0
  247. package/test/rpc-context-map-streaming.test.ts +265 -0
  248. package/test/rpc-e2e-invalidation.test.ts +421 -0
  249. package/test/rpc-multi-middleware.test.ts +85 -10
  250. package/test/rpc-stream-fullstack.test.ts +307 -0
  251. package/test/sql-store.test.ts +1905 -0
  252. package/test/validateSample.test.ts +26 -14
  253. package/test/workflow-engine-cosmos.test.ts +345 -0
  254. package/test/workflow-engine-sqlite.test.ts +297 -0
  255. package/tsconfig.examples.json +1 -1
  256. package/tsconfig.json +9 -2
  257. package/tsconfig.src.json +35 -35
  258. package/tsconfig.test.json +2 -2
  259. package/dist/Emailer/service.d.ts +0 -55
  260. package/dist/Emailer/service.d.ts.map +0 -1
  261. package/dist/Emailer/service.js +0 -6
  262. package/dist/Emailer.d.ts +0 -2
  263. package/dist/Emailer.d.ts.map +0 -1
  264. package/dist/Emailer.js +0 -2
  265. package/dist/Model/Repository/ext.d.ts +0 -41
  266. package/dist/Model/Repository/ext.d.ts.map +0 -1
  267. package/dist/Model/Repository/ext.js +0 -65
  268. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  269. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  270. package/dist/Model/Repository/internal/internal.js +0 -316
  271. package/dist/Model/Repository/legacy.d.ts +0 -19
  272. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  273. package/dist/Model/Repository/legacy.js +0 -2
  274. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  275. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  276. package/dist/Model/Repository/makeRepo.js +0 -24
  277. package/dist/Model/Repository/service.d.ts +0 -89
  278. package/dist/Model/Repository/service.d.ts.map +0 -1
  279. package/dist/Model/Repository/service.js +0 -2
  280. package/dist/Model/Repository/validation.d.ts +0 -42
  281. package/dist/Model/Repository/validation.d.ts.map +0 -1
  282. package/dist/Model/Repository/validation.js +0 -32
  283. package/dist/Model/Repository.d.ts +0 -6
  284. package/dist/Model/Repository.d.ts.map +0 -1
  285. package/dist/Model/Repository.js +0 -6
  286. package/dist/Model/dsl.d.ts +0 -32
  287. package/dist/Model/dsl.d.ts.map +0 -1
  288. package/dist/Model/dsl.js +0 -44
  289. package/dist/Model/filter/filterApi.d.ts +0 -30
  290. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  291. package/dist/Model/filter/filterApi.js +0 -2
  292. package/dist/Model/filter/types/errors.d.ts +0 -29
  293. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  294. package/dist/Model/filter/types/errors.js +0 -2
  295. package/dist/Model/filter/types/fields.d.ts +0 -15
  296. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  297. package/dist/Model/filter/types/fields.js +0 -2
  298. package/dist/Model/filter/types/path/common.d.ts +0 -316
  299. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  300. package/dist/Model/filter/types/path/common.js +0 -2
  301. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  302. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  303. package/dist/Model/filter/types/path/eager.js +0 -31
  304. package/dist/Model/filter/types/path/index.d.ts +0 -4
  305. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  306. package/dist/Model/filter/types/path/index.js +0 -3
  307. package/dist/Model/filter/types/utils.d.ts +0 -79
  308. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  309. package/dist/Model/filter/types/utils.js +0 -2
  310. package/dist/Model/filter/types/validator.d.ts +0 -30
  311. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  312. package/dist/Model/filter/types/validator.js +0 -2
  313. package/dist/Model/filter/types.d.ts +0 -5
  314. package/dist/Model/filter/types.d.ts.map +0 -1
  315. package/dist/Model/filter/types.js +0 -7
  316. package/dist/Model/query/dsl.d.ts +0 -248
  317. package/dist/Model/query/dsl.d.ts.map +0 -1
  318. package/dist/Model/query/dsl.js +0 -104
  319. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  320. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  321. package/dist/Model/query/new-kid-interpreter.js +0 -165
  322. package/dist/Model/query.d.ts +0 -15
  323. package/dist/Model/query.d.ts.map +0 -1
  324. package/dist/Model/query.js +0 -3
  325. package/dist/Model.d.ts +0 -4
  326. package/dist/Model.d.ts.map +0 -1
  327. package/dist/Model.js +0 -4
  328. package/dist/Operations.d.ts +0 -55
  329. package/dist/Operations.d.ts.map +0 -1
  330. package/dist/Operations.js +0 -102
  331. package/dist/OperationsRepo.d.ts +0 -41
  332. package/dist/OperationsRepo.d.ts.map +0 -1
  333. package/dist/OperationsRepo.js +0 -14
  334. package/dist/QueueMaker/SQLQueue.d.ts +0 -16
  335. package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
  336. package/dist/QueueMaker/SQLQueue.js +0 -130
  337. package/dist/QueueMaker/memQueue.d.ts +0 -11
  338. package/dist/QueueMaker/memQueue.d.ts.map +0 -1
  339. package/dist/QueueMaker/memQueue.js +0 -76
  340. package/dist/QueueMaker/sbqueue.d.ts +0 -12
  341. package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
  342. package/dist/QueueMaker/sbqueue.js +0 -91
  343. package/dist/QueueMaker/service.d.ts +0 -11
  344. package/dist/QueueMaker/service.d.ts.map +0 -1
  345. package/dist/QueueMaker/service.js +0 -4
  346. package/dist/RequestContext.d.ts +0 -63
  347. package/dist/RequestContext.d.ts.map +0 -1
  348. package/dist/RequestContext.js +0 -49
  349. package/dist/RequestFiberSet.d.ts +0 -36
  350. package/dist/RequestFiberSet.d.ts.map +0 -1
  351. package/dist/RequestFiberSet.js +0 -78
  352. package/dist/Store/ContextMapContainer.d.ts +0 -14
  353. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  354. package/dist/Store/ContextMapContainer.js +0 -16
  355. package/dist/Store/service.d.ts +0 -108
  356. package/dist/Store/service.d.ts.map +0 -1
  357. package/dist/Store/service.js +0 -71
  358. package/dist/Store.d.ts +0 -2
  359. package/dist/Store.d.ts.map +0 -1
  360. package/dist/Store.js +0 -2
  361. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  362. package/dist/adapters/SQL/Model.js +0 -478
  363. package/dist/adapters/SQL.d.ts.map +0 -1
  364. package/dist/adapters/ServiceBus.d.ts +0 -58
  365. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  366. package/dist/adapters/ServiceBus.js +0 -99
  367. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  368. package/dist/adapters/cosmos-client.js +0 -9
  369. package/dist/adapters/index.d.ts +0 -2
  370. package/dist/adapters/index.d.ts.map +0 -1
  371. package/dist/adapters/index.js +0 -2
  372. package/dist/adapters/logger.d.ts +0 -9
  373. package/dist/adapters/logger.d.ts.map +0 -1
  374. package/dist/adapters/logger.js +0 -3
  375. package/dist/adapters/memQueue.d.ts +0 -13
  376. package/dist/adapters/memQueue.d.ts.map +0 -1
  377. package/dist/adapters/memQueue.js +0 -20
  378. package/dist/adapters/mongo-client.d.ts.map +0 -1
  379. package/dist/adapters/mongo-client.js +0 -13
  380. package/dist/adapters/redis-client.d.ts.map +0 -1
  381. package/dist/adapters/redis-client.js +0 -94
  382. package/dist/api/ContextProvider.d.ts +0 -31
  383. package/dist/api/ContextProvider.d.ts.map +0 -1
  384. package/dist/api/ContextProvider.js +0 -38
  385. package/dist/api/codec.d.ts.map +0 -1
  386. package/dist/api/codec.js +0 -5
  387. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  388. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  389. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  390. package/dist/api/internal/auth.d.ts +0 -15
  391. package/dist/api/internal/auth.d.ts.map +0 -1
  392. package/dist/api/internal/auth.js +0 -47
  393. package/dist/api/internal/events.d.ts +0 -9
  394. package/dist/api/internal/events.d.ts.map +0 -1
  395. package/dist/api/internal/events.js +0 -42
  396. package/dist/api/internal/health.d.ts.map +0 -1
  397. package/dist/api/internal/health.js +0 -5
  398. package/dist/api/layerUtils.d.ts.map +0 -1
  399. package/dist/api/layerUtils.js +0 -16
  400. package/dist/api/middlewares.d.ts.map +0 -1
  401. package/dist/api/reportError.d.ts.map +0 -1
  402. package/dist/api/reportError.js +0 -27
  403. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  404. package/dist/api/routing/middleware/middleware.d.ts +0 -9
  405. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  406. package/dist/api/routing/middleware/middleware.js +0 -92
  407. package/dist/api/routing/middleware.d.ts.map +0 -1
  408. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  409. package/dist/api/routing/schema/jwt.js +0 -12
  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.map +0 -1
  413. package/dist/api/routing/utils.js +0 -44
  414. package/dist/api/routing.d.ts +0 -138
  415. package/dist/api/routing.d.ts.map +0 -1
  416. package/dist/api/routing.js +0 -166
  417. package/dist/api/setupRequest.d.ts +0 -12
  418. package/dist/api/setupRequest.d.ts.map +0 -1
  419. package/dist/api/setupRequest.js +0 -44
  420. package/dist/api/util.d.ts.map +0 -1
  421. package/dist/api/util.js +0 -14
  422. package/eslint.config.mjs +0 -24
  423. package/src/Emailer/service.ts +0 -52
  424. package/src/Emailer.ts +0 -1
  425. package/src/Model/Repository/ext.ts +0 -283
  426. package/src/Model/Repository/internal/internal.ts +0 -577
  427. package/src/Model/Repository/legacy.ts +0 -27
  428. package/src/Model/Repository/makeRepo.ts +0 -139
  429. package/src/Model/Repository/service.ts +0 -627
  430. package/src/Model/Repository/validation.ts +0 -31
  431. package/src/Model/Repository.ts +0 -5
  432. package/src/Model/dsl.ts +0 -128
  433. package/src/Model/filter/filterApi.ts +0 -60
  434. package/src/Model/filter/types/errors.ts +0 -47
  435. package/src/Model/filter/types/fields.ts +0 -50
  436. package/src/Model/filter/types/path/common.ts +0 -404
  437. package/src/Model/filter/types/path/eager.ts +0 -298
  438. package/src/Model/filter/types/path/index.ts +0 -4
  439. package/src/Model/filter/types/utils.ts +0 -128
  440. package/src/Model/filter/types/validator.ts +0 -46
  441. package/src/Model/filter/types.ts +0 -6
  442. package/src/Model/query/dsl.ts +0 -2110
  443. package/src/Model/query/new-kid-interpreter.ts +0 -210
  444. package/src/Model/query.ts +0 -13
  445. package/src/Model.ts +0 -3
  446. package/src/Operations.ts +0 -235
  447. package/src/OperationsRepo.ts +0 -16
  448. package/src/QueueMaker/SQLQueue.ts +0 -189
  449. package/src/QueueMaker/memQueue.ts +0 -130
  450. package/src/QueueMaker/sbqueue.ts +0 -137
  451. package/src/QueueMaker/service.ts +0 -17
  452. package/src/RequestContext.ts +0 -63
  453. package/src/RequestFiberSet.ts +0 -105
  454. package/src/Store/ContextMapContainer.ts +0 -20
  455. package/src/Store/index.test.ts.bak +0 -112
  456. package/src/Store/service.ts +0 -184
  457. package/src/Store.ts +0 -1
  458. package/src/adapters/SQL.ts +0 -1
  459. package/src/adapters/ServiceBus.ts +0 -209
  460. package/src/adapters/index.ts +0 -0
  461. package/src/adapters/logger.ts +0 -3
  462. package/src/adapters/memQueue.ts +0 -21
  463. package/src/api/internal/auth.ts +0 -68
  464. package/src/api/middlewares.ts +0 -10
  465. package/src/api/routing/middleware/middleware.ts +0 -128
  466. package/src/api/routing/middleware.ts +0 -6
  467. package/src/api/routing.ts +0 -598
  468. package/src/api/setupRequest.ts +0 -84
  469. package/tsconfig.json.bak +0 -12
  470. /package/dist/{adapters/SQL.d.ts → SQL.d.ts} +0 -0
  471. /package/dist/{api/codec.d.ts → codec.d.ts} +0 -0
  472. /package/dist/{api/internal → internal}/health.d.ts +0 -0
  473. /package/dist/{api/middlewares.d.ts → middlewares.d.ts} +0 -0
  474. /package/dist/{api/routing → routing}/schema/jwt.d.ts +0 -0
  475. /package/dist/{api/routing → routing}/tsort.d.ts +0 -0
  476. /package/dist/{api/util.d.ts → util.d.ts} +0 -0
  477. /package/src/{api/codec.ts → codec.ts} +0 -0
  478. /package/src/{api/internal → internal}/health.ts +0 -0
  479. /package/src/{api/routing → routing}/tsort.ts +0 -0
  480. /package/src/{api/util.ts → util.ts} +0 -0
@@ -0,0 +1,187 @@
1
+ import { type MakeContext, type MakeErrors, makeRouter } from "@effect-app/infra/routing"
2
+ import { expectTypeOf, it } from "@effect/vitest"
3
+ import { InvalidStateError, makeRpcClient, UnauthorizedError } from "effect-app/client"
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 { DefaultGenericMiddlewares } from "effect-app/middleware"
8
+ import { makeAllDSL, makeOneDSL } from "effect-app/Model"
9
+ import { type FixEnv } from "effect-app/Pure"
10
+ import * as RpcX from "effect-app/rpc"
11
+ import { MiddlewareMaker } from "effect-app/rpc"
12
+ import * as S from "effect-app/Schema"
13
+ import { type TypeTestId } from "effect-app/TypeTest"
14
+ import { type ConfigError } from "effect/Config"
15
+ import { type RpcSerialization } from "effect/unstable/rpc/RpcSerialization"
16
+ import { DefaultGenericMiddlewaresLive, DevModeMiddlewareLive } from "../src/routing/middleware.js"
17
+ import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElse, SomeService, Test, TestLive } from "./fixtures.js"
18
+
19
+ // Inline minimal context provider (provides `Some`)
20
+ class CtxProvider extends RpcX.RpcMiddleware.Tag<CtxProvider, { provides: Some }>()("CtxProvider") {
21
+ static Default = Layer.make(this, {
22
+ *make() {
23
+ return Effect.fnUntraced(function*(effect) {
24
+ return yield* Effect.provideService(effect, Some, Some.of({ a: 1 }))
25
+ })
26
+ }
27
+ })
28
+ }
29
+
30
+ // Provides `SomeElse` so AllowAnonymous's requirement is met.
31
+ class SomeElseProvider extends RpcX.RpcMiddleware.Tag<SomeElseProvider, { provides: SomeElse }>()("SomeElseProvider") {
32
+ static Default = Layer.make(this, {
33
+ *make() {
34
+ return Effect.fnUntraced(function*(effect) {
35
+ return yield* Effect.provideService(effect, SomeElse, SomeElse.of({ b: 2 }))
36
+ })
37
+ }
38
+ })
39
+ }
40
+
41
+ class mw extends MiddlewareMaker
42
+ .Tag<mw>()("mw", RequestContextMap)
43
+ .middleware(RequireRoles, Test)
44
+ .middleware(AllowAnonymous)
45
+ .middleware(CtxProvider)
46
+ .middleware(...DefaultGenericMiddlewares, SomeElseProvider)
47
+ {
48
+ static Default = this.layer.pipe(
49
+ Layer.provide([
50
+ RequireRolesLive,
51
+ TestLive,
52
+ AllowAnonymousLive,
53
+ CtxProvider.Default,
54
+ SomeElseProvider.Default,
55
+ DefaultGenericMiddlewaresLive,
56
+ DevModeMiddlewareLive,
57
+ SomeService.Default
58
+ ])
59
+ )
60
+ }
61
+
62
+ const { TaggedRequestFor } = makeRpcClient(mw)
63
+ const Req = TaggedRequestFor("GenRouter")
64
+
65
+ class GetThing extends Req.Query<GetThing>()("GetThing", { id: S.String }, {
66
+ success: S.String,
67
+ error: UnauthorizedError
68
+ }) {}
69
+ class DoThing extends Req.Command<DoThing>()("DoThing", { id: S.String }, { success: S.Void }) {}
70
+
71
+ const Resource = { GetThing, DoThing }
72
+
73
+ const { Router, matchAll } = makeRouter(mw.Default)
74
+
75
+ class ThingRepo extends Context.Service<ThingRepo>()("ThingRepo", {
76
+ make: Effect.succeed({ get: (id: string) => Effect.succeed(id + "!") })
77
+ }) {
78
+ static Default = Layer.effect(this, this.make)
79
+ }
80
+
81
+ // Case under test:
82
+ // `match({})` is given handlers as **shorthand generator methods** (`*GetThing(req) { ... }`).
83
+ // tsgo (>= 7 dev) infers `TNext = unknown` for these shorthand generators while TS6 infers `never`.
84
+ // `HandlerWithInputGen` in routing.ts must accept both — see the structural fix.
85
+ const router = Router(Resource)({
86
+ dependencies: [ThingRepo.Default],
87
+ *effect(match) {
88
+ const repo = yield* ThingRepo
89
+
90
+ if (Math.random() > 0.5) return yield* new InvalidStateError("nope")
91
+
92
+ return match({
93
+ *GetThing(req) {
94
+ const some = yield* Some
95
+ if (req.id === "boom") {
96
+ return yield* Effect.fail(new UnauthorizedError())
97
+ }
98
+ return yield* repo.get(req.id + String(some.a))
99
+ },
100
+ *DoThing(_req) {
101
+ yield* Effect.succeed(1)
102
+ }
103
+ })
104
+ }
105
+ })
106
+
107
+ // Same scenario but using the `raw:` variant — exercises the `raw` path of `HandlerWithInputGen`.
108
+ const routerRaw = Router({ GetThing })({
109
+ *effect(match) {
110
+ return match({
111
+ GetThing: {
112
+ *raw(req) {
113
+ const some = yield* Some
114
+ return yield* Effect.succeed(req.id + String(some.a))
115
+ }
116
+ }
117
+ })
118
+ }
119
+ })
120
+
121
+ it("router with generator-method handlers compiles", () => {
122
+ expectTypeOf(router).toMatchTypeOf<
123
+ Layer.Layer<never, ConfigError | InvalidStateError, SomeService | RpcSerialization>
124
+ >()
125
+ expectTypeOf(routerRaw).toMatchTypeOf<Layer.Layer<never, ConfigError, SomeService | RpcSerialization>>()
126
+ })
127
+
128
+ // Type-level assertions: verify generator yields propagate to MakeErrors / MakeContext
129
+ type Errors = MakeErrors<typeof router[TypeTestId]>
130
+ type Ctx = MakeContext<typeof router[TypeTestId]>
131
+ expectTypeOf<Errors>().toEqualTypeOf<InvalidStateError>()
132
+ expectTypeOf<Ctx>().toEqualTypeOf<ThingRepo>()
133
+
134
+ const matched = matchAll({ router })
135
+ expectTypeOf(matched).toMatchTypeOf<
136
+ Layer.Layer<never, ConfigError | InvalidStateError, SomeService | RpcSerialization>
137
+ >()
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // DSL R-inference regression
141
+ // ---------------------------------------------------------------------------
142
+ // `OneDSL`/`OneDSLExt.update`/`.modify` previously annotated the callback's
143
+ // effect R as `FixEnv<R, Evt, S1, S2>`. That deadlocked inference of `R`
144
+ // (TS6 → `never`, tsgo → `unknown`), causing yielded effects to leak
145
+ // `unknown` in the R slot when consumed by generator handlers.
146
+ // The fix uses bare `R` in the callback and `FixEnv<R, …>` only on the return.
147
+ class Item extends S.Class<Item>("Item")({ id: S.String, label: S.String }) {}
148
+ class Dep extends Context.Service<Dep>()("Dep", { make: Effect.succeed({ tag: "dep" as const }) }) {}
149
+
150
+ type Evt = { _tag: "Updated"; id: string }
151
+
152
+ const Items$ = makeAllDSL<Item, Evt>()
153
+ const Item$ = makeOneDSL<Item, Evt>()
154
+
155
+ // Callback body uses generator syntax (TNext = unknown under tsgo) and yields
156
+ // a service-dependent effect — R must be inferred as `Dep` (plus the
157
+ // canonical PureEnvEnv contributed by FixEnv on the return).
158
+ const oneUpdate = Item$.update((item) =>
159
+ Effect.gen(function*() {
160
+ const dep = yield* Dep
161
+ return new Item({ id: item.id, label: item.label + dep.tag })
162
+ })
163
+ )
164
+
165
+ const allUpdate = Items$.update((items) =>
166
+ Effect.gen(function*() {
167
+ const dep = yield* Dep
168
+ return items.map((_) => new Item({ id: _.id, label: _.label + dep.tag }))
169
+ })
170
+ )
171
+
172
+ const oneModify = Item$.modify((item, _dsl) =>
173
+ Effect.gen(function*() {
174
+ const dep = yield* Dep
175
+ return { ...item, tag: dep.tag }
176
+ })
177
+ )
178
+
179
+ // `R` should be `FixEnv<Dep, Evt, …>` — never collapsed to `unknown`/`never`.
180
+ // The regression manifested as `unknown` here, breaking `Dep` assignability.
181
+ expectTypeOf(oneUpdate).toMatchTypeOf<Effect.Effect<Item, never, FixEnv<Dep, Evt, Item, Item>>>()
182
+ expectTypeOf(allUpdate).toMatchTypeOf<
183
+ Effect.Effect<readonly Item[], never, FixEnv<Dep, Evt, readonly Item[], readonly Item[]>>
184
+ >()
185
+ expectTypeOf(oneModify).toMatchTypeOf<
186
+ Effect.Effect<{ tag: "dep"; id: string; label: string }, never, FixEnv<Dep, Evt, Item, Item>>
187
+ >()
@@ -0,0 +1,66 @@
1
+ import { describe, expect, it } from "@effect/vitest"
2
+ import { ConfigureInterruptibilityMiddleware } from "effect-app/middleware"
3
+ import * as S from "effect-app/Schema"
4
+ import * as Effect from "effect/Effect"
5
+ import * as Fiber from "effect/Fiber"
6
+ import * as Layer from "effect/Layer"
7
+ import * as Ref from "effect/Ref"
8
+ import { Rpc, RpcGroup, RpcTest } from "effect/unstable/rpc"
9
+ import { applyRequestTypeInterruptibility } from "../src/routing.js"
10
+ import { ConfigureInterruptibilityMiddlewareLive, RequestType } from "../src/routing/middleware.js"
11
+
12
+ const InterruptibilityRpcs = RpcGroup.make(
13
+ Rpc
14
+ .make("doCommand", { success: S.Void })
15
+ .annotate(RequestType, "command")
16
+ .middleware(ConfigureInterruptibilityMiddleware),
17
+ Rpc
18
+ .make("doQuery", { success: S.Void })
19
+ .annotate(RequestType, "query")
20
+ .middleware(ConfigureInterruptibilityMiddleware)
21
+ )
22
+
23
+ const makeImplLayer = (commandDone: Ref.Ref<boolean>, queryDone: Ref.Ref<boolean>) =>
24
+ InterruptibilityRpcs.toLayer({
25
+ doCommand: () =>
26
+ applyRequestTypeInterruptibility(
27
+ "command",
28
+ Effect.sleep("120 millis").pipe(Effect.andThen(Ref.set(commandDone, true)))
29
+ ),
30
+ doQuery: () =>
31
+ applyRequestTypeInterruptibility(
32
+ "query",
33
+ Effect.sleep("120 millis").pipe(Effect.andThen(Ref.set(queryDone, true)))
34
+ )
35
+ })
36
+
37
+ describe("routing interruptibility", () => {
38
+ it.live(
39
+ "e2e: command continues after client interrupt, query does not",
40
+ () =>
41
+ Effect.gen(function*() {
42
+ const commandDone = yield* Ref.make(false)
43
+ const queryDone = yield* Ref.make(false)
44
+
45
+ const client = yield* RpcTest
46
+ .makeClient(InterruptibilityRpcs)
47
+ .pipe(
48
+ Effect.provide(
49
+ Layer.mergeAll(makeImplLayer(commandDone, queryDone), ConfigureInterruptibilityMiddlewareLive)
50
+ )
51
+ )
52
+
53
+ const commandFiber = yield* Effect.forkDetach(client.doCommand())
54
+ yield* Effect.sleep("20 millis")
55
+ yield* Fiber.interrupt(commandFiber)
56
+ yield* Effect.sleep("180 millis")
57
+ expect(yield* Ref.get(commandDone)).toBe(true)
58
+
59
+ const queryFiber = yield* Effect.forkDetach(client.doQuery())
60
+ yield* Effect.sleep("20 millis")
61
+ yield* Fiber.interrupt(queryFiber)
62
+ yield* Effect.sleep("180 millis")
63
+ expect(yield* Ref.get(queryDone)).toBe(false)
64
+ })
65
+ )
66
+ })
@@ -0,0 +1,265 @@
1
+ /**
2
+ * E2E test for commit bb3f51d03 — `fix(infra): bind ContextMap to request scope
3
+ * for SSE streams`.
4
+ *
5
+ * Background
6
+ * ----------
7
+ * The RpcServer returns `HttpServerResponse.stream(...)` for streaming RPC
8
+ * resources. The body of that response keeps producing chunks AFTER the
9
+ * outer Effect that built the response has returned. `RequestContextMiddleware`
10
+ * provisions `ContextMapContainer` for the request. The acquireRelease
11
+ * inside `ContextMapContainer.layer` calls `clear()` on finalize, wiping
12
+ * the etag map and the per-request resolver/store cache.
13
+ *
14
+ * If that layer is built against a sub-scope of the outer Effect (the
15
+ * pre-fix behaviour: `Effect.provide(layer)`), the finalizer fires as soon
16
+ * as the middleware Effect returns the HttpServerResponse — i.e. between
17
+ * "handler done" and "first chunk written" — wiping ContextMap state that
18
+ * later chunks still need. In production this surfaces as spurious
19
+ * OptimisticConcurrencyException on writes that follow a streaming read.
20
+ *
21
+ * The fix binds the layer to the ambient request scope via
22
+ * `provideOnRequestScope`, so `clear()` only runs once the response body
23
+ * has fully drained.
24
+ *
25
+ * Reproduction strategy
26
+ * ---------------------
27
+ * - Mirror the production wiring: apply `RequestContextMiddleware` to the
28
+ * RPC router (see `boilerplate/api/src/router.ts`).
29
+ * - The stream handler sets an etag on the ContextMap BEFORE returning the
30
+ * Stream value.
31
+ * - The Stream emits three values 100ms apart; each emission reads back the
32
+ * etag via `getContextMap` and yields 1 if the value is still present,
33
+ * 0 otherwise.
34
+ * - Expectation: [1, 1, 1]. If the layer's `clear()` runs mid-stream the
35
+ * later chunks observe an empty map and the assertion fails (typically
36
+ * with [1, 0, 0] or [0, 0, 0]).
37
+ */
38
+ import { NodeHttpServer } from "@effect/platform-node"
39
+ import { expect, it } from "@effect/vitest"
40
+ import { ApiClientFactory, makeRpcClient } from "effect-app/client"
41
+ import { HttpRouter, HttpServer } from "effect-app/http"
42
+ import { DefaultGenericMiddlewares } from "effect-app/middleware"
43
+ import { MiddlewareMaker } from "effect-app/rpc"
44
+ import * as S from "effect-app/Schema"
45
+ import { getContextMap } from "effect-app/Store"
46
+ import * as Effect from "effect/Effect"
47
+ import * as Layer from "effect/Layer"
48
+ import * as Option from "effect/Option"
49
+ import * as Stream from "effect/Stream"
50
+ import { FetchHttpClient } from "effect/unstable/http"
51
+ import * as NetAddress from "effect/unstable/net/NetAddress"
52
+ import { RpcSerialization } from "effect/unstable/rpc"
53
+ import { createServer } from "http"
54
+ import { RequestContextMiddleware } from "../src/internal/RequestContextMiddleware.js"
55
+ import { makeRouter } from "../src/routing.js"
56
+ import { DefaultGenericMiddlewaresLive } from "../src/routing/middleware.js"
57
+ import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, SomeElseMiddleware, SomeElseMiddlewareLive, SomeService, Test, TestLive } from "./fixtures.js"
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Middleware — mirrors AppMiddleware shape used by the other rpc e2e tests.
61
+ // ---------------------------------------------------------------------------
62
+
63
+ class AppMiddleware extends MiddlewareMaker
64
+ .Tag<AppMiddleware>()("AppMiddleware", RequestContextMap)
65
+ .middleware(RequireRoles, Test)
66
+ .middleware(AllowAnonymous)
67
+ .middleware(SomeElseMiddleware)
68
+ .middleware(...DefaultGenericMiddlewares)
69
+ {
70
+ static Default = this.layer.pipe(
71
+ Layer.provide(
72
+ [
73
+ RequireRolesLive.pipe(Layer.provide(SomeService.Default)),
74
+ AllowAnonymousLive,
75
+ TestLive,
76
+ SomeElseMiddlewareLive,
77
+ DefaultGenericMiddlewaresLive
78
+ ] as const
79
+ )
80
+ )
81
+ }
82
+
83
+ const { Router, matchAll } = makeRouter(AppMiddleware.Default)
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Resource — single streaming command that exercises ContextMap mid-stream.
87
+ // ---------------------------------------------------------------------------
88
+
89
+ const { TaggedRequestFor } = makeRpcClient(AppMiddleware)
90
+ const Req = TaggedRequestFor("CtxMap")
91
+
92
+ class StreamEtag extends Req.Command<StreamEtag>()("StreamEtag", {}, {
93
+ stream: true,
94
+ allowAnonymous: true,
95
+ success: S.Number
96
+ }) {}
97
+
98
+ // Per-request isolation probes: each handler writes a caller-supplied value to
99
+ // the SHARED key, then emits 3 chunks each re-reading the SHARED key. If two
100
+ // concurrent requests share a ContextMap, the second writer overwrites the
101
+ // first and the first request observes the wrong value mid-stream.
102
+ class StreamWithEtag extends Req.Command<StreamWithEtag>()("StreamWithEtag", {
103
+ value: S.String
104
+ }, {
105
+ stream: true,
106
+ allowAnonymous: true,
107
+ success: S.String
108
+ }) {}
109
+
110
+ class ReadEtagOnce extends Req.Query<ReadEtagOnce>()("ReadEtagOnce", {}, {
111
+ allowAnonymous: true,
112
+ success: S.String
113
+ }) {}
114
+
115
+ const Rsc = { StreamEtag, StreamWithEtag, ReadEtagOnce }
116
+
117
+ // Distinct constants so an assertion failure points squarely at "the etag
118
+ // the handler wrote was no longer there when later chunks ran".
119
+ const ETAG_ID = "ctxmap-test-id"
120
+ const ETAG_VALUE = "v1"
121
+ const SHARED_KEY = "ctxmap-shared-key"
122
+ const MISSING = "<missing>"
123
+
124
+ const router = Router(Rsc)({
125
+ *effect(match) {
126
+ return match({
127
+ StreamEtag: () =>
128
+ Effect
129
+ .gen(function*() {
130
+ // 1) Acquire the request-scoped ContextMap. Fails (dies) if the
131
+ // container is still the default "root" — which would mean
132
+ // RequestContextMiddleware did not run for this request.
133
+ const ctxMap = yield* getContextMap.pipe(Effect.orDie)
134
+ // 2) Seed an etag BEFORE handing back the Stream. This write is
135
+ // what the per-chunk readers below verify.
136
+ ctxMap.set(ETAG_ID, ETAG_VALUE)
137
+ // 3) Emit three values 100ms apart so chunks are produced AFTER
138
+ // the outer Effect that built the response has returned. Each
139
+ // emission re-reads the etag from the request-scoped ContextMap.
140
+ return Stream.fromIterable([0, 1, 2]).pipe(
141
+ Stream.mapEffect(() =>
142
+ Effect.sleep("100 millis").pipe(
143
+ Effect.flatMap(() => getContextMap.pipe(Effect.orDie)),
144
+ Effect.map((m) => m.get(ETAG_ID) === ETAG_VALUE ? 1 : 0)
145
+ )
146
+ )
147
+ )
148
+ })
149
+ .pipe(Stream.unwrap),
150
+ StreamWithEtag: ({ value }: { readonly value: string }) =>
151
+ Effect
152
+ .gen(function*() {
153
+ const ctxMap = yield* getContextMap.pipe(Effect.orDie)
154
+ ctxMap.set(SHARED_KEY, value)
155
+ return Stream.fromIterable([0, 1, 2]).pipe(
156
+ Stream.mapEffect(() =>
157
+ Effect.sleep("100 millis").pipe(
158
+ Effect.flatMap(() => getContextMap.pipe(Effect.orDie)),
159
+ Effect.map((m) => m.get(SHARED_KEY) ?? MISSING)
160
+ )
161
+ )
162
+ )
163
+ })
164
+ .pipe(Stream.unwrap),
165
+ ReadEtagOnce: () => getContextMap.pipe(Effect.orDie, Effect.map((m) => m.get(SHARED_KEY) ?? MISSING))
166
+ })
167
+ }
168
+ })
169
+
170
+ const RpcRouterLayer = matchAll({ router })
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // HTTP wiring — fresh server on a loopback port per `it.live`. The critical
174
+ // difference vs. rpc-stream-fullstack: we apply `RequestContextMiddleware`
175
+ // here, exactly as the production boilerplate does, so the fix code path
176
+ // is what runs.
177
+ // ---------------------------------------------------------------------------
178
+
179
+ const NodeServerLayer = NodeHttpServer.layer(() => createServer(), { port: 0 })
180
+
181
+ const RequestContextMiddlewareLayer = HttpRouter.middleware(RequestContextMiddleware()).layer
182
+
183
+ const ServerLayer = HttpRouter
184
+ .serve(
185
+ RpcRouterLayer.pipe(Layer.provide(RequestContextMiddlewareLayer))
186
+ )
187
+ .pipe(
188
+ Layer.provide(NodeServerLayer),
189
+ Layer.provide(RpcSerialization.layerNdjson)
190
+ )
191
+
192
+ const ClientLayer = Layer
193
+ .unwrap(
194
+ Effect.gen(function*() {
195
+ const server = yield* HttpServer.HttpServer
196
+ const addr = server.address
197
+ if (NetAddress.isUnixPathAddress(addr)) {
198
+ return yield* Effect.die(new Error("expected inet address"))
199
+ }
200
+ const host = NetAddress.isUnspecified(addr.address) ? "127.0.0.1" : NetAddress.formatUrlHost(addr.address)
201
+ const url = `http://${host}:${addr.port}`
202
+ return ApiClientFactory
203
+ .layer({ url, headers: Option.none() })
204
+ .pipe(Layer.provide(FetchHttpClient.layer))
205
+ })
206
+ )
207
+ .pipe(Layer.provide(NodeServerLayer))
208
+
209
+ const TestLayer = Layer.mergeAll(ServerLayer, ClientLayer)
210
+
211
+ // ---------------------------------------------------------------------------
212
+ // Test
213
+ // ---------------------------------------------------------------------------
214
+
215
+ it.live(
216
+ "ContextMap survives mid-stream: etag set in handler is readable by every chunk",
217
+ Effect.fnUntraced(function*() {
218
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(Rsc)
219
+ const values = yield* Stream.runCollect(client.StreamEtag.handler())
220
+ // All three chunks emit 1 → the etag was still readable when each chunk
221
+ // executed. If the layer-bound ContextMap's `clear()` finalizer fired
222
+ // mid-stream (the pre-fix behaviour), later chunks would emit 0.
223
+ expect(values).toStrictEqual([1, 1, 1])
224
+ }, Effect.provide(TestLayer)),
225
+ { timeout: 10_000 }
226
+ )
227
+
228
+ it.live(
229
+ "succeeding requests get a fresh ContextMap: request N+1 cannot see request N's writes",
230
+ Effect.fnUntraced(function*() {
231
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(Rsc)
232
+ // 1st request writes SHARED_KEY = "first" and drains its stream so its
233
+ // request scope (and ContextMap) is closed before request 2 starts.
234
+ const first = yield* Stream.runCollect(client.StreamWithEtag.handler({ value: "first" }))
235
+ expect(first).toStrictEqual(["first", "first", "first"])
236
+ // 2nd request must NOT observe the previous request's value at any point.
237
+ const peek = yield* client.ReadEtagOnce.handler()
238
+ expect(peek).toBe(MISSING)
239
+ // 3rd request writes a different value and drains; must not be polluted by request 1.
240
+ const third = yield* Stream.runCollect(client.StreamWithEtag.handler({ value: "third" }))
241
+ expect(third).toStrictEqual(["third", "third", "third"])
242
+ }, Effect.provide(TestLayer)),
243
+ { timeout: 10_000 }
244
+ )
245
+
246
+ it.live(
247
+ "overlapping requests get isolated ContextMaps: concurrent streams see only their own writes",
248
+ Effect.fnUntraced(function*() {
249
+ const client = yield* ApiClientFactory.makeFor(Layer.empty)(Rsc)
250
+ // Two streams in flight at the same time, each writing the SAME key with a
251
+ // different value. With per-request maps each stream reads back only its
252
+ // own value across all chunks. With a shared map the later writer's value
253
+ // would leak into the earlier stream's later chunks.
254
+ const [a, b] = yield* Effect.all(
255
+ [
256
+ Stream.runCollect(client.StreamWithEtag.handler({ value: "alpha" })),
257
+ Stream.runCollect(client.StreamWithEtag.handler({ value: "beta" }))
258
+ ],
259
+ { concurrency: "unbounded" }
260
+ )
261
+ expect(a).toStrictEqual(["alpha", "alpha", "alpha"])
262
+ expect(b).toStrictEqual(["beta", "beta", "beta"])
263
+ }, Effect.provide(TestLayer)),
264
+ { timeout: 10_000 }
265
+ )