effect-app 4.0.0-beta.98 → 4.0.0

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 (371) hide show
  1. package/CHANGELOG.md +2030 -0
  2. package/dist/Array.d.ts +2 -1
  3. package/dist/Array.d.ts.map +1 -1
  4. package/dist/Array.js +4 -4
  5. package/dist/Chunk.d.ts +2 -2
  6. package/dist/Chunk.d.ts.map +1 -1
  7. package/dist/Config/SecretURL.d.ts +3 -1
  8. package/dist/Config/SecretURL.d.ts.map +1 -1
  9. package/dist/Config/SecretURL.js +3 -6
  10. package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
  11. package/dist/Config/internal/configSecretURL.js +2 -2
  12. package/dist/Config.d.ts +0 -5
  13. package/dist/Config.d.ts.map +1 -1
  14. package/dist/Config.js +7 -5
  15. package/dist/ConfigProvider.d.ts +0 -37
  16. package/dist/ConfigProvider.d.ts.map +1 -1
  17. package/dist/ConfigProvider.js +5 -41
  18. package/dist/Context.d.ts +6 -4
  19. package/dist/Context.d.ts.map +1 -1
  20. package/dist/Context.js +4 -3
  21. package/dist/DataDependencies.d.ts +136 -0
  22. package/dist/DataDependencies.d.ts.map +1 -0
  23. package/dist/DataDependencies.js +112 -0
  24. package/dist/Effect.d.ts +3 -3
  25. package/dist/Effect.d.ts.map +1 -1
  26. package/dist/Effect.js +6 -9
  27. package/dist/Emailer.d.ts +51 -0
  28. package/dist/Emailer.d.ts.map +1 -0
  29. package/dist/Emailer.js +7 -0
  30. package/dist/Function.d.ts.map +1 -1
  31. package/dist/Layer.d.ts +6 -3
  32. package/dist/Layer.d.ts.map +1 -1
  33. package/dist/Layer.js +3 -2
  34. package/dist/Model/Repository/Registry.d.ts +22 -0
  35. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  36. package/dist/Model/Repository/Registry.js +18 -0
  37. package/dist/Model/Repository/dependency.d.ts +6 -0
  38. package/dist/Model/Repository/dependency.d.ts.map +1 -0
  39. package/dist/Model/Repository/dependency.js +29 -0
  40. package/dist/Model/Repository/ext.d.ts +60 -0
  41. package/dist/Model/Repository/ext.d.ts.map +1 -0
  42. package/dist/Model/Repository/ext.js +124 -0
  43. package/dist/Model/Repository/internal/internal.d.ts +68 -0
  44. package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
  45. package/dist/Model/Repository/internal/internal.js +611 -0
  46. package/dist/Model/Repository/legacy.d.ts +21 -0
  47. package/dist/Model/Repository/legacy.d.ts.map +1 -0
  48. package/dist/Model/Repository/legacy.js +2 -0
  49. package/dist/Model/Repository/makeRepo.d.ts +71 -0
  50. package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
  51. package/dist/Model/Repository/makeRepo.js +30 -0
  52. package/dist/Model/Repository/service.d.ts +121 -0
  53. package/dist/Model/Repository/service.d.ts.map +1 -0
  54. package/dist/Model/Repository/service.js +2 -0
  55. package/dist/Model/Repository/validation.d.ts +72 -0
  56. package/dist/Model/Repository/validation.d.ts.map +1 -0
  57. package/dist/Model/Repository/validation.js +39 -0
  58. package/dist/Model/Repository.d.ts +8 -0
  59. package/dist/Model/Repository.d.ts.map +1 -0
  60. package/dist/Model/Repository.js +8 -0
  61. package/dist/Model/dsl.d.ts +33 -0
  62. package/dist/Model/dsl.d.ts.map +1 -0
  63. package/dist/Model/dsl.js +43 -0
  64. package/dist/Model/filter/filterApi.d.ts +30 -0
  65. package/dist/Model/filter/filterApi.d.ts.map +1 -0
  66. package/dist/Model/filter/filterApi.js +2 -0
  67. package/dist/Model/filter/types/errors.d.ts +29 -0
  68. package/dist/Model/filter/types/errors.d.ts.map +1 -0
  69. package/dist/Model/filter/types/errors.js +2 -0
  70. package/dist/Model/filter/types/fields.d.ts +15 -0
  71. package/dist/Model/filter/types/fields.d.ts.map +1 -0
  72. package/dist/Model/filter/types/fields.js +2 -0
  73. package/dist/Model/filter/types/path/common.d.ts +316 -0
  74. package/dist/Model/filter/types/path/common.d.ts.map +1 -0
  75. package/dist/Model/filter/types/path/common.js +2 -0
  76. package/dist/Model/filter/types/path/eager.d.ts +94 -0
  77. package/dist/Model/filter/types/path/eager.d.ts.map +1 -0
  78. package/dist/Model/filter/types/path/eager.js +36 -0
  79. package/dist/Model/filter/types/path/index.d.ts +4 -0
  80. package/dist/Model/filter/types/path/index.d.ts.map +1 -0
  81. package/dist/Model/filter/types/path/index.js +3 -0
  82. package/dist/Model/filter/types/utils.d.ts +79 -0
  83. package/dist/Model/filter/types/utils.d.ts.map +1 -0
  84. package/dist/Model/filter/types/utils.js +2 -0
  85. package/dist/Model/filter/types/validator.d.ts +30 -0
  86. package/dist/Model/filter/types/validator.d.ts.map +1 -0
  87. package/dist/Model/filter/types/validator.js +2 -0
  88. package/dist/Model/filter/types.d.ts +5 -0
  89. package/dist/Model/filter/types.d.ts.map +1 -0
  90. package/dist/Model/filter/types.js +7 -0
  91. package/dist/Model/query/dsl.d.ts +630 -0
  92. package/dist/Model/query/dsl.d.ts.map +1 -0
  93. package/dist/Model/query/dsl.js +376 -0
  94. package/dist/Model/query/new-kid-interpreter.d.ts +132 -0
  95. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
  96. package/dist/Model/query/new-kid-interpreter.js +370 -0
  97. package/dist/Model/query.d.ts +15 -0
  98. package/dist/Model/query.d.ts.map +1 -0
  99. package/dist/Model/query.js +3 -0
  100. package/dist/Model.d.ts +6 -0
  101. package/dist/Model.d.ts.map +1 -0
  102. package/dist/Model.js +6 -0
  103. package/dist/NonEmptySet.d.ts +3 -1
  104. package/dist/NonEmptySet.d.ts.map +1 -1
  105. package/dist/NonEmptySet.js +2 -2
  106. package/dist/Option.d.ts +1 -0
  107. package/dist/Option.d.ts.map +1 -1
  108. package/dist/Option.js +3 -1
  109. package/dist/Pure.d.ts +5 -3
  110. package/dist/Pure.d.ts.map +1 -1
  111. package/dist/Pure.js +5 -2
  112. package/dist/QueueMaker.d.ts +11 -0
  113. package/dist/QueueMaker.d.ts.map +1 -0
  114. package/dist/QueueMaker.js +11 -0
  115. package/dist/RequestContext.d.ts +87 -0
  116. package/dist/RequestContext.d.ts.map +1 -0
  117. package/dist/RequestContext.js +45 -0
  118. package/dist/RequestScopedDependencies.d.ts +21 -0
  119. package/dist/RequestScopedDependencies.d.ts.map +1 -0
  120. package/dist/RequestScopedDependencies.js +16 -0
  121. package/dist/Schema/Class.d.ts +163 -16
  122. package/dist/Schema/Class.d.ts.map +1 -1
  123. package/dist/Schema/Class.js +193 -21
  124. package/dist/Schema/FastCheck.d.ts.map +1 -1
  125. package/dist/Schema/SchemaParser.d.ts +5 -0
  126. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  127. package/dist/Schema/SchemaParser.js +6 -0
  128. package/dist/Schema/SpecialJsonSchema.d.ts +15 -2
  129. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -1
  130. package/dist/Schema/SpecialJsonSchema.js +70 -12
  131. package/dist/Schema/SpecialOpenApi.d.ts +1 -1
  132. package/dist/Schema/SpecialOpenApi.js +3 -3
  133. package/dist/Schema/brand.d.ts +5 -5
  134. package/dist/Schema/brand.d.ts.map +1 -1
  135. package/dist/Schema/brand.js +6 -4
  136. package/dist/Schema/email.d.ts.map +1 -1
  137. package/dist/Schema/email.js +8 -5
  138. package/dist/Schema/ext.d.ts +297 -72
  139. package/dist/Schema/ext.d.ts.map +1 -1
  140. package/dist/Schema/ext.js +331 -67
  141. package/dist/Schema/moreStrings.d.ts +82 -48
  142. package/dist/Schema/moreStrings.d.ts.map +1 -1
  143. package/dist/Schema/moreStrings.js +85 -51
  144. package/dist/Schema/numbers.d.ts +33 -21
  145. package/dist/Schema/numbers.d.ts.map +1 -1
  146. package/dist/Schema/numbers.js +64 -12
  147. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  148. package/dist/Schema/phoneNumber.js +6 -6
  149. package/dist/Schema/schema.d.ts +1 -1
  150. package/dist/Schema/schema.d.ts.map +1 -1
  151. package/dist/Schema/schema.js +2 -2
  152. package/dist/Schema/strings.d.ts +18 -4
  153. package/dist/Schema/strings.d.ts.map +1 -1
  154. package/dist/Schema/strings.js +1 -5
  155. package/dist/Schema.d.ts +223 -6
  156. package/dist/Schema.d.ts.map +1 -1
  157. package/dist/Schema.js +189 -23
  158. package/dist/SchemaAST.d.ts +2 -0
  159. package/dist/SchemaAST.d.ts.map +1 -0
  160. package/dist/SchemaAST.js +2 -0
  161. package/dist/Set.d.ts +4 -1
  162. package/dist/Set.d.ts.map +1 -1
  163. package/dist/Set.js +3 -2
  164. package/dist/Store.d.ts +215 -0
  165. package/dist/Store.d.ts.map +1 -0
  166. package/dist/Store.js +113 -0
  167. package/dist/_ext/Array.d.ts +1 -1
  168. package/dist/_ext/Array.d.ts.map +1 -1
  169. package/dist/_ext/Array.js +4 -2
  170. package/dist/_ext/misc.d.ts +4 -1
  171. package/dist/_ext/misc.d.ts.map +1 -1
  172. package/dist/_ext/misc.js +4 -2
  173. package/dist/_ext/ord.ext.d.ts +2 -1
  174. package/dist/_ext/ord.ext.d.ts.map +1 -1
  175. package/dist/_ext/ord.ext.js +2 -2
  176. package/dist/builtin.d.ts +2 -14
  177. package/dist/builtin.d.ts.map +1 -1
  178. package/dist/builtin.js +1 -1
  179. package/dist/client/InvalidationKeys.d.ts +30 -0
  180. package/dist/client/InvalidationKeys.d.ts.map +1 -0
  181. package/dist/client/InvalidationKeys.js +37 -0
  182. package/dist/client/apiClientFactory.d.ts +13 -8
  183. package/dist/client/apiClientFactory.d.ts.map +1 -1
  184. package/dist/client/apiClientFactory.js +148 -29
  185. package/dist/client/clientFor.d.ts +48 -13
  186. package/dist/client/clientFor.d.ts.map +1 -1
  187. package/dist/client/clientFor.js +9 -1
  188. package/dist/client/errors.d.ts +74 -28
  189. package/dist/client/errors.d.ts.map +1 -1
  190. package/dist/client/errors.js +41 -10
  191. package/dist/client/makeClient.d.ts +487 -32
  192. package/dist/client/makeClient.d.ts.map +1 -1
  193. package/dist/client/makeClient.js +47 -21
  194. package/dist/client.d.ts +2 -0
  195. package/dist/client.d.ts.map +1 -1
  196. package/dist/client.js +3 -1
  197. package/dist/faker.d.ts.map +1 -1
  198. package/dist/http/Request.d.ts +2 -2
  199. package/dist/http/Request.d.ts.map +1 -1
  200. package/dist/http/Request.js +2 -2
  201. package/dist/http/internal/lib.d.ts +12 -12
  202. package/dist/http/internal/lib.d.ts.map +1 -1
  203. package/dist/http/internal/lib.js +13 -13
  204. package/dist/ids.d.ts +32 -14
  205. package/dist/ids.d.ts.map +1 -1
  206. package/dist/ids.js +38 -7
  207. package/dist/index.d.ts +3 -0
  208. package/dist/index.d.ts.map +1 -1
  209. package/dist/index.js +5 -1
  210. package/dist/middleware.d.ts +12 -6
  211. package/dist/middleware.d.ts.map +1 -1
  212. package/dist/middleware.js +13 -7
  213. package/dist/rpc/Invalidation.d.ts +1124 -0
  214. package/dist/rpc/Invalidation.d.ts.map +1 -0
  215. package/dist/rpc/Invalidation.js +178 -0
  216. package/dist/rpc/MiddlewareMaker.d.ts +10 -7
  217. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  218. package/dist/rpc/MiddlewareMaker.js +57 -37
  219. package/dist/rpc/RpcContextMap.d.ts +2 -2
  220. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  221. package/dist/rpc/RpcMiddleware.d.ts +13 -10
  222. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  223. package/dist/rpc/RpcMiddleware.js +2 -2
  224. package/dist/rpc.d.ts +1 -1
  225. package/dist/rpc.d.ts.map +1 -1
  226. package/dist/rpc.js +2 -2
  227. package/dist/runtime.d.ts +19 -0
  228. package/dist/runtime.d.ts.map +1 -0
  229. package/dist/runtime.js +40 -0
  230. package/dist/setupRequest.d.ts +20 -0
  231. package/dist/setupRequest.d.ts.map +1 -0
  232. package/dist/setupRequest.js +72 -0
  233. package/dist/toast.d.ts +51 -0
  234. package/dist/toast.d.ts.map +1 -0
  235. package/dist/toast.js +34 -0
  236. package/dist/transform.d.ts +1 -1
  237. package/dist/transform.d.ts.map +1 -1
  238. package/dist/transform.js +4 -5
  239. package/dist/utils/effectify.d.ts +1 -1
  240. package/dist/utils/effectify.d.ts.map +1 -1
  241. package/dist/utils/effectify.js +2 -2
  242. package/dist/utils/extend.d.ts +6 -1
  243. package/dist/utils/extend.d.ts.map +1 -1
  244. package/dist/utils/extend.js +24 -6
  245. package/dist/utils/gen.d.ts +4 -4
  246. package/dist/utils/gen.d.ts.map +1 -1
  247. package/dist/utils/logLevel.d.ts +2 -2
  248. package/dist/utils/logLevel.d.ts.map +1 -1
  249. package/dist/utils/logger.d.ts +2 -1
  250. package/dist/utils/logger.d.ts.map +1 -1
  251. package/dist/utils/logger.js +2 -2
  252. package/dist/utils.d.ts +34 -39
  253. package/dist/utils.d.ts.map +1 -1
  254. package/dist/utils.js +33 -37
  255. package/dist/validation/validators.d.ts.map +1 -1
  256. package/dist/validation.d.ts.map +1 -1
  257. package/dist/withToast.d.ts +30 -0
  258. package/dist/withToast.d.ts.map +1 -0
  259. package/dist/withToast.js +64 -0
  260. package/package.json +26 -266
  261. package/src/Array.ts +5 -5
  262. package/src/Chunk.ts +3 -3
  263. package/src/Config/SecretURL.ts +6 -3
  264. package/src/Config/internal/configSecretURL.ts +2 -2
  265. package/src/Config.ts +6 -13
  266. package/src/ConfigProvider.ts +4 -47
  267. package/src/Context.ts +9 -8
  268. package/src/DataDependencies.ts +171 -0
  269. package/src/Effect.ts +9 -12
  270. package/src/Emailer.ts +51 -0
  271. package/src/Inputify.type.ts +1 -1
  272. package/src/Layer.ts +8 -5
  273. package/src/Model/Repository/Registry.ts +35 -0
  274. package/src/Model/Repository/dependency.ts +32 -0
  275. package/src/Model/Repository/ext.ts +374 -0
  276. package/src/Model/Repository/internal/internal.ts +988 -0
  277. package/src/Model/Repository/legacy.ts +29 -0
  278. package/src/Model/Repository/makeRepo.ts +168 -0
  279. package/src/Model/Repository/service.ts +676 -0
  280. package/src/Model/Repository/validation.ts +38 -0
  281. package/src/Model/Repository.ts +7 -0
  282. package/src/Model/dsl.ts +129 -0
  283. package/src/Model/filter/filterApi.ts +78 -0
  284. package/src/Model/filter/types/errors.ts +47 -0
  285. package/src/Model/filter/types/fields.ts +50 -0
  286. package/src/Model/filter/types/path/common.ts +404 -0
  287. package/src/Model/filter/types/path/eager.ts +329 -0
  288. package/src/Model/filter/types/path/index.ts +4 -0
  289. package/src/Model/filter/types/utils.ts +128 -0
  290. package/src/Model/filter/types/validator.ts +46 -0
  291. package/src/Model/filter/types.ts +6 -0
  292. package/src/Model/query/dsl.ts +3092 -0
  293. package/src/Model/query/new-kid-interpreter.ts +532 -0
  294. package/src/Model/query.ts +13 -0
  295. package/src/Model.ts +5 -0
  296. package/src/NonEmptySet.ts +6 -4
  297. package/src/Option.ts +2 -0
  298. package/src/Pure.ts +6 -3
  299. package/src/QueueMaker.ts +11 -0
  300. package/src/RequestContext.ts +48 -0
  301. package/src/RequestScopedDependencies.ts +45 -0
  302. package/src/Schema/Class.ts +550 -56
  303. package/src/Schema/SchemaParser.ts +12 -0
  304. package/src/Schema/SpecialJsonSchema.ts +75 -7
  305. package/src/Schema/SpecialOpenApi.ts +5 -5
  306. package/src/Schema/brand.ts +27 -12
  307. package/src/Schema/email.ts +9 -7
  308. package/src/Schema/ext.ts +408 -91
  309. package/src/Schema/moreStrings.ts +213 -88
  310. package/src/Schema/numbers.ts +107 -29
  311. package/src/Schema/phoneNumber.ts +8 -9
  312. package/src/Schema/schema.ts +1 -1
  313. package/src/Schema/strings.ts +23 -14
  314. package/src/Schema.ts +403 -46
  315. package/src/SchemaAST.ts +1 -0
  316. package/src/Set.ts +6 -2
  317. package/src/Store.ts +325 -0
  318. package/src/_ext/Array.ts +4 -2
  319. package/src/_ext/misc.ts +4 -1
  320. package/src/_ext/ord.ext.ts +2 -1
  321. package/src/builtin-json.d.ts +24 -0
  322. package/src/builtin.ts +16 -21
  323. package/src/client/InvalidationKeys.ts +55 -0
  324. package/src/client/apiClientFactory.ts +291 -126
  325. package/src/client/clientFor.ts +101 -33
  326. package/src/client/errors.ts +66 -20
  327. package/src/client/makeClient.ts +519 -60
  328. package/src/client.ts +6 -4
  329. package/src/http/Request.ts +4 -4
  330. package/src/http/internal/lib.ts +12 -12
  331. package/src/http.ts +1 -1
  332. package/src/ids.ts +45 -8
  333. package/src/index.ts +20 -16
  334. package/src/middleware.ts +12 -8
  335. package/src/rpc/Invalidation.ts +278 -0
  336. package/src/rpc/MiddlewareMaker.ts +86 -79
  337. package/src/rpc/README.md +2 -2
  338. package/src/rpc/RpcContextMap.ts +4 -4
  339. package/src/rpc/RpcMiddleware.ts +18 -13
  340. package/src/rpc.ts +4 -4
  341. package/src/runtime.ts +57 -0
  342. package/src/setupRequest.ts +138 -0
  343. package/src/toast.ts +54 -0
  344. package/src/transform.ts +4 -4
  345. package/src/utils/effectify.ts +1 -1
  346. package/src/utils/extend.ts +24 -5
  347. package/src/utils/gen.ts +7 -7
  348. package/src/utils/logLevel.ts +1 -1
  349. package/src/utils/logger.ts +3 -2
  350. package/src/utils.ts +85 -158
  351. package/src/validation.ts +2 -2
  352. package/src/withToast.ts +133 -0
  353. package/test/dist/rpc.test.d.ts.map +1 -1
  354. package/test/dist/rpcMiddlewareContext.test.d.ts.map +1 -0
  355. package/test/moreStrings.test.ts +55 -10
  356. package/test/rpc.test.ts +28 -11
  357. package/test/rpcMiddlewareContext.test.ts +63 -0
  358. package/test/schema.test.ts +318 -55
  359. package/test/secretURL.test.ts +4 -1
  360. package/test/special.test.ts +663 -131
  361. package/test/utils.test.ts +5 -5
  362. package/tsconfig.base.json +6 -4
  363. package/tsconfig.json +9 -1
  364. package/tsconfig.src.json +29 -29
  365. package/tsconfig.test.json +2 -2
  366. package/dist/Operations.d.ts +0 -124
  367. package/dist/Operations.d.ts.map +0 -1
  368. package/dist/Operations.js +0 -29
  369. package/eslint.config.mjs +0 -26
  370. package/src/Operations.ts +0 -55
  371. package/tsconfig.json.bak +0 -12
