@effect-app/infra 4.0.0-beta.2 → 4.0.0-beta.200

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 (308) hide show
  1. package/CHANGELOG.md +1514 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +15 -7
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +10 -12
  6. package/dist/Emailer/Sendgrid.d.ts +14 -14
  7. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  8. package/dist/Emailer/Sendgrid.js +16 -15
  9. package/dist/Emailer/fake.d.ts +1 -1
  10. package/dist/Emailer/service.d.ts +11 -5
  11. package/dist/Emailer/service.d.ts.map +1 -1
  12. package/dist/Emailer/service.js +3 -3
  13. package/dist/Emailer.d.ts +1 -1
  14. package/dist/MainFiberSet.d.ts +9 -9
  15. package/dist/MainFiberSet.d.ts.map +1 -1
  16. package/dist/MainFiberSet.js +3 -3
  17. package/dist/Model/Repository/Registry.d.ts +20 -0
  18. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  19. package/dist/Model/Repository/Registry.js +17 -0
  20. package/dist/Model/Repository/ext.d.ts +33 -15
  21. package/dist/Model/Repository/ext.d.ts.map +1 -1
  22. package/dist/Model/Repository/ext.js +54 -2
  23. package/dist/Model/Repository/internal/internal.d.ts +6 -6
  24. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  25. package/dist/Model/Repository/internal/internal.js +43 -32
  26. package/dist/Model/Repository/legacy.d.ts +1 -1
  27. package/dist/Model/Repository/makeRepo.d.ts +7 -6
  28. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  29. package/dist/Model/Repository/makeRepo.js +5 -1
  30. package/dist/Model/Repository/service.d.ts +28 -23
  31. package/dist/Model/Repository/service.d.ts.map +1 -1
  32. package/dist/Model/Repository/validation.d.ts +142 -17
  33. package/dist/Model/Repository/validation.d.ts.map +1 -1
  34. package/dist/Model/Repository/validation.js +5 -5
  35. package/dist/Model/Repository.d.ts +2 -1
  36. package/dist/Model/Repository.d.ts.map +1 -1
  37. package/dist/Model/Repository.js +2 -1
  38. package/dist/Model/dsl.d.ts +4 -4
  39. package/dist/Model/dsl.d.ts.map +1 -1
  40. package/dist/Model/filter/filterApi.d.ts +5 -5
  41. package/dist/Model/filter/filterApi.d.ts.map +1 -1
  42. package/dist/Model/filter/types/errors.d.ts +1 -1
  43. package/dist/Model/filter/types/fields.d.ts +1 -1
  44. package/dist/Model/filter/types/path/common.d.ts +1 -1
  45. package/dist/Model/filter/types/path/eager.d.ts +1 -1
  46. package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
  47. package/dist/Model/filter/types/path/eager.js +1 -1
  48. package/dist/Model/filter/types/path/index.d.ts +1 -1
  49. package/dist/Model/filter/types/utils.d.ts +1 -1
  50. package/dist/Model/filter/types/validator.d.ts +1 -1
  51. package/dist/Model/filter/types.d.ts +1 -1
  52. package/dist/Model/query/dsl.d.ts +25 -25
  53. package/dist/Model/query/dsl.d.ts.map +1 -1
  54. package/dist/Model/query/new-kid-interpreter.d.ts +6 -6
  55. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  56. package/dist/Model/query/new-kid-interpreter.js +3 -3
  57. package/dist/Model/query.d.ts +1 -1
  58. package/dist/Model.d.ts +2 -1
  59. package/dist/Model.d.ts.map +1 -1
  60. package/dist/Model.js +2 -1
  61. package/dist/QueueMaker/SQLQueue.d.ts +6 -8
  62. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  63. package/dist/QueueMaker/SQLQueue.js +106 -115
  64. package/dist/QueueMaker/errors.d.ts +2 -2
  65. package/dist/QueueMaker/errors.d.ts.map +1 -1
  66. package/dist/QueueMaker/memQueue.d.ts +7 -4
  67. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  68. package/dist/QueueMaker/memQueue.js +52 -62
  69. package/dist/QueueMaker/sbqueue.d.ts +6 -3
  70. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  71. package/dist/QueueMaker/sbqueue.js +39 -53
  72. package/dist/QueueMaker/service.d.ts +1 -1
  73. package/dist/RequestContext.d.ts +117 -31
  74. package/dist/RequestContext.d.ts.map +1 -1
  75. package/dist/RequestContext.js +7 -8
  76. package/dist/RequestFiberSet.d.ts +7 -7
  77. package/dist/RequestFiberSet.d.ts.map +1 -1
  78. package/dist/RequestFiberSet.js +5 -5
  79. package/dist/Store/ContextMapContainer.d.ts +20 -4
  80. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  81. package/dist/Store/ContextMapContainer.js +13 -3
  82. package/dist/Store/Cosmos/query.d.ts +1 -1
  83. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  84. package/dist/Store/Cosmos/query.js +10 -12
  85. package/dist/Store/Cosmos.d.ts +1 -1
  86. package/dist/Store/Cosmos.d.ts.map +1 -1
  87. package/dist/Store/Cosmos.js +318 -240
  88. package/dist/Store/Disk.d.ts +2 -2
  89. package/dist/Store/Disk.d.ts.map +1 -1
  90. package/dist/Store/Disk.js +25 -22
  91. package/dist/Store/Memory.d.ts +4 -4
  92. package/dist/Store/Memory.d.ts.map +1 -1
  93. package/dist/Store/Memory.js +27 -22
  94. package/dist/Store/SQL/Pg.d.ts +4 -0
  95. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  96. package/dist/Store/SQL/Pg.js +189 -0
  97. package/dist/Store/SQL/query.d.ts +38 -0
  98. package/dist/Store/SQL/query.d.ts.map +1 -0
  99. package/dist/Store/SQL/query.js +367 -0
  100. package/dist/Store/SQL.d.ts +20 -0
  101. package/dist/Store/SQL.d.ts.map +1 -0
  102. package/dist/Store/SQL.js +381 -0
  103. package/dist/Store/codeFilter.d.ts +1 -1
  104. package/dist/Store/codeFilter.d.ts.map +1 -1
  105. package/dist/Store/codeFilter.js +2 -1
  106. package/dist/Store/index.d.ts +5 -2
  107. package/dist/Store/index.d.ts.map +1 -1
  108. package/dist/Store/index.js +15 -3
  109. package/dist/Store/service.d.ts +18 -7
  110. package/dist/Store/service.d.ts.map +1 -1
  111. package/dist/Store/service.js +24 -6
  112. package/dist/Store/utils.d.ts +1 -1
  113. package/dist/Store/utils.d.ts.map +1 -1
  114. package/dist/Store/utils.js +3 -4
  115. package/dist/Store.d.ts +1 -1
  116. package/dist/adapters/SQL/Model.d.ts +30 -47
  117. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  118. package/dist/adapters/SQL/Model.js +22 -14
  119. package/dist/adapters/SQL.d.ts +1 -1
  120. package/dist/adapters/ServiceBus.d.ts +11 -11
  121. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  122. package/dist/adapters/ServiceBus.js +15 -17
  123. package/dist/adapters/cosmos-client.d.ts +3 -3
  124. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  125. package/dist/adapters/cosmos-client.js +3 -3
  126. package/dist/adapters/index.d.ts +8 -2
  127. package/dist/adapters/index.d.ts.map +1 -1
  128. package/dist/adapters/index.js +8 -2
  129. package/dist/adapters/logger.d.ts +2 -2
  130. package/dist/adapters/logger.d.ts.map +1 -1
  131. package/dist/adapters/memQueue.d.ts +3 -3
  132. package/dist/adapters/memQueue.d.ts.map +1 -1
  133. package/dist/adapters/memQueue.js +3 -3
  134. package/dist/adapters/mongo-client.d.ts +3 -3
  135. package/dist/adapters/mongo-client.d.ts.map +1 -1
  136. package/dist/adapters/mongo-client.js +3 -3
  137. package/dist/adapters/redis-client.d.ts +4 -4
  138. package/dist/adapters/redis-client.d.ts.map +1 -1
  139. package/dist/adapters/redis-client.js +3 -3
  140. package/dist/api/ContextProvider.d.ts +8 -8
  141. package/dist/api/ContextProvider.d.ts.map +1 -1
  142. package/dist/api/ContextProvider.js +6 -6
  143. package/dist/api/codec.d.ts +1 -1
  144. package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
  145. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  146. package/dist/api/internal/RequestContextMiddleware.js +2 -2
  147. package/dist/api/internal/auth.d.ts +45 -7
  148. package/dist/api/internal/auth.d.ts.map +1 -1
  149. package/dist/api/internal/auth.js +160 -29
  150. package/dist/api/internal/events.d.ts +3 -3
  151. package/dist/api/internal/events.d.ts.map +1 -1
  152. package/dist/api/internal/events.js +12 -8
  153. package/dist/api/internal/health.d.ts +1 -1
  154. package/dist/api/layerUtils.d.ts +6 -6
  155. package/dist/api/layerUtils.d.ts.map +1 -1
  156. package/dist/api/layerUtils.js +5 -5
  157. package/dist/api/middlewares.d.ts +1 -1
  158. package/dist/api/reportError.d.ts +1 -1
  159. package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
  160. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  161. package/dist/api/routing/middleware/middleware.d.ts +50 -4
  162. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  163. package/dist/api/routing/middleware/middleware.js +79 -17
  164. package/dist/api/routing/middleware.d.ts +1 -2
  165. package/dist/api/routing/middleware.d.ts.map +1 -1
  166. package/dist/api/routing/middleware.js +1 -2
  167. package/dist/api/routing/schema/jwt.d.ts +2 -2
  168. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  169. package/dist/api/routing/schema/jwt.js +1 -1
  170. package/dist/api/routing/tsort.d.ts +1 -1
  171. package/dist/api/routing/tsort.d.ts.map +1 -1
  172. package/dist/api/routing/utils.d.ts +3 -3
  173. package/dist/api/routing/utils.d.ts.map +1 -1
  174. package/dist/api/routing.d.ts +32 -35
  175. package/dist/api/routing.d.ts.map +1 -1
  176. package/dist/api/routing.js +84 -36
  177. package/dist/api/setupRequest.d.ts +8 -5
  178. package/dist/api/setupRequest.d.ts.map +1 -1
  179. package/dist/api/setupRequest.js +14 -9
  180. package/dist/api/util.d.ts +1 -1
  181. package/dist/arbs.d.ts +1 -1
  182. package/dist/arbs.d.ts.map +1 -1
  183. package/dist/arbs.js +5 -3
  184. package/dist/errorReporter.d.ts +5 -5
  185. package/dist/errorReporter.d.ts.map +1 -1
  186. package/dist/errorReporter.js +20 -25
  187. package/dist/errors.d.ts +1 -1
  188. package/dist/fileUtil.d.ts +1 -1
  189. package/dist/fileUtil.d.ts.map +1 -1
  190. package/dist/fileUtil.js +3 -2
  191. package/dist/index.d.ts +1 -1
  192. package/dist/logger/jsonLogger.d.ts +1 -1
  193. package/dist/logger/logFmtLogger.d.ts +1 -1
  194. package/dist/logger/shared.d.ts +1 -1
  195. package/dist/logger/shared.js +2 -2
  196. package/dist/logger.d.ts +1 -1
  197. package/dist/logger.d.ts.map +1 -1
  198. package/dist/rateLimit.d.ts +9 -3
  199. package/dist/rateLimit.d.ts.map +1 -1
  200. package/dist/rateLimit.js +5 -11
  201. package/dist/test.d.ts +2 -2
  202. package/dist/test.d.ts.map +1 -1
  203. package/dist/test.js +1 -1
  204. package/dist/vitest.d.ts +1 -1
  205. package/examples/query.ts +39 -35
  206. package/package.json +41 -37
  207. package/src/CUPS.ts +9 -11
  208. package/src/Emailer/Sendgrid.ts +18 -15
  209. package/src/Emailer/service.ts +9 -3
  210. package/src/MainFiberSet.ts +5 -6
  211. package/src/Model/Repository/Registry.ts +33 -0
  212. package/src/Model/Repository/ext.ts +96 -10
  213. package/src/Model/Repository/internal/internal.ts +97 -88
  214. package/src/Model/Repository/makeRepo.ts +12 -10
  215. package/src/Model/Repository/service.ts +31 -22
  216. package/src/Model/Repository/validation.ts +4 -4
  217. package/src/Model/Repository.ts +1 -0
  218. package/src/Model/dsl.ts +3 -3
  219. package/src/Model/filter/types/path/eager.ts +1 -2
  220. package/src/Model/query/dsl.ts +18 -18
  221. package/src/Model/query/new-kid-interpreter.ts +2 -2
  222. package/src/Model.ts +1 -0
  223. package/src/QueueMaker/SQLQueue.ts +123 -154
  224. package/src/QueueMaker/memQueue.ts +85 -107
  225. package/src/QueueMaker/sbqueue.ts +54 -81
  226. package/src/RequestContext.ts +8 -10
  227. package/src/RequestFiberSet.ts +4 -4
  228. package/src/Store/ContextMapContainer.ts +41 -2
  229. package/src/Store/Cosmos/query.ts +16 -20
  230. package/src/Store/Cosmos.ts +452 -342
  231. package/src/Store/Disk.ts +52 -49
  232. package/src/Store/Memory.ts +55 -51
  233. package/src/Store/SQL/Pg.ts +318 -0
  234. package/src/Store/SQL/query.ts +409 -0
  235. package/src/Store/SQL.ts +668 -0
  236. package/src/Store/codeFilter.ts +1 -0
  237. package/src/Store/index.ts +17 -2
  238. package/src/Store/service.ts +32 -8
  239. package/src/Store/utils.ts +23 -22
  240. package/src/adapters/SQL/Model.ts +83 -72
  241. package/src/adapters/ServiceBus.ts +114 -118
  242. package/src/adapters/cosmos-client.ts +2 -2
  243. package/src/adapters/index.ts +7 -0
  244. package/src/adapters/memQueue.ts +2 -2
  245. package/src/adapters/mongo-client.ts +2 -2
  246. package/src/adapters/redis-client.ts +2 -2
  247. package/src/api/ContextProvider.ts +12 -13
  248. package/src/api/internal/RequestContextMiddleware.ts +1 -1
  249. package/src/api/internal/auth.ts +246 -44
  250. package/src/api/internal/events.ts +15 -10
  251. package/src/api/layerUtils.ts +8 -8
  252. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  253. package/src/api/routing/middleware/middleware.ts +112 -15
  254. package/src/api/routing/middleware.ts +0 -2
  255. package/src/api/routing/schema/jwt.ts +2 -3
  256. package/src/api/routing.ts +153 -79
  257. package/src/api/setupRequest.ts +30 -10
  258. package/src/arbs.ts +4 -2
  259. package/src/errorReporter.ts +63 -75
  260. package/src/fileUtil.ts +2 -1
  261. package/src/logger/shared.ts +1 -1
  262. package/src/rateLimit.ts +30 -22
  263. package/src/test.ts +1 -1
  264. package/test/auth.test.ts +101 -0
  265. package/test/contextProvider.test.ts +11 -11
  266. package/test/controller.test.ts +27 -21
  267. package/test/dist/auth.test.d.ts.map +1 -0
  268. package/test/dist/contextProvider.test.d.ts.map +1 -1
  269. package/test/dist/controller.test.d.ts.map +1 -1
  270. package/test/dist/fixtures.d.ts +26 -12
  271. package/test/dist/fixtures.d.ts.map +1 -1
  272. package/test/dist/fixtures.js +12 -10
  273. package/test/dist/query.test.d.ts.map +1 -1
  274. package/test/dist/rawQuery.test.d.ts.map +1 -1
  275. package/test/dist/repository-ext.test.d.ts.map +1 -0
  276. package/test/dist/requires.test.d.ts.map +1 -1
  277. package/test/dist/router-generator.test.d.ts.map +1 -0
  278. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  279. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  280. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  281. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  282. package/test/dist/sql-store.test.d.ts.map +1 -0
  283. package/test/fixtures.ts +11 -9
  284. package/test/query.test.ts +216 -36
  285. package/test/rawQuery.test.ts +23 -19
  286. package/test/repository-ext.test.ts +60 -0
  287. package/test/requires.test.ts +6 -6
  288. package/test/router-generator.test.ts +180 -0
  289. package/test/routing-interruptibility.test.ts +63 -0
  290. package/test/rpc-e2e-invalidation.test.ts +507 -0
  291. package/test/rpc-multi-middleware.test.ts +79 -10
  292. package/test/rpc-stream-fullstack.test.ts +325 -0
  293. package/test/sql-store.test.ts +1064 -0
  294. package/test/validateSample.test.ts +15 -12
  295. package/tsconfig.examples.json +1 -1
  296. package/tsconfig.json +0 -1
  297. package/tsconfig.json.bak +2 -2
  298. package/tsconfig.src.json +35 -35
  299. package/tsconfig.test.json +2 -2
  300. package/dist/Operations.d.ts +0 -55
  301. package/dist/Operations.d.ts.map +0 -1
  302. package/dist/Operations.js +0 -102
  303. package/dist/OperationsRepo.d.ts +0 -41
  304. package/dist/OperationsRepo.d.ts.map +0 -1
  305. package/dist/OperationsRepo.js +0 -14
  306. package/eslint.config.mjs +0 -24
  307. package/src/Operations.ts +0 -235
  308. package/src/OperationsRepo.ts +0 -16
