effect-app 4.0.0-beta.99 → 4.0.1

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 (391) hide show
  1. package/CHANGELOG.md +2042 -0
  2. package/dist/Array.d.ts +1 -17
  3. package/dist/Array.d.ts.map +1 -1
  4. package/dist/Array.js +3 -27
  5. package/dist/Chunk.d.ts +3 -27
  6. package/dist/Chunk.d.ts.map +1 -1
  7. package/dist/Chunk.js +4 -39
  8. package/dist/Config/SecretURL.d.ts +3 -1
  9. package/dist/Config/SecretURL.d.ts.map +1 -1
  10. package/dist/Config/SecretURL.js +3 -6
  11. package/dist/Config/internal/configSecretURL.d.ts.map +1 -1
  12. package/dist/Config/internal/configSecretURL.js +2 -2
  13. package/dist/Config.d.ts +0 -5
  14. package/dist/Config.d.ts.map +1 -1
  15. package/dist/Config.js +7 -5
  16. package/dist/ConfigProvider.d.ts +0 -37
  17. package/dist/ConfigProvider.d.ts.map +1 -1
  18. package/dist/ConfigProvider.js +5 -41
  19. package/dist/Context.d.ts +6 -4
  20. package/dist/Context.d.ts.map +1 -1
  21. package/dist/Context.js +4 -3
  22. package/dist/DataDependencies.d.ts +136 -0
  23. package/dist/DataDependencies.d.ts.map +1 -0
  24. package/dist/DataDependencies.js +112 -0
  25. package/dist/DateTime.d.ts +33 -0
  26. package/dist/DateTime.d.ts.map +1 -0
  27. package/dist/DateTime.js +18 -0
  28. package/dist/Effect.d.ts +3 -14
  29. package/dist/Effect.d.ts.map +1 -1
  30. package/dist/Effect.js +8 -26
  31. package/dist/Emailer.d.ts +51 -0
  32. package/dist/Emailer.d.ts.map +1 -0
  33. package/dist/Emailer.js +7 -0
  34. package/dist/Function.d.ts +0 -4
  35. package/dist/Function.d.ts.map +1 -1
  36. package/dist/Function.js +1 -7
  37. package/dist/Layer.d.ts +6 -3
  38. package/dist/Layer.d.ts.map +1 -1
  39. package/dist/Layer.js +3 -2
  40. package/dist/Model/Repository/Registry.d.ts +22 -0
  41. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  42. package/dist/Model/Repository/Registry.js +18 -0
  43. package/dist/Model/Repository/dependency.d.ts +6 -0
  44. package/dist/Model/Repository/dependency.d.ts.map +1 -0
  45. package/dist/Model/Repository/dependency.js +29 -0
  46. package/dist/Model/Repository/ext.d.ts +60 -0
  47. package/dist/Model/Repository/ext.d.ts.map +1 -0
  48. package/dist/Model/Repository/ext.js +124 -0
  49. package/dist/Model/Repository/internal/internal.d.ts +68 -0
  50. package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
  51. package/dist/Model/Repository/internal/internal.js +610 -0
  52. package/dist/Model/Repository/legacy.d.ts +21 -0
  53. package/dist/Model/Repository/legacy.d.ts.map +1 -0
  54. package/dist/Model/Repository/legacy.js +2 -0
  55. package/dist/Model/Repository/makeRepo.d.ts +71 -0
  56. package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
  57. package/dist/Model/Repository/makeRepo.js +30 -0
  58. package/dist/Model/Repository/service.d.ts +121 -0
  59. package/dist/Model/Repository/service.d.ts.map +1 -0
  60. package/dist/Model/Repository/service.js +2 -0
  61. package/dist/Model/Repository/validation.d.ts +72 -0
  62. package/dist/Model/Repository/validation.d.ts.map +1 -0
  63. package/dist/Model/Repository/validation.js +39 -0
  64. package/dist/Model/Repository.d.ts +8 -0
  65. package/dist/Model/Repository.d.ts.map +1 -0
  66. package/dist/Model/Repository.js +8 -0
  67. package/dist/Model/dsl.d.ts +34 -0
  68. package/dist/Model/dsl.d.ts.map +1 -0
  69. package/dist/Model/dsl.js +45 -0
  70. package/dist/Model/filter/filterApi.d.ts +30 -0
  71. package/dist/Model/filter/filterApi.d.ts.map +1 -0
  72. package/dist/Model/filter/filterApi.js +2 -0
  73. package/dist/Model/filter/types/errors.d.ts +29 -0
  74. package/dist/Model/filter/types/errors.d.ts.map +1 -0
  75. package/dist/Model/filter/types/errors.js +2 -0
  76. package/dist/Model/filter/types/fields.d.ts +15 -0
  77. package/dist/Model/filter/types/fields.d.ts.map +1 -0
  78. package/dist/Model/filter/types/fields.js +2 -0
  79. package/dist/Model/filter/types/path/common.d.ts +316 -0
  80. package/dist/Model/filter/types/path/common.d.ts.map +1 -0
  81. package/dist/Model/filter/types/path/common.js +2 -0
  82. package/dist/Model/filter/types/path/eager.d.ts +94 -0
  83. package/dist/Model/filter/types/path/eager.d.ts.map +1 -0
  84. package/dist/Model/filter/types/path/eager.js +36 -0
  85. package/dist/Model/filter/types/path/index.d.ts +4 -0
  86. package/dist/Model/filter/types/path/index.d.ts.map +1 -0
  87. package/dist/Model/filter/types/path/index.js +3 -0
  88. package/dist/Model/filter/types/utils.d.ts +79 -0
  89. package/dist/Model/filter/types/utils.d.ts.map +1 -0
  90. package/dist/Model/filter/types/utils.js +2 -0
  91. package/dist/Model/filter/types/validator.d.ts +30 -0
  92. package/dist/Model/filter/types/validator.d.ts.map +1 -0
  93. package/dist/Model/filter/types/validator.js +2 -0
  94. package/dist/Model/filter/types.d.ts +5 -0
  95. package/dist/Model/filter/types.d.ts.map +1 -0
  96. package/dist/Model/filter/types.js +7 -0
  97. package/dist/Model/query/dsl.d.ts +630 -0
  98. package/dist/Model/query/dsl.d.ts.map +1 -0
  99. package/dist/Model/query/dsl.js +376 -0
  100. package/dist/Model/query/new-kid-interpreter.d.ts +132 -0
  101. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
  102. package/dist/Model/query/new-kid-interpreter.js +369 -0
  103. package/dist/Model/query.d.ts +15 -0
  104. package/dist/Model/query.d.ts.map +1 -0
  105. package/dist/Model/query.js +3 -0
  106. package/dist/Model.d.ts +6 -0
  107. package/dist/Model.d.ts.map +1 -0
  108. package/dist/Model.js +6 -0
  109. package/dist/NonEmptySet.d.ts +3 -1
  110. package/dist/NonEmptySet.d.ts.map +1 -1
  111. package/dist/NonEmptySet.js +2 -2
  112. package/dist/Option.d.ts +1 -2
  113. package/dist/Option.d.ts.map +1 -1
  114. package/dist/Option.js +3 -7
  115. package/dist/Pure.d.ts +9 -7
  116. package/dist/Pure.d.ts.map +1 -1
  117. package/dist/Pure.js +20 -17
  118. package/dist/QueueMaker.d.ts +11 -0
  119. package/dist/QueueMaker.d.ts.map +1 -0
  120. package/dist/QueueMaker.js +11 -0
  121. package/dist/RequestContext.d.ts +87 -0
  122. package/dist/RequestContext.d.ts.map +1 -0
  123. package/dist/RequestContext.js +45 -0
  124. package/dist/RequestScopedDependencies.d.ts +21 -0
  125. package/dist/RequestScopedDependencies.d.ts.map +1 -0
  126. package/dist/RequestScopedDependencies.js +16 -0
  127. package/dist/Schema/Class.d.ts +163 -16
  128. package/dist/Schema/Class.d.ts.map +1 -1
  129. package/dist/Schema/Class.js +193 -21
  130. package/dist/Schema/FastCheck.d.ts.map +1 -1
  131. package/dist/Schema/SchemaParser.d.ts +5 -0
  132. package/dist/Schema/SchemaParser.d.ts.map +1 -0
  133. package/dist/Schema/SchemaParser.js +6 -0
  134. package/dist/Schema/SpecialJsonSchema.d.ts +15 -2
  135. package/dist/Schema/SpecialJsonSchema.d.ts.map +1 -1
  136. package/dist/Schema/SpecialJsonSchema.js +70 -12
  137. package/dist/Schema/SpecialOpenApi.d.ts +1 -1
  138. package/dist/Schema/SpecialOpenApi.js +3 -3
  139. package/dist/Schema/brand.d.ts +5 -5
  140. package/dist/Schema/brand.d.ts.map +1 -1
  141. package/dist/Schema/brand.js +6 -4
  142. package/dist/Schema/email.d.ts.map +1 -1
  143. package/dist/Schema/email.js +8 -5
  144. package/dist/Schema/ext.d.ts +279 -75
  145. package/dist/Schema/ext.d.ts.map +1 -1
  146. package/dist/Schema/ext.js +330 -72
  147. package/dist/Schema/moreStrings.d.ts +82 -48
  148. package/dist/Schema/moreStrings.d.ts.map +1 -1
  149. package/dist/Schema/moreStrings.js +85 -51
  150. package/dist/Schema/numbers.d.ts +33 -21
  151. package/dist/Schema/numbers.d.ts.map +1 -1
  152. package/dist/Schema/numbers.js +64 -12
  153. package/dist/Schema/phoneNumber.d.ts.map +1 -1
  154. package/dist/Schema/phoneNumber.js +6 -6
  155. package/dist/Schema/schema.d.ts +1 -1
  156. package/dist/Schema/schema.d.ts.map +1 -1
  157. package/dist/Schema/schema.js +2 -2
  158. package/dist/Schema/strings.d.ts +18 -4
  159. package/dist/Schema/strings.d.ts.map +1 -1
  160. package/dist/Schema/strings.js +1 -5
  161. package/dist/Schema.d.ts +224 -7
  162. package/dist/Schema.d.ts.map +1 -1
  163. package/dist/Schema.js +190 -24
  164. package/dist/SchemaAST.d.ts +2 -0
  165. package/dist/SchemaAST.d.ts.map +1 -0
  166. package/dist/SchemaAST.js +2 -0
  167. package/dist/Set.d.ts +4 -1
  168. package/dist/Set.d.ts.map +1 -1
  169. package/dist/Set.js +8 -6
  170. package/dist/Store.d.ts +215 -0
  171. package/dist/Store.d.ts.map +1 -0
  172. package/dist/Store.js +113 -0
  173. package/dist/_ext/Array.d.ts +1 -1
  174. package/dist/_ext/Array.d.ts.map +1 -1
  175. package/dist/_ext/Array.js +4 -2
  176. package/dist/_ext/ord.ext.d.ts +2 -1
  177. package/dist/_ext/ord.ext.d.ts.map +1 -1
  178. package/dist/_ext/ord.ext.js +2 -2
  179. package/dist/builtin.d.ts +2 -14
  180. package/dist/builtin.d.ts.map +1 -1
  181. package/dist/builtin.js +1 -1
  182. package/dist/client/InvalidationKeys.d.ts +30 -0
  183. package/dist/client/InvalidationKeys.d.ts.map +1 -0
  184. package/dist/client/InvalidationKeys.js +37 -0
  185. package/dist/client/apiClientFactory.d.ts +13 -8
  186. package/dist/client/apiClientFactory.d.ts.map +1 -1
  187. package/dist/client/apiClientFactory.js +154 -34
  188. package/dist/client/clientFor.d.ts +48 -13
  189. package/dist/client/clientFor.d.ts.map +1 -1
  190. package/dist/client/clientFor.js +9 -1
  191. package/dist/client/errors.d.ts +74 -28
  192. package/dist/client/errors.d.ts.map +1 -1
  193. package/dist/client/errors.js +41 -10
  194. package/dist/client/makeClient.d.ts +487 -32
  195. package/dist/client/makeClient.d.ts.map +1 -1
  196. package/dist/client/makeClient.js +47 -21
  197. package/dist/client.d.ts +2 -0
  198. package/dist/client.d.ts.map +1 -1
  199. package/dist/client.js +3 -1
  200. package/dist/faker.d.ts.map +1 -1
  201. package/dist/http/Request.d.ts +2 -2
  202. package/dist/http/Request.d.ts.map +1 -1
  203. package/dist/http/Request.js +2 -2
  204. package/dist/http/internal/lib.d.ts +12 -12
  205. package/dist/http/internal/lib.d.ts.map +1 -1
  206. package/dist/http/internal/lib.js +13 -13
  207. package/dist/ids.d.ts +32 -14
  208. package/dist/ids.d.ts.map +1 -1
  209. package/dist/ids.js +38 -7
  210. package/dist/index.d.ts +3 -0
  211. package/dist/index.d.ts.map +1 -1
  212. package/dist/index.js +5 -1
  213. package/dist/middleware.d.ts +12 -6
  214. package/dist/middleware.d.ts.map +1 -1
  215. package/dist/middleware.js +13 -7
  216. package/dist/migration/nativeEffect.d.ts +21 -0
  217. package/dist/migration/nativeEffect.d.ts.map +1 -0
  218. package/dist/migration/nativeEffect.js +598 -0
  219. package/dist/rpc/Invalidation.d.ts +1124 -0
  220. package/dist/rpc/Invalidation.d.ts.map +1 -0
  221. package/dist/rpc/Invalidation.js +178 -0
  222. package/dist/rpc/MiddlewareMaker.d.ts +10 -7
  223. package/dist/rpc/MiddlewareMaker.d.ts.map +1 -1
  224. package/dist/rpc/MiddlewareMaker.js +57 -37
  225. package/dist/rpc/RpcContextMap.d.ts +2 -2
  226. package/dist/rpc/RpcContextMap.d.ts.map +1 -1
  227. package/dist/rpc/RpcMiddleware.d.ts +13 -10
  228. package/dist/rpc/RpcMiddleware.d.ts.map +1 -1
  229. package/dist/rpc/RpcMiddleware.js +2 -2
  230. package/dist/rpc.d.ts +1 -1
  231. package/dist/rpc.d.ts.map +1 -1
  232. package/dist/rpc.js +2 -2
  233. package/dist/runtime.d.ts +19 -0
  234. package/dist/runtime.d.ts.map +1 -0
  235. package/dist/runtime.js +40 -0
  236. package/dist/setupRequest.d.ts +20 -0
  237. package/dist/setupRequest.d.ts.map +1 -0
  238. package/dist/setupRequest.js +72 -0
  239. package/dist/toast.d.ts +51 -0
  240. package/dist/toast.d.ts.map +1 -0
  241. package/dist/toast.js +34 -0
  242. package/dist/transform.d.ts +1 -1
  243. package/dist/transform.d.ts.map +1 -1
  244. package/dist/transform.js +4 -5
  245. package/dist/utils/effectify.d.ts +1 -1
  246. package/dist/utils/effectify.d.ts.map +1 -1
  247. package/dist/utils/effectify.js +2 -2
  248. package/dist/utils/extend.d.ts +6 -1
  249. package/dist/utils/extend.d.ts.map +1 -1
  250. package/dist/utils/extend.js +24 -6
  251. package/dist/utils/gen.d.ts +4 -4
  252. package/dist/utils/gen.d.ts.map +1 -1
  253. package/dist/utils/logLevel.d.ts +2 -2
  254. package/dist/utils/logLevel.d.ts.map +1 -1
  255. package/dist/utils/logger.d.ts +2 -1
  256. package/dist/utils/logger.d.ts.map +1 -1
  257. package/dist/utils/logger.js +2 -2
  258. package/dist/utils.d.ts +34 -45
  259. package/dist/utils.d.ts.map +1 -1
  260. package/dist/utils.js +33 -49
  261. package/dist/validation/validators.d.ts.map +1 -1
  262. package/dist/validation.d.ts.map +1 -1
  263. package/dist/withToast.d.ts +30 -0
  264. package/dist/withToast.d.ts.map +1 -0
  265. package/dist/withToast.js +64 -0
  266. package/package.json +25 -266
  267. package/src/Array.ts +7 -39
  268. package/src/Chunk.ts +6 -55
  269. package/src/Config/SecretURL.ts +6 -3
  270. package/src/Config/internal/configSecretURL.ts +2 -2
  271. package/src/Config.ts +6 -13
  272. package/src/ConfigProvider.ts +4 -47
  273. package/src/Context.ts +9 -8
  274. package/src/DataDependencies.ts +171 -0
  275. package/src/DateTime.ts +473 -0
  276. package/src/Effect.ts +10 -32
  277. package/src/Emailer.ts +51 -0
  278. package/src/Function.ts +0 -7
  279. package/src/Inputify.type.ts +1 -1
  280. package/src/Layer.ts +8 -5
  281. package/src/Model/Repository/Registry.ts +35 -0
  282. package/src/Model/Repository/dependency.ts +32 -0
  283. package/src/Model/Repository/ext.ts +374 -0
  284. package/src/Model/Repository/internal/internal.ts +987 -0
  285. package/src/Model/Repository/legacy.ts +29 -0
  286. package/src/Model/Repository/makeRepo.ts +168 -0
  287. package/src/Model/Repository/service.ts +676 -0
  288. package/src/Model/Repository/validation.ts +38 -0
  289. package/src/Model/Repository.ts +7 -0
  290. package/src/Model/dsl.ts +130 -0
  291. package/src/Model/filter/filterApi.ts +78 -0
  292. package/src/Model/filter/types/errors.ts +47 -0
  293. package/src/Model/filter/types/fields.ts +50 -0
  294. package/src/Model/filter/types/path/common.ts +404 -0
  295. package/src/Model/filter/types/path/eager.ts +329 -0
  296. package/src/Model/filter/types/path/index.ts +4 -0
  297. package/src/Model/filter/types/utils.ts +128 -0
  298. package/src/Model/filter/types/validator.ts +46 -0
  299. package/src/Model/filter/types.ts +6 -0
  300. package/src/Model/query/dsl.ts +3092 -0
  301. package/src/Model/query/new-kid-interpreter.ts +531 -0
  302. package/src/Model/query.ts +13 -0
  303. package/src/Model.ts +5 -0
  304. package/src/NonEmptySet.ts +6 -4
  305. package/src/Option.ts +3 -8
  306. package/src/Pure.ts +20 -17
  307. package/src/QueueMaker.ts +11 -0
  308. package/src/RequestContext.ts +48 -0
  309. package/src/RequestScopedDependencies.ts +45 -0
  310. package/src/Schema/Class.ts +550 -56
  311. package/src/Schema/SchemaParser.ts +12 -0
  312. package/src/Schema/SpecialJsonSchema.ts +75 -7
  313. package/src/Schema/SpecialOpenApi.ts +5 -5
  314. package/src/Schema/brand.ts +27 -12
  315. package/src/Schema/email.ts +9 -7
  316. package/src/Schema/ext.ts +405 -95
  317. package/src/Schema/moreStrings.ts +213 -88
  318. package/src/Schema/numbers.ts +107 -29
  319. package/src/Schema/phoneNumber.ts +8 -9
  320. package/src/Schema/schema.ts +1 -1
  321. package/src/Schema/strings.ts +23 -14
  322. package/src/Schema.ts +403 -46
  323. package/src/SchemaAST.ts +1 -0
  324. package/src/Set.ts +10 -5
  325. package/src/Store.ts +325 -0
  326. package/src/_ext/Array.ts +4 -2
  327. package/src/_ext/ord.ext.ts +2 -1
  328. package/src/builtin-json.d.ts +24 -0
  329. package/src/builtin.ts +16 -21
  330. package/src/client/InvalidationKeys.ts +55 -0
  331. package/src/client/apiClientFactory.ts +295 -129
  332. package/src/client/clientFor.ts +101 -33
  333. package/src/client/errors.ts +66 -20
  334. package/src/client/makeClient.ts +519 -60
  335. package/src/client.ts +6 -4
  336. package/src/http/Request.ts +4 -4
  337. package/src/http/internal/lib.ts +12 -12
  338. package/src/http.ts +1 -1
  339. package/src/ids.ts +45 -8
  340. package/src/index.ts +20 -16
  341. package/src/middleware.ts +12 -8
  342. package/src/migration/nativeEffect.ts +645 -0
  343. package/src/rpc/Invalidation.ts +278 -0
  344. package/src/rpc/MiddlewareMaker.ts +86 -79
  345. package/src/rpc/README.md +2 -2
  346. package/src/rpc/RpcContextMap.ts +4 -4
  347. package/src/rpc/RpcMiddleware.ts +18 -13
  348. package/src/rpc.ts +4 -4
  349. package/src/runtime.ts +57 -0
  350. package/src/setupRequest.ts +138 -0
  351. package/src/toast.ts +54 -0
  352. package/src/transform.ts +4 -4
  353. package/src/utils/effectify.ts +1 -1
  354. package/src/utils/extend.ts +24 -5
  355. package/src/utils/gen.ts +7 -7
  356. package/src/utils/logLevel.ts +1 -1
  357. package/src/utils/logger.ts +3 -2
  358. package/src/utils.ts +85 -175
  359. package/src/validation.ts +2 -2
  360. package/src/withToast.ts +133 -0
  361. package/test/dateTimeLocal.test.ts +55 -0
  362. package/test/dist/dateTimeLocal.test.d.ts.map +1 -0
  363. package/test/dist/nativeEffectMigration.test.d.ts.map +1 -0
  364. package/test/dist/rpc.test.d.ts.map +1 -1
  365. package/test/dist/rpcMiddlewareContext.test.d.ts.map +1 -0
  366. package/test/moreStrings.test.ts +55 -10
  367. package/test/nativeEffectMigration.test.ts +77 -0
  368. package/test/rpc.test.ts +28 -11
  369. package/test/rpcMiddlewareContext.test.ts +63 -0
  370. package/test/schema.test.ts +318 -55
  371. package/test/secretURL.test.ts +4 -1
  372. package/test/special.test.ts +663 -131
  373. package/test/utils.test.ts +5 -5
  374. package/tsconfig.base.json +6 -4
  375. package/tsconfig.json +9 -1
  376. package/tsconfig.src.json +29 -29
  377. package/tsconfig.test.json +2 -2
  378. package/dist/Operations.d.ts +0 -124
  379. package/dist/Operations.d.ts.map +0 -1
  380. package/dist/Operations.js +0 -29
  381. package/dist/_ext/date.d.ts +0 -15
  382. package/dist/_ext/date.d.ts.map +0 -1
  383. package/dist/_ext/date.js +0 -16
  384. package/dist/_ext/misc.d.ts +0 -14
  385. package/dist/_ext/misc.d.ts.map +0 -1
  386. package/dist/_ext/misc.js +0 -17
  387. package/eslint.config.mjs +0 -26
  388. package/src/Operations.ts +0 -55
  389. package/src/_ext/date.ts +0 -27
  390. package/src/_ext/misc.ts +0 -46
  391. package/tsconfig.json.bak +0 -12