@@ -1,18 +1,18 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { Effect, Layer, type Schema, Schema as S, type Scope } from "effect"
3
2
  import { type NonEmptyArray, type NonEmptyReadonlyArray } from "effect/Array"
3
+ import * as Effect from "effect/Effect"
4
+ import * as Layer from "effect/Layer"
5
+ import { Rpc, type RpcGroup, type RpcSchema } from "effect/rpc"
6
+ import { type HandlersFrom } from "effect/rpc/RpcGroup"
7
+ import * as S from "effect/Schema"
8
+ import type * as Scope from "effect/Scope"
4
9
  import { type Simplify } from "effect/Types"
5
- import { Rpc, type RpcGroup, type RpcSchema } from "effect/unstable/rpc"
6
- import { type HandlersFrom } from "effect/unstable/rpc/RpcGroup"
7
- import { type RequestId } from "effect/unstable/rpc/RpcMessage"
8
- import * as Context from "../Context.js"
9
- import { type HttpHeaders } from "../http.js"
10
- import { PreludeLogger } from "../logger.js"
11
- import { type TypeTestId } from "../TypeTest.js"
12
- import { typedValuesOf } from "../utils.js"
13
- import { type GetContextConfig, type RequestContextMapTagAny, type RpcContextMap } from "./RpcContextMap.js"
14
- import { type AddMiddleware, type AnyDynamic, type RpcDynamic, type RpcMiddlewareV4, type TagClassAny } from "./RpcMiddleware.js"
15
- import * as RpcMiddlewareX from "./RpcMiddleware.js"
10
+ import * as Context from "../Context.ts"
11
+ import { PreludeLogger } from "../logger.ts"
12
+ import { type TypeTestId } from "../TypeTest.ts"
13
+ import { type GetContextConfig, type RequestContextMapTagAny, type RpcContextMap } from "./RpcContextMap.ts"
14
+ import { type AddMiddleware, type AnyDynamic, type RpcDynamic, type RpcMiddlewareV4, type TagClassAny } from "./RpcMiddleware.ts"
15
+ import * as RpcMiddlewareX from "./RpcMiddleware.ts"
16
16
 