@@ -2,25 +2,34 @@
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
3
  /* eslint-disable @typescript-eslint/no-empty-object-type */
4
4
  /* eslint-disable @typescript-eslint/no-explicit-any */
5
- import { Config, Effect, Layer, type NonEmptyReadonlyArray, Predicate, S, type Scope } from "effect-app"
5
+ import { Config, Effect, Layer, type NonEmptyReadonlyArray, Predicate, Ref, S, type Scope, Stream } from "effect-app"
6
+ import { getMeta } from "effect-app/client"
6
7
  import { type HttpHeaders } from "effect-app/http"
8
+ import { Invalidation } from "effect-app/rpc"
7
9
  import { type GetEffectContext, type GetEffectError, type RpcContextMap } from "effect-app/rpc/RpcContextMap"
8
10
  import { type TypeTestId } from "effect-app/TypeTest"
9
11
  import { typedKeysOf, typedValuesOf } from "effect-app/utils"
10
12
  import { type Yieldable } from "effect/Effect"
11
13
  import { Rpc, RpcGroup, type RpcSerialization, RpcServer } from "effect/unstable/rpc"
12
14
  import { type LayerUtils } from "./layerUtils.js"
13
- import { type RouterMiddleware } from "./routing/middleware.js"
15
+ import { RequestType as RequestTypeAnnotation, type RouterMiddleware } from "./routing/middleware.js"
14
16
 