@@ -0,0 +1,278 @@
1
+ import * as Ref from "effect/Ref"
2
+ import { Rpc } from "effect/rpc"
3
+ import { type ClientForOptions, makeQueryKey } from "../client/clientFor.ts"
4
+ import * as Context from "../Context.ts"
5
+ import * as DataDependencies from "../DataDependencies.ts"
6
+ import * as Effect from "../Effect.ts"
7
+ import * as S from "../Schema.ts"
8
+
9
+ /**
10
+ * Shorthand for a handler-derived invalidation key.
11
+ * Accepts an RPC handler object so `add(GetMe)` is equivalent to
12
+ * `add(makeQueryKey(GetMe))`.
13
+ */
14
+ export type InvalidationKeyInput = InvalidationKey | { readonly id: string; readonly options?: ClientForOptions }
15
+
16
+ const normalizeKey = (input: InvalidationKeyInput): InvalidationKey => {
17
+ if (Array.isArray(input)) return input
18
+ const handler = input as { id: string; options?: ClientForOptions }
19
+ return makeQueryKey(handler.options ? { id: handler.id, options: handler.options } : { id: handler.id })
20
+ }
21
+
22
+ // Disambiguates `add` overloads: a `string[]` means a single key; an array
23
+ // containing arrays-or-objects means a batch of inputs.
24
+ const isBatch = (
25
+ input: InvalidationKeyInput | ReadonlyArray<InvalidationKeyInput>
26
+ ): input is ReadonlyArray<InvalidationKeyInput> =>
27
+ Array.isArray(input) && input[0] !== undefined && typeof input[0] !== "string"
28
+
29
+ const normalizeInputs = (
30
+ input: InvalidationKeyInput | ReadonlyArray<InvalidationKeyInput>
31
+ ): ReadonlyArray<InvalidationKey> => isBatch(input) ? input.map(normalizeKey) : [normalizeKey(input)]
32
+
33
+ /**
34
+ * A single segment within an `InvalidationKey` array.
35
+ * Accepts any JSON-compatible value: string, number, boolean, null,
36
+ * arrays and objects recursively — matching TanStack Query's `queryKey` element type.
37
+ */
38
+ export const InvalidationKeySegment = S.Json
39
+ export type InvalidationKeySegment = S.Schema.Type<typeof InvalidationKeySegment>
40
+
41
+ /** Schema for a single invalidation key – an array of segments compatible with TanStack Query `queryKey`. */
42
+ export const InvalidationKey = S.Array(InvalidationKeySegment)
43
+ export type InvalidationKey = S.Schema.Type<typeof InvalidationKey>
44
+
45
+ /** Schema for the full set of invalidation keys – an array of `InvalidationKey`. */
46
+ export const InvalidationKeys = S.Array(InvalidationKey)
47
+ export type InvalidationKeys = S.Schema.Type<typeof InvalidationKeys>
48
+
49
+ /** Metadata included in every command response for server-driven cache invalidation. */
50
+ export const CommandMetaData = S.Struct({
51
+ invalidateQueries: InvalidationKeys,
52
+ dataDependencies: DataDependencies.DataDependencySet
53
+ })
54
+ export type CommandMetaData = S.Schema.Type<typeof CommandMetaData>
55
+
56
+ export const emptyDataDependencies: DataDependencies.DataDependencySet = {
57
+ reads: DataDependencies.empty(),
58
+ writes: DataDependencies.empty()
59
+ }
60
+
61
+ export const makeMetaData = (
62
+ invalidateQueries: InvalidationKeys,
63
+ dataDependencies: DataDependencies.DataDependencySet = emptyDataDependencies
64
+ ): CommandMetaData => ({ invalidateQueries, dataDependencies })
65
+
66
+ export const QueryResponseWithMetaData = <S extends S.Top>(success: S) =>
67
+ S.Struct({ payload: success, metadata: S.Struct({ dataDependencies: DataDependencies.DataDependencySet }) })
68
+
69
+ /**
70
+ * Wraps a command's success schema so that the wire format carries both the `payload`
71
+ * (the handler's actual return value) and `metadata` (server-driven cache invalidation keys).
72
+ * Transparent to users: the server handler returns the plain payload and the client receives
73
+ * the plain payload — wrapping/unwrapping is handled internally by the routing layer.
74
+ */
75
+ export const CommandResponseWithMetaData = <S extends S.Top>(success: S) =>
76
+ S.Struct({ payload: success, metadata: CommandMetaData })
77
+
78
+ /**
79
+ * Wraps a command's failure schema so that the wire format carries both the `error`
80
+ * (the handler's actual failure value) and `metadata` (server-driven cache invalidation keys
81
+ * accumulated thus far before the failure occurred).
82
+ * Transparent to users: the server handler fails with the plain error and the client receives
83
+ * the plain error — wrapping/unwrapping is handled internally by the routing layer.
84
+ */
85
+ export const CommandFailureWithMetaData = <E extends S.Top>(error: E) =>
86
+ S.Struct({ _tag: S.Literal("CommandFailureWithMetaData"), error, metadata: CommandMetaData })
87
+
88
+ /**
89
+ * Stream chunk schema for stream responses with metadata.
90
+ * Each item is either a data value, an intermediate "metadata" signal carrying cache
91
+ * invalidation keys accumulated since the previous drain, or a final "done" signal.
92
+ * Transparent to users: stream handlers return plain values and clients receive plain values —
93
+ * wrapping/unwrapping is handled internally by the routing layer.
94
+ *
95
+ * The "done" chunk is always the last item in the stream and carries any remaining invalidation
96
+ * keys. An optional "metadata" chunk may appear after any "value" chunk and carries keys
97
+ * accumulated since the last drain (V3: mid-stream invalidation).
98
+ */
99
+ export const StreamResponseChunk = <S extends S.Top>(success: S) =>
100
+ S.Union([
101
+ S.Struct({ _tag: S.Literal("value"), value: success }),
102
+ S.Struct({ _tag: S.Literal("metadata"), metadata: CommandMetaData }),
103
+ S.Struct({ _tag: S.Literal("done"), metadata: CommandMetaData })
104
+ ])
105
+
106
+ export type StreamResponseChunk<A> =
107
+ | { readonly _tag: "value"; readonly value: A }
108
+ | { readonly _tag: "metadata"; readonly metadata: CommandMetaData }
109
+ | { readonly _tag: "done"; readonly metadata: CommandMetaData }
110
+
111
+ /**
112
+ * Stream chunk schema for stream failures with metadata.
113
+ * Used to signal a stream failure while still carrying cache invalidation keys
114
+ * accumulated thus far.
115
+ */
116
+ export const StreamFailureChunk = <E extends S.Top>(error: E) =>
117
+ S.Struct({ _tag: S.Literal("error"), error, metadata: CommandMetaData })
118
+
119
+ export type StreamFailureChunk<E> = { readonly _tag: "error"; readonly error: E; readonly metadata: CommandMetaData }
120
+
121
+ /**
122
+ * Context annotation for declaring static cache invalidation keys on a low-level `Rpc` definition.
123
+ * These keys are always included in the command response metadata, regardless of the handler logic.
124
+ *
125
+ * Prefer using `makeQueryKey` over raw string arrays to stay in sync with the actual query
126
+ * definitions without manual string maintenance:
127
+ *
128
+ * ```ts
129
+ * import { makeQueryKey } from "effect-app/client"
130
+ * import { Invalidation } from "effect-app/rpc"
131
+ * import * as UserRsc from "../User/index.ts" // separate module to avoid circular deps
132
+ *
133
+ * class UpdateProfile extends Rpc.make("UpdateProfile", { ... })
134
+ * .annotate(Invalidation.Invalidates, [makeQueryKey(UserRsc.GetMe), makeQueryKey(UserRsc.GetProfile)]) {}
135
+ * ```
136
+ *
137
+ * **Circular dependency note:** if mutations and queries live in the same file you may hit a
138
+ * circular reference at evaluation time. The idiomatic fix is to move mutations into their own
139
+ * module (e.g. `User/mutations.ts`) that directly imports the relevant query classes rather than
140
+ * re-exporting them through a barrel.
141
+ *
142
+ * For the higher-level `Command`/`Query` builders from `makeRpcClient`, use the
143
+ * `invalidatesQueries` callback argument instead (it receives the same query keys at runtime).
144
+ */
145
+ export const Invalidates = Context.Reference<ReadonlyArray<InvalidationKey>>(
146
+ "effect-app/rpc/Invalidates",
147
+ { defaultValue: () => [] }
148
+ )
149
+ export type Invalidates = typeof Invalidates
150
+
151
+ /** The shape of the per-request service that accumulates invalidation keys. */
152
+ export interface InvalidationSetService {
153
+ readonly add: (
154
+ input: InvalidationKeyInput | ReadonlyArray<InvalidationKeyInput>
155
+ ) => Effect.Effect<void>
156
+ readonly get: Effect.Effect<ReadonlyArray<InvalidationKey>>
157
+ /**
158
+ * V3: Reads all currently accumulated keys and resets the bucket to empty.
159
+ * Used by the stream routing layer to emit intermediate "metadata" chunks
160
+ * without re-sending keys that have already been forwarded to the client.
161
+ */
162
+ readonly drain: Effect.Effect<ReadonlyArray<InvalidationKey>>
163
+ }
164
+
165
+ /**
166
+ * Request-scoped service for accumulating invalidation keys dynamically inside a handler.
167
+ * Provided by `InvalidationMiddlewareLive` for every RPC call; has a no-op default so it is
168
+ * safe to use even when the HTTP middleware is absent (tests, workers, etc.).
169
+ *
170
+ * Use `InvalidationSet.add(key)` as a shorthand to skip `.use(_ => _.add(key))`. The
171
+ * underlying service is still available via `.use` / `.useSync` for advanced cases.
172
+ *
173
+ * `add` accepts an RPC handler directly (e.g. `UserRsc.GetMe`) — its query key is derived via
174
+ * `makeQueryKey` so keys stay in sync with the actual query definitions. Raw `InvalidationKey`
175
+ * arrays and arrays of either form are also accepted.
176
+ *
177
+ * ```ts
178
+ * import * as Effect from "effect/Effect"
179
+ * import { Invalidation } from "effect-app/rpc"
180
+ * import * as CartRsc from "../Cart/queries.ts"
181
+ * import * as UserRsc from "../User/queries.ts"
182
+ *
183
+ * const handler = Effect.fnUntraced(function*(req: UpdateCartRequest) {
184
+ * const cart = yield* CartRepo.save(req.cart)
185
+ *
186
+ * // single handler
187
+ * yield* Invalidation.InvalidationSet.add(UserRsc.GetMe)
188
+ *
189
+ * // batch
190
+ * if (cart.isCheckedOut) {
191
+ * yield* Invalidation.InvalidationSet.add([CartRsc.GetCartStats, UserRsc.GetMe])
192
+ * }
193
+ *
194
+ * return cart
195
+ * })
196
+ * ```
197
+ *
198
+ * You can combine static (`Invalidates` annotation) and dynamic (`InvalidationSet.use`) keys:
199
+ * the annotation pre-populates the set before the handler runs; dynamic additions accumulate
200
+ * throughout the handler. All keys are included in the command response metadata.
201
+ */
202
+ const InvalidationSetRef = Context.Reference<InvalidationSetService>(
203
+ "effect-app/rpc/InvalidationSet",
204
+ {
205
+ defaultValue: () => ({
206
+ add: (_input: InvalidationKeyInput | ReadonlyArray<InvalidationKeyInput>) => Effect.void,
207
+ get: Effect.succeed([] as ReadonlyArray<InvalidationKey>),
208
+ drain: Effect.succeed([] as ReadonlyArray<InvalidationKey>)
209
+ })
210
+ }
211
+ )
212
+ export const InvalidationSet = Object.assign(InvalidationSetRef, {
213
+ /**
214
+ * Shortcut for `InvalidationSet.use(_ => _.add(input))`. Accepts a single
215
+ * `InvalidationKeyInput` or an array of them.
216
+ */
217
+ add: (input: InvalidationKeyInput | ReadonlyArray<InvalidationKeyInput>) =>
218
+ InvalidationSetRef.use((_) => _.add(input))
219
+ })
220
+ export type InvalidationSet = typeof InvalidationSetRef
221
+
222
+ /** Creates a fresh `InvalidationSet` implementation backed by a `Ref`. */
223
+ export const makeInvalidationSet = (ref: Ref.Ref<ReadonlyArray<InvalidationKey>>): InvalidationSetService => ({
224
+ add: (input) => Ref.update(ref, (keys) => [...keys, ...normalizeInputs(input)]),
225
+ get: Ref.get(ref),
226
+ drain: Ref.getAndSet(ref, [])
227
+ })
228
+
229
+ /**
230
+ * `Rpc.Custom` definition for command RPCs that wrap the success/error schemas
231
+ * with `CommandResponseWithMetaData` / `CommandFailureWithMetaData`. The wrap
232
+ * lets server forward accumulated invalidation keys on both success and
233
+ * handler-thrown failure paths. Middleware-thrown errors bypass the wrap
234
+ * (the handler never ran, so no metadata) and flow raw at the Cause level —
235
+ * the client decodes them via the `rpc.middlewares[*].error` failure-union
236
+ * channel of `Rpc.exitSchema`.
237
+ */
238
+ // eslint-disable-next-line import/namespace
239
+ export interface CommandRpc extends Rpc.Custom {
240
+ readonly out: Rpc.Custom.Out<
241
+ ReturnType<typeof CommandResponseWithMetaData<this["success"] & S.Top>>,
242
+ ReturnType<typeof CommandFailureWithMetaData<this["error"] & S.Top>>
243
+ >
244
+ }
245
+
246
+ /**
247
+ * Custom Rpc constructor for command RPCs.
248
+ * Wraps the success schema with `CommandResponseWithMetaData` and the error
249
+ * schema with `CommandFailureWithMetaData`.
250
+ */
251
+ export const makeCommandRpc = Rpc.custom<CommandRpc>(({ defect, error, success }) => ({
252
+ success: CommandResponseWithMetaData(success),
253
+ error: CommandFailureWithMetaData(error),
254
+ defect
255
+ }))
256
+
257
+ /**
258
+ * `Rpc.Custom` definition for stream RPCs that wrap the success/error schemas
259
+ * with `StreamResponseChunk` / `StreamFailureChunk`.
260
+ */
261
+ // eslint-disable-next-line import/namespace
262
+ export interface StreamRpc extends Rpc.Custom {
263
+ readonly out: Rpc.Custom.Out<
264
+ ReturnType<typeof StreamResponseChunk<this["success"] & S.Top>>,
265
+ ReturnType<typeof StreamFailureChunk<this["error"] & S.Top>>
266
+ >
267
+ }
268
+
269
+ /**
270
+ * Custom Rpc constructor for stream RPCs.
271
+ * Wraps the success schema with `StreamResponseChunk` and
272
+ * the error schema with `StreamFailureChunk`.
273
+ */
274
+ export const makeStreamRpc = Rpc.custom<StreamRpc>(({ defect, error, success }) => ({
275
+ success: StreamResponseChunk(success),
276
+ error: StreamFailureChunk(error),
277
+ defect
278
+ }))
@@ -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