17
17
  // adapter for effect/rpc v3 middleware provides. (in effect-smol (v4), it's just a Service Identifier, no tags.)
18
18
  // hm?
@@ -94,7 +94,7 @@ export namespace MiddlewareMaker {
94
94
  : never
95
95
  : never
96
96
 
97
- export type Errors<T> = T extends TagClassAny ? T extends { error: S.Top } ? S.Schema.Type<T["error"]>
97
+ export type Errors<T> = T extends TagClassAny ? T extends { error: S.Top } ? T["error"]["Type"]
98
98
  : never
99
99
  : never
100
100
 
@@ -170,9 +170,9 @@ export interface BuildingMiddleware<
170
170
  > {
171
171
  rpc: <
172
172
  const Tag extends string,
173
- Payload extends Schema.Top | Schema.Struct.Fields = typeof Schema.Void,
174
- Success extends Schema.Top = typeof Schema.Void,
175
- Error extends Schema.Top = typeof Schema.Never,
173
+ Payload extends S.Top | S.Struct.Fields = typeof S.Void,
174
+ Success extends S.Top = typeof S.Void,
175
+ Error extends S.Top = typeof S.Never,
176
176
  const Stream extends boolean = false,
177
177
  Config extends GetContextConfig<RequestContextMap> = {}
178
178
  >(tag: Tag, options?: {
@@ -181,16 +181,16 @@ export interface BuildingMiddleware<
181
181
  readonly error?: Error
182
182
  readonly stream?: Stream
183
183
  readonly config?: Config
184
- readonly primaryKey?: [Payload] extends [Schema.Struct.Fields] ? ((
185
- payload: Payload extends Schema.Struct.Fields ? Simplify<Schema.Struct<Payload>["Type"]> : Payload["Type"]
184
+ readonly primaryKey?: [Payload] extends [S.Struct.Fields] ? ((
185
+ payload: Payload extends S.Struct.Fields ? Simplify<S.Struct<Payload>["Type"]> : Payload["Type"]
186
186
  ) => string)
187
187
  : never
188
188
  }) =>
189
189
  & Rpc.Rpc<
190
190
  Tag,
191
- Payload extends Schema.Struct.Fields ? Schema.Struct<Payload> : Payload,
191
+ Payload extends S.Struct.Fields ? S.Struct<Payload> : Payload,
192
192
  Stream extends true ? RpcSchema.Stream<Success, Error> : Success,
193
- Stream extends true ? typeof Schema.Never : Error
193
+ Stream extends true ? typeof S.Never : Error
194
194
  >
195
195
  & { readonly config: Config }
196
196
 
@@ -259,10 +259,10 @@ export type MiddlewaresBuilder<
259
259
  : { new(_: never): {} }
260
260
  : { new(_: never): {} })
261
261
 
262
- const middlewareMaker = <
262
+ const middlewareMaker = Effect.fnUntraced(function*<
263
263
  MiddlewareProviders extends ReadonlyArray<MiddlewareMaker.Any>
264
- >(middlewares: MiddlewareProviders): Effect.Effect<
265
- RpcMiddlewareV4<
264
+ >(middlewares: MiddlewareProviders) {
265
+ type Middleware = RpcMiddlewareV4<
266
266
  MiddlewareMaker.ManyProvided<MiddlewareProviders>,
267
267
  MiddlewareMaker.ManyErrors<MiddlewareProviders>,
268
268
  Exclude<
@@ -271,42 +271,32 @@ const middlewareMaker = <
271
271
  > extends never ? never
272
272
  : Exclude<MiddlewareMaker.ManyRequired<MiddlewareProviders>, MiddlewareMaker.ManyProvided<MiddlewareProviders>>
273
273
  >
274
- > => {
274
+ type Next = Parameters<Middleware>[0]
275
+ type Options = Parameters<Middleware>[1]
276
+
275
277
  // we want to run them in reverse order because latter middlewares will provide context to former ones
276
- middlewares = middlewares.toReversed() as any
277
-
278
- return Effect.gen(function*() {
279
- const context = yield* Effect.context()
280
-
281
- // returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements
282
- // v4: wrap middleware takes (effect, options) as two params instead of a single options bag
283
- return (
284
- next: Effect.Effect<any, any, any>,
285
- options: {
286
- readonly clientId: number
287
- readonly requestId: RequestId
288
- readonly rpc: Rpc.AnyWithProps
289
- readonly payload: unknown
290
- readonly headers: HttpHeaders.Headers
291
- }
292
- ) => {
293
- // we start with the actual handler
294
- let handler = next
295
-
296
- // inspired from Effect/RpcMiddleware
297
- for (const tag of middlewares) {
298
- // use the tag to get the middleware from context
299
- const middleware = Context.getUnsafe(context, tag)
300
-
301
- // wrap the current handler, allowing the middleware to run before and after it
302
- handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
303
- Effect.andThen(middleware(handler, options))
304
- ) as any
305
- }
306
- return handler
278
+ const reversed = middlewares.toReversed()
279
+ const context = yield* Effect.context()
280
+
281
+ // returns a Effect/RpcMiddlewareV4 with Scope.Scope in requirements
282
+ // v4: wrap middleware takes (effect, options) as two params instead of a single options bag
283
+ return (next: Next, options: Options) => {
284
+ // we start with the actual handler
285
+ let handler = next
286
+
287
+ // inspired from Effect/RpcMiddleware
288
+ for (const tag of reversed) {
289
+ // use the tag to get the middleware from context
290
+ const middleware = Context.getUnsafe(context, tag)
291
+
292
+ // wrap the current handler, allowing the middleware to run before and after it
293
+ handler = PreludeLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
294
+ Effect.andThen(middleware(handler, options))
295
+ ) as any
307
296
  }
308
- }) as any
309
- }
297
+ return handler
298
+ }
299
+ })
310
300
 
311
301
  const makeMiddlewareBasic = <Self>() =>
312
302
  // by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
@@ -322,7 +312,26 @@ const makeMiddlewareBasic = <Self>() =>
322
312
  // reverse middlewares and wrap one after the other
323
313
  const middleware = middlewareMaker(make)
324
314
 
325
- const failures = make.map((_) => _.error).filter(Boolean)
315
+ // Per-middleware error: union of the static `error` on the tag (if any) AND
316
+ // the rcm config entry pointed at by the middleware's `dynamic.key` (if any).
317
+ // Reason: middlewares declared with `dynamic: RequestContextMap.get("foo")`
318
+ // don't set a static `error` field — at runtime their `.error` defaults to
319
+ // `S.Never`. Without pulling from rcm, the composite middleware's
320
+ // `.error` collapses to `Never`, and `Rpc.exitSchema` (which walks
321
+ // `rpc.middlewares[*].error` to build the wire failure union) can't decode
322
+ // the actual middleware-thrown error type. Critical for stream rpcs whose
323
+ // top-level `errorSchema` is force-set to `Never` by effect-rpc.
324
+ const isMeaningfulError = (e: S.Top | undefined): e is S.Top => e !== undefined && e !== null && e !== S.Never
325
+ const rcmRecord = rcm as Record<string, RpcContextMap.Any>
326
+ const failures: Array<S.Top> = make.flatMap((_) => {
327
+ const out: Array<S.Top> = []
328
+ if (isMeaningfulError(_.error)) out.push(_.error)
329
+ const key = _.dynamic?.key as string | undefined
330
+ if (key && rcmRecord[key] && isMeaningfulError(rcmRecord[key].error)) {
331
+ out.push(rcmRecord[key].error)
332
+ }
333
+ return out
334
+ })
326
335
  const provides = make.flatMap((_) => !_.provides ? [] : Array.isArray(_.provides) ? _.provides : [_.provides])
327
336
  const requires = make
328
337
  .flatMap((_) => !_.requires ? [] : Array.isArray(_.requires) ? _.requires : [_.requires])
@@ -358,10 +367,11 @@ const makeMiddlewareBasic = <Self>() =>
358
367
  .effect(
359
368
  MiddlewareMaker,
360
369
  middleware as Effect.Effect<
361
- any,
362
- Effect.Error<typeof middleware>,
363
- Effect.Services<typeof middleware>
370
+ any
364
371
  >
372
+ // todo; they dont change the type..
373
+ // Effect.Error<typeof middleware>,
374
+ // Effect.Services<typeof middleware>
365
375
  )
366
376
 
367
377
  // add to the tag a default implementation
@@ -380,7 +390,7 @@ export const Tag = <Self>() =>
380
390
  const Id extends string,
381
391
  RequestContextMap extends RequestContextMapTagAny
382
392
  >(id: Id, rcm: RequestContextMap): MiddlewaresBuilder<Self, Id, RequestContextMap["config"]> => {
383
- let allMiddleware: MiddlewareMaker.Any[] = []
393
+ const allMiddleware: MiddlewareMaker.Any[] = []
384
394
  const requestContext = Context.Service<"RequestContextConfig", GetContextConfig<RequestContextMap["config"]>>(
385
395
  "RequestContextConfig"
386
396
  )
@@ -389,9 +399,9 @@ export const Tag = <Self>() =>
389
399
  // rpc with config
390
400
  rpc: <
391
401
  const Tag extends string,
392
- Payload extends Schema.Top | Schema.Struct.Fields = typeof Schema.Void,
393
- Success extends Schema.Top = typeof Schema.Void,
394
- Error extends Schema.Top = typeof Schema.Never,
402
+ Payload extends S.Top | S.Struct.Fields = typeof S.Void,
403
+ Success extends S.Top = typeof S.Void,
404
+ Error extends S.Top = typeof S.Never,
395
405
  const Stream extends boolean = false,
396
406
  Config extends GetContextConfig<RequestContextMap["config"]> = {}
397
407
  >(tag: Tag, options?: {
@@ -400,35 +410,32 @@ export const Tag = <Self>() =>
400
410
  readonly error?: Error
401
411
  readonly stream?: Stream
402
412
  readonly config?: Config
403
- readonly primaryKey?: [Payload] extends [Schema.Struct.Fields] ? ((
404
- payload: Payload extends Schema.Struct.Fields ? Simplify<Schema.Struct<Payload>["Type"]> : Payload["Type"]
413
+ readonly primaryKey?: [Payload] extends [S.Struct.Fields] ? ((
414
+ payload: Payload extends S.Struct.Fields ? Simplify<S.Struct<Payload>["Type"]> : Payload["Type"]
405
415
  ) => string)
406
416
  : never
407
417
  }):
408
418
  & Rpc.Rpc<
409
419
  Tag,
410
- Payload extends Schema.Struct.Fields ? Schema.Struct<Payload> : Payload,
420
+ Payload extends S.Struct.Fields ? S.Struct<Payload> : Payload,
411
421
  // TODO: enhance `Error`. type based on middleware config.
412
422
  Stream extends true ? RpcSchema.Stream<Success, Error> : Success,
413
- Stream extends true ? typeof Schema.Never : Error
423
+ Stream extends true ? typeof S.Never : Error
414
424
  >
415
425
  & { config: Config } =>
416
426
  {
417
427
  const config = options?.config ?? {} as Config
418
428
 
419
- // based on the config, we must enhance (union) or set failures.
420
- // TODO: we should only include errors that are relevant based on the middleware config.ks
421
- const error = options?.error
422
- const errors = typedValuesOf(rcm.config).map((_) => _.error).filter((_) => _ && _ !== S.Never) // TODO: only the errors relevant based on config
423
- const allErrors = error ? [error, ...errors] : errors
424
- const [firstError, ...restErrors] = allErrors
425
- const newError = firstError ? S.Union([firstError, ...restErrors]) : S.Never
426
-
429
+ // The rpc's `error` schema carries ONLY the request's own declared errors.
430
+ // Middleware errors (rcm-derived) reach the wire via the middleware tag
431
+ // attached to the rpc group later (`RpcGroup.middleware(...)` at the
432
+ // routing/client level), and are unioned into the failure schema by
433
+ // `Rpc.exitSchema`'s `rpc.middlewares[*].error` walk.
427
434
  // @ts-expect-error — TypeScript can't prove Simplify<T> ≡ { [K in keyof T]: T[K] } for unresolved generics (primaryKey)
428
435
  const rpc = Rpc.make(tag, {
429
436
  ...options?.payload !== undefined ? { payload: options.payload } : {},
430
437
  ...options?.success !== undefined ? { success: options.success } : {},
431
- error: newError,
438
+ ...options?.error !== undefined ? { error: options.error } : {},
432
439
  ...options?.stream !== undefined ? { stream: options.stream } : {},
433
440
  ...options?.primaryKey !== undefined ? { primaryKey: options.primaryKey } : {}
434
441
  }) as any
@@ -438,7 +445,7 @@ export const Tag = <Self>() =>
438
445
  middleware: (...middlewares: any[]) => {
439
446
  for (const mw of middlewares) {
440
447
  // recall that we run middlewares in reverse order
441
- allMiddleware = [mw, ...allMiddleware]
448
+ allMiddleware.unshift(mw)
442
449
  }
443
450
  return allMiddleware.filter((m) => !!m.dynamic).length !== Object.keys(rcm.config).length
444
451
  // for sure, until all the dynamic middlewares are provided it's non sensical to call makeMiddlewareBasic
package/src/rpc/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  The extensions use V4 format of RPC middleware:
4
4
 
5
5
  - supports `requires` besides `provides`
6
- - `requires` and `provides` should be set as second generic argument: `Tag<Self, Config>`.
6
+ - `requires` and `provides` should be set as second generic argument: `Tag<Self, Config>`.
7
7
  - `wrap: true` is the default, there is no classic `provides: Tag`
8
8
 
9
9
  ## Features
@@ -34,7 +34,7 @@ NOTE: perhaps not as useful anymore if support for dynamic middleware gets integ
34
34
 
35
35
  ## Examples
36
36
 
37
- See [tests](../../../infra/test/rpc-multi-middleware.test.ts)
37
+ See [tests](../../../infra/test/rpc-multi-middleware.test.ts)
38
38
 
39
39
  ## Future
40
40
 
@@ -2,10 +2,10 @@
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-return */
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
4
 
5
- import { type Schema as S } from "effect"
6
- import { type AnyWithProps } from "effect/unstable/rpc/Rpc"
7
- import * as Context from "../Context.js"
8
- import { type RpcDynamic } from "./RpcMiddleware.js"
5
+ import { type AnyWithProps } from "effect/rpc/Rpc"
6
+ import type * as S from "effect/Schema"
7
+ import * as Context from "../Context.ts"
8
+ import { type RpcDynamic } from "./RpcMiddleware.ts"
9
9
 
10
10
  type Values<T extends Record<any, any>> = T[keyof T]
11
11
 
@@ -1,18 +1,21 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-return */
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
- import { type Effect, type Schema, type Schema as S, type Scope, type Stream } from "effect"
5
4
  import { type NonEmptyReadonlyArray } from "effect/Array"
6
- import { type Rpc, RpcMiddleware } from "effect/unstable/rpc"
7
- import { type TypeId } from "effect/unstable/rpc/RpcMiddleware"
8
- import type * as Context from "../Context.js"
9
- import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.js"
5
+ import type * as Effect from "effect/Effect"
6
+ import { type Rpc, RpcMiddleware } from "effect/rpc"
7
+ import { type TypeId } from "effect/rpc/RpcMiddleware"
8
+ import type * as S from "effect/Schema"
9
+ import type * as Scope from "effect/Scope"
10
+ import type * as Stream from "effect/Stream"
11
+ import type * as Context from "../Context.ts"
12
+ import { type GetEffectContext, type RpcContextMap } from "./RpcContextMap.ts"
10
13
 
11
14
  export type RpcMiddlewareV4<Provides, E, Requires> = RpcMiddleware.RpcMiddleware<Provides, E, Requires>
12
15
 
13
16
  export type RpcOptionsOriginal = {
14
17
  readonly optional?: boolean
15
- readonly error?: Schema.Top
18
+ readonly error?: S.Top
16
19
  readonly requiredForClient?: boolean
17
20
  }
18
21
 
@@ -40,7 +43,7 @@ export interface TagClassAny extends RpcMiddleware.AnyService {
40
43
  readonly optional: boolean
41
44
  readonly provides: any
42
45
  readonly requires: any
43
- readonly error: Schema.Top
46
+ readonly error: S.Top
44
47
  readonly dynamic?: RpcDynamic<any, any> | undefined
45
48
  readonly dependsOn?: NonEmptyReadonlyArray<AnyDynamic> | undefined
46
49
  }
@@ -50,27 +53,29 @@ export declare namespace TagClass {
50
53
  * @since 1.0.0
51
54
  * @category models
52
55
  */
53
- export type FailureSchema<Options> = Options extends { readonly error: Schema.Top; readonly optional?: false }
56
+ export type FailureSchema<Options> = Options extends { readonly error: S.Top; readonly optional?: false }
54
57
  ? Options["error"]
55
58
  // actually not, the Failure depends on Dynamic Middleware Configuration!
56
59
  // : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["error"]
57
- : typeof Schema.Never
60
+ : typeof S.Never
58
61
 
59
62
  /**
60
63
  * @since 1.0.0
61
64
  * @category models
62
65
  */
63
- export type Failure<Options> = Options extends { readonly error: Schema.Codec<infer _A>; readonly optional?: false }
64
- ? _A
66
+ export type Failure<Options> = Options extends { readonly error: S.Codec<infer _A>; readonly optional?: false } ? _A
65
67
  // actually not, the Failure depends on Dynamic Middleware Configuration!
66
- : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? S.Schema.Type<A["error"]>
68
+ : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["error"]["Type"]
67
69
  : never
68
70
 
69
71
  /**
70
72
  * @since 1.0.0
71
73
  * @category models
72
74
  */
73
- export type FailureContext<Options> = Schema.Codec.DecodingServices<FailureSchema<Options>>
75
+ // Avoid `S.Codec.DecodingServices<X> = X extends Top ? X["DecodingServices"] : never`:
76
+ // tsgo fails to reduce it here and leaves `unknown`. `FailureSchema<Options>` is
77
+ // always `S.Top` (either `Options["error"]` or `typeof S.Never`), so read directly.
78
+ export type FailureContext<Options> = FailureSchema<Options>["DecodingServices"]
74
79
 
75
80
  /**
76
81
  * @since 1.0.0
package/src/rpc.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * as RpcX from "./rpc.js"
2
- export * as MiddlewareMaker from "./rpc/MiddlewareMaker.js"
3
- export * as RpcContextMap from "./rpc/RpcContextMap.js"
4
- export * as RpcMiddleware from "./rpc/RpcMiddleware.js"
1
+ export * as Invalidation from "./rpc/Invalidation.ts"
2
+ export * as MiddlewareMaker from "./rpc/MiddlewareMaker.ts"
3
+ export * as RpcContextMap from "./rpc/RpcContextMap.ts"
4
+ export * as RpcMiddleware from "./rpc/RpcMiddleware.ts"
package/src/runtime.ts ADDED
@@ -0,0 +1,57 @@
1
+ import * as Exit from "effect/Exit"
2
+ import { flow } from "effect/Function"
3
+ import * as Logger from "effect/Logger"
4
+ import * as ManagedRuntime from "effect/ManagedRuntime"
5
+ import { CauseException } from "./client/errors.ts"
6
+ import { type Context } from "./Context.ts"
7
+ import * as Effect from "./Effect.ts"
8
+ import * as Layer from "./Layer.ts"
9
+
10
+ export const makeAppRuntime = Effect.fnUntraced(function*<A, E>(layer: Layer.Layer<A, E>) {
11
+ const l = layer.pipe(
12
+ Layer.provide(Logger.layer([Logger.consolePretty()])),
13
+ Layer.orDie
14
+ )
15
+ const mrt = ManagedRuntime.make(l)
16
+ yield* mrt.contextEffect
17
+ return Object.assign(mrt, {
18
+ [Symbol.dispose]() {
19
+ return Effect.runSync(mrt.disposeEffect)
20
+ },
21
+
22
+ [Symbol.asyncDispose]() {
23
+ return mrt.dispose()
24
+ }
25
+ }) // as we initialise here, there is no more error left.
26
+ })
27
+
28
+ export function initializeSync<A, E>(layer: Layer.Layer<A, E>) {
29
+ const runtime = Effect.runSync(makeAppRuntime(layer))
30
+ return runtime
31
+ }
32
+
33
+ export function initializeAsync<A, E>(layer: Layer.Layer<A, E>) {
34
+ return Effect
35
+ .runPromise(makeAppRuntime(layer))
36
+ }
37
+
38
+ // we wrap into CauseException because we want to keep the full cause of the failure.
39
+ export const makeRunPromise = <T>(services: Context<T>) =>
40
+ flow(Effect.runPromiseExitWith(services), (_) =>
41
+ _.then(
42
+ Exit.match({
43
+ onFailure: (cause) => Promise.reject(new CauseException(cause, "runPromise")),
44
+ onSuccess: (value) => Promise.resolve(value)
45
+ })
46
+ ))
47
+
48
+ export const makeRunSync = <T>(services: Context<T>) =>
49
+ flow(
50
+ Effect.runSyncExitWith(services),
51
+ Exit.match({
52
+ onFailure: (cause) => {
53
+ throw new CauseException(cause, "runSync")
54
+ },
55
+ onSuccess: (value) => value
56
+ })
57
+ )
@@ -0,0 +1,138 @@
1
+ import { SqlClient } from "effect/sql"
2
+ import * as Tracer from "effect/Tracer"
3
+ import { DataDependencyRecorder } from "./DataDependencies.ts"
4
+ import * as Effect from "./Effect.ts"
5
+ import * as Layer from "./Layer.ts"
6
+ import * as Option from "./Option.ts"
7
+ import { LocaleRef, RequestContext, spanAttributes } from "./RequestContext.ts"
8
+ import * as RequestScopedDependencies from "./RequestScopedDependencies.ts"
9
+ import { NonEmptyString255 } from "./Schema.ts"
10
+ import { ContextMapContainer, storeId } from "./Store.ts"
11
+
12
+ const withSqlTransaction = <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
13
+ Effect.serviceOption(SqlClient.SqlClient).pipe(
14
+ Effect.flatMap(Option.match({
15
+ onNone: () => self,
16
+ onSome: (sql) => sql.withTransaction(self).pipe(Effect.orDie)
17
+ }))
18
+ )
19
+
20
+ export const getRequestContext = Effect
21
+ .all({
22
+ span: Effect.currentSpan.pipe(Effect.orDie),
23
+ locale: LocaleRef,
24
+ namespace: storeId
25
+ })
26
+ .pipe(
27
+ Effect.map(({ locale, namespace, span }) =>
28
+ RequestContext.make({
29
+ span: Tracer.externalSpan(span),
30
+ locale,
31
+ namespace,
32
+ name: NonEmptyString255(span.name)
33
+ })
34
+ )
35
+ )
36
+
37
+ export const getRC = Effect.all({
38
+ locale: LocaleRef,
39
+ namespace: storeId
40
+ })
41
+
42
+ const withRequestSpan = (name = "request", options?: Tracer.SpanOptions) => <R, E, A>(f: Effect.Effect<A, E, R>) =>
43
+ Effect.andThen(
44
+ getRC,
45
+ (ctx) =>
46
+ f.pipe(
47
+ Effect.withSpan(name, {
48
+ ...options,
49
+ attributes: { ...spanAttributes({ ...ctx, name: NonEmptyString255(name) }), ...options?.attributes }
50
+ }, {
51
+ captureStackTrace: options?.captureStackTrace ?? false
52
+ }),
53
+ // TODO: false
54
+ // request context info is picked up directly in the logger for annotations.
55
+ Effect.withLogSpan(name)
56
+ )
57
+ )
58
+
59
+ export interface SetupRequestOptions {
60
+ readonly withTransaction?: boolean
61
+ }
62
+
63
+ export const requestStateLayer = RequestScopedDependencies.layer(ContextMapContainer, DataDependencyRecorder)
64
+
65
+ // Build `layer` against the ambient (request) scope rather than a sub-scope of the
66
+ // returned Effect. Required when the returned value is a streaming HttpServerResponse:
67
+ // the response body keeps producing chunks (and using layer-provided state) after the
68
+ // Effect returns, so a sub-scope would close too early and run finalizers mid-stream.
69
+ export const provideOnRequestScope =
70
+ <ROut, E2, RIn>(layer: Layer.Layer<ROut, E2, RIn>) => <A, E, R>(self: Effect.Effect<A, E, R>) =>
71
+ Effect.gen(function*() {
72
+ const requestScope = yield* Effect.scope
73
+ // Fresh MemoMap per request: `Layer.buildWithScope` would otherwise reuse
74
+ // the ambient MemoMap living on the HTTP server fiber, sharing the built
75
+ // value (e.g. ContextMap) across every request handled by that server.
76
+ const memoMap = yield* Layer.makeMemoMap
77
+ const ctx = yield* Layer.buildWithMemoMap(layer, memoMap, requestScope)
78
+ return yield* Effect.provide(self, ctx)
79
+ })
80
+
81
+ export const setupRequestContextFromCurrent =
82
+ (name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
83
+ self
84
+ .pipe(
85
+ options?.withTransaction === true ? withSqlTransaction : (_) => _,
86
+ withRequestSpan(name, options),
87
+ Effect.provide(requestStateLayer, { local: true })
88
+ )
89
+
90
+ // Streaming variant: binds ContextMapContainer to the ambient (request) scope so its
91
+ // finalizer (clear()) runs only after the response body is fully drained, not when the
92
+ // outer Effect returns its HttpServerResponse value. Use for handlers that return a
93
+ // streaming HttpServerResponse (e.g. SSE) — see RequestContextMiddleware for context.
94
+ export const setupStreamingRequestContextFromCurrent =
95
+ (name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
96
+ self.pipe(
97
+ options?.withTransaction === true ? withSqlTransaction : (_) => _,
98
+ withRequestSpan(name, options),
99
+ provideOnRequestScope(requestStateLayer)
100
+ )
101
+
102
+ // TODO: consider integrating Effect.withParentSpan
103
+ export function setupRequestContext<R, E, A>(
104
+ self: Effect.Effect<A, E, R>,
105
+ requestContext: RequestContext,
106
+ options?: SetupRequestOptions
107
+ ) {
108
+ const layer = Layer.mergeAll(
109
+ requestStateLayer,
110
+ Layer.succeed(LocaleRef, requestContext.locale),
111
+ Layer.succeed(storeId, requestContext.namespace)
112
+ )
113
+ return self
114
+ .pipe(
115
+ options?.withTransaction === true ? withSqlTransaction : (_) => _,
116
+ withRequestSpan(requestContext.name),
117
+ Effect.provide(layer, { local: true })
118
+ )
119
+ }
120
+
121
+ export function setupRequestContextWithCustomSpan<R, E, A>(
122
+ self: Effect.Effect<A, E, R>,
123
+ requestContext: RequestContext,
124
+ name: string,
125
+ options?: Tracer.SpanOptions & SetupRequestOptions
126
+ ) {
127
+ const layer = Layer.mergeAll(
128
+ requestStateLayer,
129
+ Layer.succeed(LocaleRef, requestContext.locale),
130
+ Layer.succeed(storeId, requestContext.namespace)
131
+ )
132
+ return self
133
+ .pipe(
134
+ options?.withTransaction === true ? withSqlTransaction : (_) => _,
135
+ withRequestSpan(name, options),
136
+ Effect.provide(layer, { local: true })
137
+ )
138
+ }
package/src/toast.ts ADDED
@@ -0,0 +1,54 @@
1
+ import * as Context from "./Context.ts"
2
+ import { accessEffectFn } from "./Context.ts"
3
+ import * as Effect from "./Effect.ts"
4
+ import * as Option from "./Option.ts"
5
+
6
+ export type ToastId = string | number
7
+ export type ToastOpts = { id?: ToastId; timeout?: number; groupId?: string; requestId?: string }
8
+ export type ToastOptsInternal = { id?: ToastId | null; timeout?: number; groupId?: string; requestId?: string }
9
+
10
+ export type UseToast = () => {
11
+ error: (this: void, message: string, options?: ToastOpts) => ToastId
12
+ warning: (this: void, message: string, options?: ToastOpts) => ToastId
13
+ success: (this: void, message: string, options?: ToastOpts) => ToastId
14
+ info: (this: void, message: string, options?: ToastOpts) => ToastId
15
+ dismiss: (this: void, id: ToastId) => void
16
+ }
17
+
18
+ export class CurrentToastId extends Context.Opaque<CurrentToastId, { toastId: ToastId }>()("CurrentToastId") {}
19
+
20
+ /** fallback to CurrentToastId when available unless id is explicitly set to a value or null */
21
+ export const wrap = (toast: ReturnType<UseToast>) => {
22
+ const wrap = (toastHandler: (message: string, options?: ToastOpts) => ToastId) => {
23
+ return (message: string, options?: ToastOptsInternal) =>
24
+ Effect.serviceOption(CurrentToastId).pipe(
25
+ Effect.flatMap((currentToast) =>
26
+ Effect.sync(() => {
27
+ const { id: _id, ...rest } = options ?? {}
28
+ const id = _id !== undefined
29
+ ? _id ?? undefined
30
+ : Option.getOrUndefined(Option.map(currentToast, (_) => _.toastId))
31
+ // when id is undefined, we may end up with no toast at all..
32
+ return toastHandler(message, id !== undefined ? { ...rest, id } : rest)
33
+ })
34
+ )
35
+ )
36
+ }
37
+ return {
38
+ error: wrap(toast.error),
39
+ info: wrap(toast.info),
40
+ success: wrap(toast.success),
41
+ warning: wrap(toast.warning),
42
+ dismiss: (toastId: ToastId) => Effect.sync(() => toast.dismiss(toastId))
43
+ }
44
+ }
45
+
46
+ type ToastShape = ReturnType<typeof wrap>
47
+
48
+ export class Toast extends Context.Opaque<Toast, ToastShape>()("Toast") {
49
+ static readonly error = accessEffectFn(this, "error")
50
+ static readonly info = accessEffectFn(this, "info")
51
+ static readonly success = accessEffectFn(this, "success")
52
+ static readonly warning = accessEffectFn(this, "warning")
53
+ static readonly dismiss = accessEffectFn(this, "dismiss")
54
+ }