15
17
  export * from "./routing/middleware.js"
16
18
 
19
+ export const applyRequestTypeInterruptibility = <A, E, R>(
20
+ requestType: "command" | "query",
21
+ effect: Effect.Effect<A, E, R>
22
+ ) => requestType === "command" ? Rpc.uninterruptible(effect) : effect
23
+
17
24
  // it's the result of extending S.Req setting success, config
18
25
  // it's a schema plus some metadata
19
- export type AnyRequestModule = S.Any & {
26
+ export type AnyRequestModule = S.Top & {
20
27
  _tag: string // unique identifier for the request module
28
+ type: "command" | "query"
29
+ stream: boolean
21
30
  config: any // ?
22
- success: S.Any // validates the success response
23
- error: S.Any // validates the failure response
31
+ success: S.Top // validates the success response
32
+ error: S.Top // validates the failure response
24
33
  }
25
34
 
26
35
  // builder pattern for adding actions to a router until all actions are added
@@ -53,10 +62,10 @@ namespace RequestTypes {
53
62
  }
54
63
  type RequestType = typeof RequestTypes[keyof typeof RequestTypes]
55
64
 
56
- type GetSuccess<T> = T extends { success: S.Any } ? T["success"] : typeof S.Void
57
- type GetFailure<T extends { error?: S.Any }> = T["error"] extends never ? typeof S.Never : T["error"]
65
+ type GetSuccess<T> = T extends { success: S.Top } ? T["success"] : typeof S.Void
66
+ type GetFailure<T extends { error?: S.Top }> = T["error"] extends never ? typeof S.Never : T["error"]
58
67
 
59
- type GetSuccessShape<Action extends { success?: S.Any }, RT extends RequestType> = {
68
+ type GetSuccessShape<Action extends { success?: S.Top }, RT extends RequestType> = {
60
69
  d: S.Schema.Type<GetSuccess<Action>>
61
70
  raw: S.Codec.Encoded<GetSuccess<Action>>
62
71
  }[RT]
@@ -65,7 +74,10 @@ interface HandlerBase<Action extends AnyRequestModule, RT extends RequestType, A
65
74
  new(): {}
66
75
  _tag: RT
67
76
  stack: string
68
- handler: (req: S.Schema.Type<Action>, headers: HttpHeaders.Headers) => Effect.Effect<A, E, R>
77
+ handler: (
78
+ req: S.Schema.Type<Action>,
79
+ headers: HttpHeaders.Headers
80
+ ) => Effect.Effect<A, E, R> | Stream.Stream<A, E, R>
69
81
  }
70
82
 
71
83
  export interface Handler<Action extends AnyRequestModule, RT extends RequestType, R> extends
@@ -106,7 +118,7 @@ type Match<
106
118
  > = {
107
119
  // note: the defaults of = never prevent the whole router to error (??)
108
120
  <A extends GetSuccessShape<Resource[Key], RT>, R2 = never, E = never>(
109
- f: Effect.Effect<A, E, R2>
121
+ f: (req: S.Schema.Type<Resource[Key]>) => Effect.Effect<A, E, R2>
110
122
  ): Handler<
111
123
  Resource[Key],
112
124
  RT,
@@ -117,7 +129,7 @@ type Match<
117
129
  >
118
130
 
119
131
  <A extends GetSuccessShape<Resource[Key], RT>, R2 = never, E = never>(
120
- f: (req: S.Schema.Type<Resource[Key]>) => Effect.Effect<A, E, R2>
132
+ f: (req: S.Schema.Type<Resource[Key]>) => Stream.Stream<A, E, R2>
121
133
  ): Handler<
122
134
  Resource[Key],
123
135
  RT,
@@ -141,7 +153,7 @@ export type RouteMatcher<
141
153
  & {
142
154
  success: Resource[Key]["success"]
143
155
  successRaw: S.Codec<S.Codec.Encoded<Resource[Key]["success"]>>
144
- error: Resource[Key]["failure"]
156
+ error: Resource[Key]["error"]
145
157
  /**
146
158
  * Requires the Encoded shape (e.g directly undecoded from DB, so that we don't do multiple Decode/Encode)
147
159
  */
@@ -182,10 +194,9 @@ export const makeRouter = <
182
194
  * if `check` is provided, the router will only be created if the effect succeeds with true
183
195
  */
184
196
  function matchFor<
185
- const ModuleName extends string,
186
197
  const Resource extends Record<string, any>
187
198
  >(
188
- rsc: Resource & { meta: { moduleName: ModuleName } },
199
+ rsc: Resource,
189
200
  options?: { check?: Effect.Effect<boolean> }
190
201
  ) {
191
202
  type HandlerWithInputGen<
@@ -194,7 +205,8 @@ export const makeRouter = <
194
205
  > = (
195
206
  req: S.Schema.Type<Action>
196
207
  ) => Generator<
197
- Effect.Effect<
208
+ Yieldable<
209
+ any,
198
210
  any,
199
211
  S.Schema.Type<GetFailure<Action>> | S.SchemaError,
200
212
  // the actual implementation of the handler may just require the dynamic context provided by the middleware
@@ -218,37 +230,37 @@ export const makeRouter = <
218
230
  GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
219
231
  >
220
232
 
221
- type HandlerEff<
233
+ type HandlerWithInputStream<
222
234
  Action extends AnyRequestModule,
223
235
  RT extends RequestType
224
- > = Effect.Effect<
236
+ > = (
237
+ req: S.Schema.Type<Action>
238
+ ) => Stream.Stream<
225
239
  GetSuccessShape<Action, RT>,
226
240
  S.Schema.Type<GetFailure<Action>> | S.SchemaError,
227
- // the actual implementation of the handler may just require the dynamic context provided by the middleware
228
- // and the per request context provided by the context provider
229
241
  GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
230
242
  >
231
243
 
232
244
  type Handlers<Action extends AnyRequestModule, RT extends RequestType> =
233
245
  | HandlerWithInputGen<Action, RT>
234
246
  | HandlerWithInputEff<Action, RT>
235
- | HandlerEff<Action, RT>
247
+ | HandlerWithInputStream<Action, RT>
236
248
 
237
249
  type HandlersDecoded<Action extends AnyRequestModule> = Handlers<Action, RequestTypes.DECODED>
238
250
 
239
251
  type HandlersRaw<Action extends AnyRequestModule> =
240
252
  | { raw: HandlerWithInputGen<Action, RequestTypes.RAW> }
241
253
  | { raw: HandlerWithInputEff<Action, RequestTypes.RAW> }
242
- | { raw: HandlerEff<Action, RequestTypes.RAW> }
254
+ | { raw: HandlerWithInputStream<Action, RequestTypes.RAW> }
243
255
 
244
256
  type AnyHandlers<Action extends AnyRequestModule> = HandlersRaw<Action> | HandlersDecoded<Action>
245
257
 
246
- const { meta } = rsc
258
+ const meta = getMeta(rsc)
247
259
 
248
260
  type RequestModules = FilterRequestModules<Resource>
249
261
  const requestModules = typedKeysOf(rsc).reduce((acc, cur) => {
250
- if (Predicate.isObject(rsc[cur]) && rsc[cur]["success"]) {
251
- acc[cur as keyof RequestModules] = rsc[cur] as RequestModules[keyof RequestModules]
262
+ if (Predicate.isObjectKeyword(rsc[cur]) && rsc[cur]["success"]) {
263
+ acc[cur as keyof RequestModules] = rsc[cur]
252
264
  }
253
265
  return acc
254
266
  }, {} as RequestModules)
@@ -259,19 +271,13 @@ export const makeRouter = <
259
271
  // handlerImpl is the actual handler implementation
260
272
  if (handlerImpl[Symbol.toStringTag] === "GeneratorFunction") handlerImpl = Effect.fnUntraced(handlerImpl)
261
273
  const stack = new Error().stack?.split("\n").slice(2).join("\n")
262
- return Effect.isEffect(handlerImpl)
263
- ? class {
264
- static request = rsc[cur]
265
- static stack = stack
266
- static _tag = RequestTypes.DECODED
267
- static handler = () => handlerImpl
268
- }
269
- : class {
270
- static request = rsc[cur]
271
- static stack = stack
272
- static _tag = RequestTypes.DECODED
273
- static handler = handlerImpl
274
- }
274
+ // oxlint-disable-next-line typescript/no-extraneous-class
275
+ return class {
276
+ static request = rsc[cur]
277
+ static stack = stack
278
+ static _tag = RequestTypes.DECODED
279
+ static handler = handlerImpl
280
+ }
275
281
  }, {
276
282
  success: rsc[cur].success,
277
283
  successRaw: S.toEncoded(rsc[cur].success),
@@ -282,19 +288,13 @@ export const makeRouter = <
282
288
  (handlerImpl: any) => {
283
289
  if (handlerImpl[Symbol.toStringTag] === "GeneratorFunction") handlerImpl = Effect.fnUntraced(handlerImpl)
284
290
  const stack = new Error().stack?.split("\n").slice(2).join("\n")
285
- return Effect.isEffect(handlerImpl)
286
- ? class {
287
- static request = rsc[cur]
288
- static stack = stack
289
- static _tag = RequestTypes.RAW
290
- static handler = () => handlerImpl
291
- }
292
- : class {
293
- static request = rsc[cur]
294
- static stack = stack
295
- static _tag = RequestTypes.RAW
296
- static handler = handlerImpl
297
- }
291
+ // oxlint-disable-next-line typescript/no-extraneous-class
292
+ return class {
293
+ static request = rsc[cur]
294
+ static stack = stack
295
+ static _tag = RequestTypes.RAW
296
+ static handler = handlerImpl
297
+ }
298
298
  }
299
299
  })
300
300
  return prev
@@ -317,19 +317,15 @@ export const makeRouter = <
317
317
  // retrieves context R from the actual implementation of the handler
318
318
  Impl[K] extends { raw: any }
319
319
  ? Impl[K]["raw"] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
320
- : Impl[K]["raw"] extends Effect.Effect<any, any, infer R> ? R
320
+ : Impl[K]["raw"] extends (...args: any[]) => Stream.Stream<any, any, infer R> ? R
321
321
  : Impl[K]["raw"] extends (...args: any[]) => Generator<
322
- Effect.Effect<any, any, infer R>,
323
- any,
324
- any
322
+ Yieldable<any, any, any, infer R>
325
323
  > ? R
326
324
  : never
327
325
  : Impl[K] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
328
- : Impl[K] extends Effect.Effect<any, any, infer R> ? R
326
+ : Impl[K] extends (...args: any[]) => Stream.Stream<any, any, infer R> ? R
329
327
  : Impl[K] extends (...args: any[]) => Generator<
330
- Effect.Effect<any, any, infer R>,
331
- any,
332
- any
328
+ Yieldable<any, any, any, infer R>
333
329
  > ? R
334
330
  : never,
335
331
  | GetEffectContext<RequestContextMap, Resource[K]["config"]>
@@ -358,7 +354,7 @@ export const makeRouter = <
358
354
  match: any
359
355
  ) =>
360
356
  | Effect.Effect<THandlers, MakeE, MakeR>
361
- | Generator<Yieldable<any, any, MakeE, MakeR>, THandlers, any>
357
+ | Generator<Yieldable<any, any, MakeE, MakeR>, THandlers>
362
358
  ) => {
363
359
  const dependenciesL = (dependencies ? Layer.mergeAll(...dependencies as any) : Layer.empty) as Layer.Layer<
364
360
  LayerUtils.GetLayersSuccess<MakeDependencies>,
@@ -385,12 +381,78 @@ export const makeRouter = <
385
381
  static success = S.toEncoded(resource.success)
386
382
  } as any
387
383
  : resource,
388
- (payload: any, headers: any) =>
389
- (handler.handler(payload, headers) as Effect.Effect<unknown, unknown, unknown>).pipe(
384
+ (payload: any, headers: any) => {
385
+ let result: any = handler.handler(payload, headers)
386
+ // Stream resources accept handlers returning either Stream or Effect.
387
+ // Lift Effect to Stream so `Effect.fail(...)` and `Effect<Stream>` (e.g.
388
+ // from generator handlers) work the same as a returned Stream.
389
+ if (resource.stream && Effect.isEffect(result)) {
390
+ result = Stream.unwrap(
391
+ (result as Effect.Effect<unknown, unknown, unknown>).pipe(
392
+ Effect.map((v) => Stream.isStream(v) ? v : Stream.succeed(v))
393
+ )
394
+ )
395
+ }
396
+ if (Stream.isStream(result)) {
397
+ // Wrap stream items as { _tag: "value", value } and append a final
398
+ // { _tag: "done", metadata } chunk carrying accumulated invalidation keys.
399
+ // V2: on failure, convert to { _tag: "error", error, metadata } chunk so
400
+ // clients can invalidate queries even when the stream fails.
401
+ const keysRef = Ref.makeUnsafe<ReadonlyArray<Invalidation.InvalidationKey>>([])
402
+ const invalidationSet = Invalidation.makeInvalidationSet(keysRef)
403
+ return Stream.concat(
404
+ (result as Stream.Stream<any, any, any>).pipe(
405
+ Stream.map((item: any) => ({ _tag: "value" as const, value: item })),
406
+ Stream.provideService(Invalidation.InvalidationSet, invalidationSet),
407
+ // V3: after each value chunk, drain accumulated keys and emit a "metadata"
408
+ // chunk if any keys were collected since the last drain. This lets clients
409
+ // invalidate queries mid-stream without waiting for the "done" chunk.
410
+ Stream.flatMap((valueChunk: any) =>
411
+ Stream
412
+ .fromEffect(
413
+ Ref.getAndSet(keysRef, []).pipe(
414
+ Effect.map((keys) =>
415
+ keys.length > 0
416
+ ? [
417
+ valueChunk,
418
+ { _tag: "metadata" as const, metadata: { invalidateQueries: keys } }
419
+ ]
420
+ : [valueChunk]
421
+ )
422
+ )
423
+ )
424
+ .pipe(Stream.flatMap(Stream.fromIterable))
425
+ ),
426
+ // V2: catch stream failures and embed them in the stream as an error chunk
427
+ Stream.catch((err: any) =>
428
+ Stream.fromEffect(
429
+ Ref.get(keysRef).pipe(
430
+ Effect.flatMap((keys) =>
431
+ Effect.fail({
432
+ _tag: "error" as const,
433
+ error: err,
434
+ metadata: { invalidateQueries: keys }
435
+ })
436
+ )
437
+ )
438
+ )
439
+ )
440
+ ),
441
+ Stream.fromEffect(
442
+ Ref.get(keysRef).pipe(
443
+ Effect.map((keys) => ({ _tag: "done" as const, metadata: { invalidateQueries: keys } }))
444
+ )
445
+ )
446
+ )
447
+ }
448
+ const effect = (result as Effect.Effect<unknown, unknown, unknown>).pipe(
390
449
  Effect.withSpan(`Request.${meta.moduleName}.${resource._tag}`, {}, {
391
450
  captureStackTrace: () => handler.stack // capturing the handler stack is the main reason why we are doing the span here
392
451
  })
393
452
  )
453
+
454
+ return applyRequestTypeInterruptibility(resource.type, effect)
455
+ }
394
456
  ] as const
395
457
  return acc
396
458
  }, {} as any) as {
@@ -414,9 +476,29 @@ export const makeRouter = <
414
476
  const rpcs = RpcGroup
415
477
  .make(
416
478
  ...typedValuesOf(mapped).map(([resource]) => {
417
- return Rpc
418
- .make(resource._tag, { payload: resource, success: resource.success, error: resource.failure })
479
+ const isStream = resource.stream
480
+ const isCommand = resource.type === "command"
481
+ return (isCommand
482
+ ? isStream
483
+ ? Invalidation.makeStreamRpc(resource._tag, {
484
+ payload: resource,
485
+ success: resource.success,
486
+ error: resource.error,
487
+ stream: true as const
488
+ })
489
+ : Invalidation.makeCommandRpc(resource._tag, {
490
+ payload: resource,
491
+ success: resource.success,
492
+ error: resource.error
493
+ })
494
+ : Rpc.make(resource._tag, {
495
+ payload: resource,
496
+ success: resource.success,
497
+ error: resource.error,
498
+ stream: isStream
499
+ }))
419
500
  .annotate(middleware.requestContext, resource.config ?? {})
501
+ .annotate(RequestTypeAnnotation, resource.type)
420
502
  })
421
503
  )
422
504
  .prefix(`${meta.moduleName}.`)
@@ -478,8 +560,7 @@ export const makeRouter = <
478
560
  any,
479
561
  any
480
562
  >,
481
- { [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> },
482
- any
563
+ { [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> }
483
564
  >
484
565
  /** @deprecated */
485
566
  readonly ಠ_ಠ: never
@@ -509,15 +590,8 @@ export const makeRouter = <
509
590
  dependencies?: ReadonlyArray<Layer.Any>
510
591
  // v4: generators yield Yieldable with asEffect()
511
592
  effect: (match: typeof router3) => Generator<
512
- {
513
- asEffect(): Effect.Effect<
514
- any,
515
- any,
516
- any
517
- >
518
- },
519
- { [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> },
520
- any
593
+ Yieldable<any, any, any, any>,
594
+ { [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> }
521
595
  >
522
596
  }
523
597
  >(
@@ -577,23 +651,23 @@ export type MakeErrors<Make> = /*Make extends { readonly effect: (_: any) => Eff
577
651
  : Make extends { readonly effect: (_: any) => Effect.Effect<any, never, any> } ? never
578
652
  : */
579
653
  // v4: generators yield Yieldable with asEffect()
580
- Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, never, any>, any, any> } ? never
581
- : Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, infer E, any>, any, any> } ? E
654
+ Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, never, any>> } ? never
655
+ : Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, infer E, any>> } ? E
582
656
  : never
583
657
 
584
658
  export type MakeContext<Make> = /*Make extends { readonly effect: (_: any) => Effect.Effect<any, any, infer R> } ? R
585
659
  : Make extends { readonly effect: (_: any) => Effect.Effect<any, any, never> } ? never
586
660
  : */
587
661
  // v4: generators yield Yieldable with asEffect()
588
- Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any, never>, any, any> } ? never
589
- : Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any, infer R>, any, any> } ? R
662
+ Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any>> } ? never
663
+ : Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any, infer R>> } ? R
590
664
  : never
591
665
 
592
666
  export type MakeHandlers<Make, _Handlers extends Record<string, any>> = /*Make extends
593
667
  { readonly effect: (_: any) => Effect.Effect<{ [K in keyof Handlers]: AnyHandler<Handlers[K]> }, any, any> }
594
668
  ? Effect.Success<ReturnType<Make["effect"]>>
595
669
  : */
596
- Make extends { readonly effect: (_: any) => Generator<any, infer S, any> } ? S
670
+ Make extends { readonly effect: (_: any) => Generator<any, infer S> } ? S
597
671
  : never
598
672
 
599
673
  export type MakeDepsE<Make> = Layer.Error<MakeDeps<Make>>
@@ -1,9 +1,18 @@
1
- import { Effect, Layer, Tracer } from "effect-app"
1
+ import { Effect, Layer, Option, Tracer } from "effect-app"
2
2
  import { NonEmptyString255 } from "effect-app/Schema"
3
+ import { SqlClient } from "effect/unstable/sql"
3
4
  import { LocaleRef, RequestContext, spanAttributes } from "../RequestContext.js"
4
5
  import { ContextMapContainer } from "../Store/ContextMapContainer.js"
5
6
  import { storeId } from "../Store/Memory.js"
6
7
 
8
+ const withSqlTransaction = <R, E, A>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
9
+ Effect.serviceOption(SqlClient.SqlClient).pipe(
10
+ Effect.flatMap(Option.match({
11
+ onNone: () => self,
12
+ onSome: (sql) => sql.withTransaction(self).pipe(Effect.orDie)
13
+ }))
14
+ )
15
+
7
16
  export const getRequestContext = Effect
8
17
  .all({
9
18
  span: Effect.currentSpan.pipe(Effect.orDie),
@@ -12,7 +21,7 @@ export const getRequestContext = Effect
12
21
  })
13
22
  .pipe(
14
23
  Effect.map(({ locale, namespace, span }) =>
15
- new RequestContext({
24
+ RequestContext.make({
16
25
  span: Tracer.externalSpan(span),
17
26
  locale,
18
27
  namespace,
@@ -43,25 +52,35 @@ const withRequestSpan = (name = "request", options?: Tracer.SpanOptions) => <R,
43
52
  )
44
53
  )
45
54
 
55
+ export interface SetupRequestOptions {
56
+ readonly withTransaction?: boolean
57
+ }
58
+
46
59
  export const setupRequestContextFromCurrent =
47
- (name = "request", options?: Tracer.SpanOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
60
+ (name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
48
61
  self
49
62
  .pipe(
63
+ options?.withTransaction === true ? withSqlTransaction : (_) => _,
50
64
  withRequestSpan(name, options),
51
- Effect.provide(ContextMapContainer.layer)
65
+ Effect.provide(ContextMapContainer.layer, { local: true })
52
66
  )
53
67
 
54
68
  // TODO: consider integrating Effect.withParentSpan
55
- export function setupRequestContext<R, E, A>(self: Effect.Effect<A, E, R>, requestContext: RequestContext) {
69
+ export function setupRequestContext<R, E, A>(
70
+ self: Effect.Effect<A, E, R>,
71
+ requestContext: RequestContext,
72
+ options?: SetupRequestOptions
73
+ ) {
56
74
  const layer = Layer.mergeAll(
57
75
  ContextMapContainer.layer,
58
76
  Layer.succeed(LocaleRef, requestContext.locale),
59
- Layer.succeed(storeId, NonEmptyString255(requestContext.namespace))
77
+ Layer.succeed(storeId, requestContext.namespace)
60
78
  )
61
79
  return self
62
80
  .pipe(
81
+ options?.withTransaction === true ? withSqlTransaction : (_) => _,
63
82
  withRequestSpan(requestContext.name),
64
- Effect.provide(layer)
83
+ Effect.provide(layer, { local: true })
65
84
  )
66
85
  }
67
86
 
@@ -69,16 +88,17 @@ export function setupRequestContextWithCustomSpan<R, E, A>(
69
88
  self: Effect.Effect<A, E, R>,
70
89
  requestContext: RequestContext,
71
90
  name: string,
72
- options?: Tracer.SpanOptions
91
+ options?: Tracer.SpanOptions & SetupRequestOptions
73
92
  ) {
74
93
  const layer = Layer.mergeAll(
75
94
  ContextMapContainer.layer,
76
95
  Layer.succeed(LocaleRef, requestContext.locale),
77
- Layer.succeed(storeId, NonEmptyString255(requestContext.namespace))
96
+ Layer.succeed(storeId, requestContext.namespace)
78
97
  )
79
98
  return self
80
99
  .pipe(
100
+ options?.withTransaction === true ? withSqlTransaction : (_) => _,
81
101
  withRequestSpan(name, options),
82
- Effect.provide(layer)
102
+ Effect.provide(layer, { local: true })
83
103
  )
84
104
  }
package/src/arbs.ts CHANGED
@@ -5,9 +5,11 @@ import { type S } from "effect-app"
5
5
  import { setFaker } from "effect-app/faker"
6
6
  import * as FastCheck from "effect/testing/FastCheck"
7
7
  import { Random } from "fast-check"
8
- import * as rand from "pure-rand"
8
+ import { congruential32 } from "pure-rand/generator/congruential32"
9
9
 
10
- const rnd = new Random(rand.congruential32(5))
10
+ const seed = 5
11
+ const rng = congruential32(seed)
12
+ const rnd = new Random(rng)
11
13
 
12
14
  setFaker(faker)
13
15