@effect-app/infra 4.0.0-beta.19 → 4.0.0-beta.191

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 (305) hide show
  1. package/CHANGELOG.md +1285 -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 +10 -4
  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 +16 -16
  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 +5 -7
  62. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  63. package/dist/QueueMaker/SQLQueue.js +105 -114
  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 +51 -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 +37 -53
  72. package/dist/QueueMaker/service.d.ts +1 -1
  73. package/dist/RequestContext.d.ts +114 -26
  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 +19 -3
  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 +308 -242
  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 +28 -42
  117. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  118. package/dist/adapters/SQL/Model.js +2 -2
  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 +13 -15
  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 +3 -3
  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 +44 -6
  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 +9 -7
  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 +75 -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 +1 -1
  168. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  169. package/dist/api/routing/tsort.d.ts +1 -1
  170. package/dist/api/routing/tsort.d.ts.map +1 -1
  171. package/dist/api/routing/utils.d.ts +3 -3
  172. package/dist/api/routing/utils.d.ts.map +1 -1
  173. package/dist/api/routing.d.ts +31 -23
  174. package/dist/api/routing.d.ts.map +1 -1
  175. package/dist/api/routing.js +70 -11
  176. package/dist/api/setupRequest.d.ts +8 -5
  177. package/dist/api/setupRequest.d.ts.map +1 -1
  178. package/dist/api/setupRequest.js +12 -7
  179. package/dist/api/util.d.ts +1 -1
  180. package/dist/arbs.d.ts +1 -1
  181. package/dist/arbs.d.ts.map +1 -1
  182. package/dist/arbs.js +5 -3
  183. package/dist/errorReporter.d.ts +4 -4
  184. package/dist/errorReporter.d.ts.map +1 -1
  185. package/dist/errorReporter.js +20 -25
  186. package/dist/errors.d.ts +1 -1
  187. package/dist/fileUtil.d.ts +1 -1
  188. package/dist/fileUtil.d.ts.map +1 -1
  189. package/dist/index.d.ts +1 -1
  190. package/dist/logger/jsonLogger.d.ts +1 -1
  191. package/dist/logger/logFmtLogger.d.ts +1 -1
  192. package/dist/logger/shared.d.ts +1 -1
  193. package/dist/logger/shared.js +2 -2
  194. package/dist/logger.d.ts +1 -1
  195. package/dist/logger.d.ts.map +1 -1
  196. package/dist/rateLimit.d.ts +9 -3
  197. package/dist/rateLimit.d.ts.map +1 -1
  198. package/dist/rateLimit.js +5 -11
  199. package/dist/test.d.ts +2 -2
  200. package/dist/test.d.ts.map +1 -1
  201. package/dist/test.js +1 -1
  202. package/dist/vitest.d.ts +1 -1
  203. package/examples/query.ts +39 -35
  204. package/package.json +41 -37
  205. package/src/CUPS.ts +9 -11
  206. package/src/Emailer/Sendgrid.ts +17 -14
  207. package/src/Emailer/service.ts +9 -3
  208. package/src/MainFiberSet.ts +5 -6
  209. package/src/Model/Repository/Registry.ts +33 -0
  210. package/src/Model/Repository/ext.ts +96 -10
  211. package/src/Model/Repository/internal/internal.ts +97 -88
  212. package/src/Model/Repository/makeRepo.ts +12 -10
  213. package/src/Model/Repository/service.ts +31 -22
  214. package/src/Model/Repository/validation.ts +4 -4
  215. package/src/Model/Repository.ts +1 -0
  216. package/src/Model/dsl.ts +3 -3
  217. package/src/Model/filter/types/path/eager.ts +1 -2
  218. package/src/Model/query/dsl.ts +18 -18
  219. package/src/Model/query/new-kid-interpreter.ts +2 -2
  220. package/src/Model.ts +1 -0
  221. package/src/QueueMaker/SQLQueue.ts +121 -151
  222. package/src/QueueMaker/memQueue.ts +82 -103
  223. package/src/QueueMaker/sbqueue.ts +56 -86
  224. package/src/RequestContext.ts +7 -9
  225. package/src/RequestFiberSet.ts +4 -4
  226. package/src/Store/ContextMapContainer.ts +41 -2
  227. package/src/Store/Cosmos/query.ts +16 -20
  228. package/src/Store/Cosmos.ts +437 -343
  229. package/src/Store/Disk.ts +52 -49
  230. package/src/Store/Memory.ts +54 -48
  231. package/src/Store/SQL/Pg.ts +318 -0
  232. package/src/Store/SQL/query.ts +409 -0
  233. package/src/Store/SQL.ts +668 -0
  234. package/src/Store/codeFilter.ts +1 -0
  235. package/src/Store/index.ts +17 -2
  236. package/src/Store/service.ts +32 -8
  237. package/src/Store/utils.ts +23 -22
  238. package/src/adapters/SQL/Model.ts +10 -4
  239. package/src/adapters/ServiceBus.ts +112 -116
  240. package/src/adapters/cosmos-client.ts +2 -2
  241. package/src/adapters/index.ts +7 -0
  242. package/src/adapters/memQueue.ts +2 -2
  243. package/src/adapters/mongo-client.ts +2 -2
  244. package/src/adapters/redis-client.ts +2 -2
  245. package/src/api/ContextProvider.ts +12 -13
  246. package/src/api/internal/RequestContextMiddleware.ts +1 -1
  247. package/src/api/internal/auth.ts +246 -44
  248. package/src/api/internal/events.ts +12 -8
  249. package/src/api/layerUtils.ts +8 -8
  250. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  251. package/src/api/routing/middleware/middleware.ts +109 -15
  252. package/src/api/routing/middleware.ts +0 -2
  253. package/src/api/routing.ts +164 -29
  254. package/src/api/setupRequest.ts +28 -8
  255. package/src/arbs.ts +4 -2
  256. package/src/errorReporter.ts +62 -74
  257. package/src/logger/shared.ts +1 -1
  258. package/src/rateLimit.ts +30 -22
  259. package/src/test.ts +1 -1
  260. package/test/auth.test.ts +101 -0
  261. package/test/contextProvider.test.ts +11 -11
  262. package/test/controller.test.ts +18 -14
  263. package/test/dist/auth.test.d.ts.map +1 -0
  264. package/test/dist/contextProvider.test.d.ts.map +1 -1
  265. package/test/dist/controller.test.d.ts.map +1 -1
  266. package/test/dist/date-query.test.d.ts.map +1 -0
  267. package/test/dist/fixtures.d.ts +26 -12
  268. package/test/dist/fixtures.d.ts.map +1 -1
  269. package/test/dist/fixtures.js +12 -10
  270. package/test/dist/query.test.d.ts.map +1 -1
  271. package/test/dist/rawQuery.test.d.ts.map +1 -1
  272. package/test/dist/repository-ext.test.d.ts.map +1 -0
  273. package/test/dist/requires.test.d.ts.map +1 -1
  274. package/test/dist/router-generator.test.d.ts.map +1 -0
  275. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  276. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  277. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  278. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  279. package/test/dist/sql-store.test.d.ts.map +1 -0
  280. package/test/fixtures.ts +11 -9
  281. package/test/query.test.ts +216 -34
  282. package/test/rawQuery.test.ts +23 -19
  283. package/test/repository-ext.test.ts +60 -0
  284. package/test/requires.test.ts +6 -6
  285. package/test/router-generator.test.ts +180 -0
  286. package/test/routing-interruptibility.test.ts +63 -0
  287. package/test/rpc-e2e-invalidation.test.ts +507 -0
  288. package/test/rpc-multi-middleware.test.ts +78 -9
  289. package/test/rpc-stream-fullstack.test.ts +181 -0
  290. package/test/sql-store.test.ts +1064 -0
  291. package/test/validateSample.test.ts +15 -12
  292. package/tsconfig.examples.json +1 -1
  293. package/tsconfig.json +0 -1
  294. package/tsconfig.json.bak +2 -2
  295. package/tsconfig.src.json +35 -35
  296. package/tsconfig.test.json +2 -2
  297. package/dist/Operations.d.ts +0 -55
  298. package/dist/Operations.d.ts.map +0 -1
  299. package/dist/Operations.js +0 -102
  300. package/dist/OperationsRepo.d.ts +0 -41
  301. package/dist/OperationsRepo.d.ts.map +0 -1
  302. package/dist/OperationsRepo.js +0 -14
  303. package/eslint.config.mjs +0 -24
  304. package/src/Operations.ts +0 -235
  305. package/src/OperationsRepo.ts +0 -16
@@ -2,22 +2,30 @@
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
26
  export type AnyRequestModule = S.Top & {
20
27
  _tag: string // unique identifier for the request module
28
+ type: "command" | "query" | "stream"
21
29
  config: any // ?
22
30
  success: S.Top // validates the success response
23
31
  error: S.Top // validates the failure response
@@ -65,7 +73,10 @@ interface HandlerBase<Action extends AnyRequestModule, RT extends RequestType, A
65
73
  new(): {}
66
74
  _tag: RT
67
75
  stack: string
68
- handler: (req: S.Schema.Type<Action>, headers: HttpHeaders.Headers) => Effect.Effect<A, E, R>
76
+ handler: (
77
+ req: S.Schema.Type<Action>,
78
+ headers: HttpHeaders.Headers
79
+ ) => Effect.Effect<A, E, R> | Stream.Stream<A, E, R>
69
80
  }
70
81
 
71
82
  export interface Handler<Action extends AnyRequestModule, RT extends RequestType, R> extends
@@ -126,6 +137,28 @@ type Match<
126
137
  Scope.Scope
127
138
  >
128
139
  >
140
+
141
+ <A extends GetSuccessShape<Resource[Key], RT>, R2 = never, E = never>(
142
+ f: Stream.Stream<A, E, R2>
143
+ ): Handler<
144
+ Resource[Key],
145
+ RT,
146
+ Exclude<
147
+ Exclude<R2, GetEffectContext<RequestContextMap, Resource[Key]["config"]>>,
148
+ Scope.Scope
149
+ >
150
+ >
151
+
152
+ <A extends GetSuccessShape<Resource[Key], RT>, R2 = never, E = never>(
153
+ f: (req: S.Schema.Type<Resource[Key]>) => Stream.Stream<A, E, R2>
154
+ ): Handler<
155
+ Resource[Key],
156
+ RT,
157
+ Exclude<
158
+ Exclude<R2, GetEffectContext<RequestContextMap, Resource[Key]["config"]>>,
159
+ Scope.Scope
160
+ >
161
+ >
129
162
  }
130
163
 
131
164
  export type RouteMatcher<
@@ -182,10 +215,9 @@ export const makeRouter = <
182
215
  * if `check` is provided, the router will only be created if the effect succeeds with true
183
216
  */
184
217
  function matchFor<
185
- const ModuleName extends string,
186
218
  const Resource extends Record<string, any>
187
219
  >(
188
- rsc: Resource & { meta: { moduleName: ModuleName } },
220
+ rsc: Resource,
189
221
  options?: { check?: Effect.Effect<boolean> }
190
222
  ) {
191
223
  type HandlerWithInputGen<
@@ -230,10 +262,32 @@ export const makeRouter = <
230
262
  GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
231
263
  >
232
264
 
265
+ type HandlerWithInputStream<
266
+ Action extends AnyRequestModule,
267
+ RT extends RequestType
268
+ > = (
269
+ req: S.Schema.Type<Action>
270
+ ) => Stream.Stream<
271
+ GetSuccessShape<Action, RT>,
272
+ S.Schema.Type<GetFailure<Action>> | S.SchemaError,
273
+ GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
274
+ >
275
+
276
+ type HandlerStream<
277
+ Action extends AnyRequestModule,
278
+ RT extends RequestType
279
+ > = Stream.Stream<
280
+ GetSuccessShape<Action, RT>,
281
+ S.Schema.Type<GetFailure<Action>> | S.SchemaError,
282
+ GetEffectContext<RequestContextMap, Action["config"]> | ContextProviderA
283
+ >
284
+
233
285
  type Handlers<Action extends AnyRequestModule, RT extends RequestType> =
234
286
  | HandlerWithInputGen<Action, RT>
235
287
  | HandlerWithInputEff<Action, RT>
236
288
  | HandlerEff<Action, RT>
289
+ | HandlerWithInputStream<Action, RT>
290
+ | HandlerStream<Action, RT>
237
291
 
238
292
  type HandlersDecoded<Action extends AnyRequestModule> = Handlers<Action, RequestTypes.DECODED>
239
293
 
@@ -241,15 +295,17 @@ export const makeRouter = <
241
295
  | { raw: HandlerWithInputGen<Action, RequestTypes.RAW> }
242
296
  | { raw: HandlerWithInputEff<Action, RequestTypes.RAW> }
243
297
  | { raw: HandlerEff<Action, RequestTypes.RAW> }
298
+ | { raw: HandlerWithInputStream<Action, RequestTypes.RAW> }
299
+ | { raw: HandlerStream<Action, RequestTypes.RAW> }
244
300
 
245
301
  type AnyHandlers<Action extends AnyRequestModule> = HandlersRaw<Action> | HandlersDecoded<Action>
246
302
 
247
- const { meta } = rsc
303
+ const meta = getMeta(rsc)
248
304
 
249
305
  type RequestModules = FilterRequestModules<Resource>
250
306
  const requestModules = typedKeysOf(rsc).reduce((acc, cur) => {
251
307
  if (Predicate.isObjectKeyword(rsc[cur]) && rsc[cur]["success"]) {
252
- acc[cur as keyof RequestModules] = rsc[cur] as RequestModules[keyof RequestModules]
308
+ acc[cur as keyof RequestModules] = rsc[cur]
253
309
  }
254
310
  return acc
255
311
  }, {} as RequestModules)
@@ -260,13 +316,16 @@ export const makeRouter = <
260
316
  // handlerImpl is the actual handler implementation
261
317
  if (handlerImpl[Symbol.toStringTag] === "GeneratorFunction") handlerImpl = Effect.fnUntraced(handlerImpl)
262
318
  const stack = new Error().stack?.split("\n").slice(2).join("\n")
263
- return Effect.isEffect(handlerImpl)
319
+ const isValueShape = Effect.isEffect(handlerImpl) || Stream.isStream(handlerImpl)
320
+ return isValueShape
321
+ // oxlint-disable-next-line typescript/no-extraneous-class
264
322
  ? class {
265
323
  static request = rsc[cur]
266
324
  static stack = stack
267
325
  static _tag = RequestTypes.DECODED
268
326
  static handler = () => handlerImpl
269
327
  }
328
+ // oxlint-disable-next-line typescript/no-extraneous-class
270
329
  : class {
271
330
  static request = rsc[cur]
272
331
  static stack = stack
@@ -283,13 +342,16 @@ export const makeRouter = <
283
342
  (handlerImpl: any) => {
284
343
  if (handlerImpl[Symbol.toStringTag] === "GeneratorFunction") handlerImpl = Effect.fnUntraced(handlerImpl)
285
344
  const stack = new Error().stack?.split("\n").slice(2).join("\n")
286
- return Effect.isEffect(handlerImpl)
345
+ const isValueShape = Effect.isEffect(handlerImpl) || Stream.isStream(handlerImpl)
346
+ return isValueShape
347
+ // oxlint-disable-next-line typescript/no-extraneous-class
287
348
  ? class {
288
349
  static request = rsc[cur]
289
350
  static stack = stack
290
351
  static _tag = RequestTypes.RAW
291
352
  static handler = () => handlerImpl
292
353
  }
354
+ // oxlint-disable-next-line typescript/no-extraneous-class
293
355
  : class {
294
356
  static request = rsc[cur]
295
357
  static stack = stack
@@ -319,18 +381,18 @@ export const makeRouter = <
319
381
  Impl[K] extends { raw: any }
320
382
  ? Impl[K]["raw"] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
321
383
  : Impl[K]["raw"] extends Effect.Effect<any, any, infer R> ? R
384
+ : Impl[K]["raw"] extends (...args: any[]) => Stream.Stream<any, any, infer R> ? R
385
+ : Impl[K]["raw"] extends Stream.Stream<any, any, infer R> ? R
322
386
  : Impl[K]["raw"] extends (...args: any[]) => Generator<
323
- Yieldable<any, any, any, infer R>,
324
- any,
325
- any
387
+ Yieldable<any, any, any, infer R>
326
388
  > ? R
327
389
  : never
328
390
  : Impl[K] extends (...args: any[]) => Effect.Effect<any, any, infer R> ? R
329
391
  : Impl[K] extends Effect.Effect<any, any, infer R> ? R
392
+ : Impl[K] extends (...args: any[]) => Stream.Stream<any, any, infer R> ? R
393
+ : Impl[K] extends Stream.Stream<any, any, infer R> ? R
330
394
  : Impl[K] extends (...args: any[]) => Generator<
331
- Yieldable<any, any, any, infer R>,
332
- any,
333
- any
395
+ Yieldable<any, any, any, infer R>
334
396
  > ? R
335
397
  : never,
336
398
  | GetEffectContext<RequestContextMap, Resource[K]["config"]>
@@ -359,7 +421,7 @@ export const makeRouter = <
359
421
  match: any
360
422
  ) =>
361
423
  | Effect.Effect<THandlers, MakeE, MakeR>
362
- | Generator<Yieldable<any, any, MakeE, MakeR>, THandlers, any>
424
+ | Generator<Yieldable<any, any, MakeE, MakeR>, THandlers>
363
425
  ) => {
364
426
  const dependenciesL = (dependencies ? Layer.mergeAll(...dependencies as any) : Layer.empty) as Layer.Layer<
365
427
  LayerUtils.GetLayersSuccess<MakeDependencies>,
@@ -386,12 +448,68 @@ export const makeRouter = <
386
448
  static success = S.toEncoded(resource.success)
387
449
  } as any
388
450
  : resource,
389
- (payload: any, headers: any) =>
390
- (handler.handler(payload, headers) as Effect.Effect<unknown, unknown, unknown>).pipe(
451
+ (payload: any, headers: any) => {
452
+ const result = handler.handler(payload, headers)
453
+ if (Stream.isStream(result)) {
454
+ // Wrap stream items as { _tag: "value", value } and append a final
455
+ // { _tag: "done", metadata } chunk carrying accumulated invalidation keys.
456
+ // V2: on failure, convert to { _tag: "error", error, metadata } chunk so
457
+ // clients can invalidate queries even when the stream fails.
458
+ const keysRef = Ref.makeUnsafe<ReadonlyArray<Invalidation.InvalidationKey>>([])
459
+ const invalidationSet = Invalidation.makeInvalidationSet(keysRef)
460
+ return Stream.concat(
461
+ (result as Stream.Stream<any, any, any>).pipe(
462
+ Stream.map((item: any) => ({ _tag: "value" as const, value: item })),
463
+ Stream.provideService(Invalidation.InvalidationSet, invalidationSet),
464
+ // V3: after each value chunk, drain accumulated keys and emit a "metadata"
465
+ // chunk if any keys were collected since the last drain. This lets clients
466
+ // invalidate queries mid-stream without waiting for the "done" chunk.
467
+ Stream.flatMap((valueChunk: any) =>
468
+ Stream
469
+ .fromEffect(
470
+ Ref.getAndSet(keysRef, []).pipe(
471
+ Effect.map((keys) =>
472
+ keys.length > 0
473
+ ? [
474
+ valueChunk,
475
+ { _tag: "metadata" as const, metadata: { invalidateQueries: keys } }
476
+ ]
477
+ : [valueChunk]
478
+ )
479
+ )
480
+ )
481
+ .pipe(Stream.flatMap(Stream.fromIterable))
482
+ ),
483
+ // V2: catch stream failures and embed them in the stream as an error chunk
484
+ Stream.catch((err: any) =>
485
+ Stream.fromEffect(
486
+ Ref.get(keysRef).pipe(
487
+ Effect.flatMap((keys) =>
488
+ Effect.fail({
489
+ _tag: "error" as const,
490
+ error: err,
491
+ metadata: { invalidateQueries: keys }
492
+ })
493
+ )
494
+ )
495
+ )
496
+ )
497
+ ),
498
+ Stream.fromEffect(
499
+ Ref.get(keysRef).pipe(
500
+ Effect.map((keys) => ({ _tag: "done" as const, metadata: { invalidateQueries: keys } }))
501
+ )
502
+ )
503
+ )
504
+ }
505
+ const effect = (result as Effect.Effect<unknown, unknown, unknown>).pipe(
391
506
  Effect.withSpan(`Request.${meta.moduleName}.${resource._tag}`, {}, {
392
507
  captureStackTrace: () => handler.stack // capturing the handler stack is the main reason why we are doing the span here
393
508
  })
394
509
  )
510
+
511
+ return applyRequestTypeInterruptibility(resource.type, effect)
512
+ }
395
513
  ] as const
396
514
  return acc
397
515
  }, {} as any) as {
@@ -415,9 +533,28 @@ export const makeRouter = <
415
533
  const rpcs = RpcGroup
416
534
  .make(
417
535
  ...typedValuesOf(mapped).map(([resource]) => {
418
- return Rpc
419
- .make(resource._tag, { payload: resource, success: resource.success, error: resource.error })
536
+ const isStream = resource.type === "stream"
537
+ const isCommand = resource.type === "command"
538
+ return (isCommand
539
+ ? Invalidation.makeCommandRpc(resource._tag, {
540
+ payload: resource,
541
+ success: resource.success,
542
+ error: resource.error
543
+ })
544
+ : isStream
545
+ ? Invalidation.makeStreamRpc(resource._tag, {
546
+ payload: resource,
547
+ success: resource.success,
548
+ error: resource.error,
549
+ stream: true as const
550
+ })
551
+ : Rpc.make(resource._tag, {
552
+ payload: resource,
553
+ success: resource.success,
554
+ error: resource.error
555
+ }))
420
556
  .annotate(middleware.requestContext, resource.config ?? {})
557
+ .annotate(RequestTypeAnnotation, resource.type)
421
558
  })
422
559
  )
423
560
  .prefix(`${meta.moduleName}.`)
@@ -479,8 +616,7 @@ export const makeRouter = <
479
616
  any,
480
617
  any
481
618
  >,
482
- { [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> },
483
- any
619
+ { [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> }
484
620
  >
485
621
  /** @deprecated */
486
622
  readonly ಠ_ಠ: never
@@ -511,8 +647,7 @@ export const makeRouter = <
511
647
  // v4: generators yield Yieldable with asEffect()
512
648
  effect: (match: typeof router3) => Generator<
513
649
  Yieldable<any, any, any, any>,
514
- { [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> },
515
- any
650
+ { [K in keyof FilterRequestModules<Resource>]: AnyHandler<Resource[K]> }
516
651
  >
517
652
  }
518
653
  >(
@@ -572,23 +707,23 @@ export type MakeErrors<Make> = /*Make extends { readonly effect: (_: any) => Eff
572
707
  : Make extends { readonly effect: (_: any) => Effect.Effect<any, never, any> } ? never
573
708
  : */
574
709
  // v4: generators yield Yieldable with asEffect()
575
- Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, never, any>, any, any> } ? never
576
- : Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, infer E, any>, any, any> } ? E
710
+ Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, never, any>> } ? never
711
+ : Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, infer E, any>> } ? E
577
712
  : never
578
713
 
579
714
  export type MakeContext<Make> = /*Make extends { readonly effect: (_: any) => Effect.Effect<any, any, infer R> } ? R
580
715
  : Make extends { readonly effect: (_: any) => Effect.Effect<any, any, never> } ? never
581
716
  : */
582
717
  // v4: generators yield Yieldable with asEffect()
583
- Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any, never>, any, any> } ? never
584
- : Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any, infer R>, any, any> } ? R
718
+ Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any>> } ? never
719
+ : Make extends { readonly effect: (_: any) => Generator<Yieldable<any, any, any, infer R>> } ? R
585
720
  : never
586
721
 
587
722
  export type MakeHandlers<Make, _Handlers extends Record<string, any>> = /*Make extends
588
723
  { readonly effect: (_: any) => Effect.Effect<{ [K in keyof Handlers]: AnyHandler<Handlers[K]> }, any, any> }
589
724
  ? Effect.Success<ReturnType<Make["effect"]>>
590
725
  : */
591
- Make extends { readonly effect: (_: any) => Generator<any, infer S, any> } ? S
726
+ Make extends { readonly effect: (_: any) => Generator<any, infer S> } ? S
592
727
  : never
593
728
 
594
729
  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,16 +52,25 @@ 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),
@@ -60,8 +78,9 @@ export function setupRequestContext<R, E, A>(self: Effect.Effect<A, E, R>, reque
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,7 +88,7 @@ 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,
@@ -78,7 +97,8 @@ export function setupRequestContextWithCustomSpan<R, E, A>(
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
 
@@ -13,47 +13,41 @@ const tryCauseException = <E>(cause: Cause.Cause<E>, name: string): CauseExcepti
13
13
  }
14
14
  }
15
15
 
16
- export function reportError(
17
- name: string
18
- ) {
19
- return (
20
- cause: Cause.Cause<unknown>,
21
- extras?: Record<string, unknown>,
22
- level: LogLevel.Severity = "Error"
23
- ) =>
24
- Effect
25
- .gen(function*() {
26
- if (Cause.hasInterruptsOnly(cause)) {
27
- yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
28
- return
29
- }
30
- const error = tryCauseException(cause, name)
31
-
32
- yield* reportSentry(error, extras, LogLevelToSentry(level))
33
- yield* InfraLogger
34
- .logWithLevel(level, "Reporting error", cause)
35
- .pipe(
36
- Effect.annotateLogs(dropUndefined({
37
- extras,
38
- error: tryToReport(error),
39
- cause: tryToJson(cause),
40
- __error_name__: name
41
- }))
42
- )
43
- .pipe(
44
- Effect.catchCause((cause) => InfraLogger.logWarning("Failed to log error", cause)),
45
- Effect.catchCause(() => InfraLogger.logFatal("Failed to log error cause"))
46
- )
16
+ export function reportError(name: string) {
17
+ return Effect.fnUntraced(
18
+ function*(
19
+ cause: Cause.Cause<unknown>,
20
+ extras?: Record<string, unknown>,
21
+ level: LogLevel.Severity = "Error"
22
+ ) {
23
+ if (Cause.hasInterruptsOnly(cause)) {
24
+ yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
25
+ return
26
+ }
27
+ const error = tryCauseException(cause, name)
47
28
 
48
- return error
49
- })
50
- .pipe(
51
- Effect.tapCause((cause) =>
52
- InfraLogger.logError("Failed to report error", cause).pipe(
53
- Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
54
- )
29
+ yield* reportSentry(error, extras, LogLevelToSentry(level))
30
+ yield* InfraLogger
31
+ .logWithLevel(level, "Reporting error", cause)
32
+ .pipe(
33
+ Effect.annotateLogs(dropUndefined({
34
+ extras,
35
+ error: tryToReport(error),
36
+ cause: tryToJson(cause),
37
+ __error_name__: name
38
+ })),
39
+ Effect.catchCause((cause) => InfraLogger.logWarning("Failed to log error", cause)),
40
+ Effect.catchCause(() => InfraLogger.logFatal("Failed to log error cause"))
55
41
  )
56
- )
42
+
43
+ return error
44
+ },
45
+ (effect) =>
46
+ Effect.tapCause(effect, (cause) =>
47
+ InfraLogger.logError("Failed to report error", cause).pipe(
48
+ Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
49
+ ))
50
+ )
57
51
  }
58
52
 
59
53
  function reportSentry(
@@ -66,45 +60,39 @@ function reportSentry(
66
60
  scope.setLevel(level)
67
61
  if (context) scope.setContext("context", { ...context })
68
62
  if (extras) scope.setContext("extras", extras)
69
- scope.setContext("error", { data: tryToReport(error) })
70
- scope.setContext("cause", { data: tryToJson(error.originalCause) })
63
+ const squashed = Cause.squash(error.originalCause)
64
+ scope.setContext("mainError", tryToJson(squashed))
65
+ scope.setContext("error", tryToReport(error))
66
+ scope.setContext("cause", tryToJson(error.originalCause))
71
67
  Sentry.captureException(error, scope)
72
68
  }))
73
69
  }
74
70
 
75
- export function logError<E>(
76
- name: string
77
- ) {
78
- return (cause: Cause.Cause<E>, extras?: Record<string, unknown>) =>
79
- Effect
80
- .gen(function*() {
81
- if (Cause.hasInterruptsOnly(cause)) {
82
- yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
83
- return
84
- }
85
- yield* InfraLogger
86
- .logWarning("Logging error", cause)
87
- .pipe(
88
- Effect.annotateLogs(dropUndefined({
89
- extras,
90
- cause: tryToJson(cause),
91
- __error_name__: name
92
- }))
93
- )
94
- })
95
- .pipe(
96
- Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
97
- )
71
+ export function logError<E>(name: string) {
72
+ return Effect.fnUntraced(
73
+ function*(cause: Cause.Cause<E>, extras?: Record<string, unknown>) {
74
+ if (Cause.hasInterruptsOnly(cause)) {
75
+ yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
76
+ return
77
+ }
78
+ yield* InfraLogger
79
+ .logWarning("Logging error", cause)
80
+ .pipe(Effect.annotateLogs(dropUndefined({
81
+ extras,
82
+ cause: tryToJson(cause),
83
+ __error_name__: name
84
+ })))
85
+ },
86
+ (effect) => Effect.tapCause(effect, () => InfraLogger.logFatal("Failed to log error cause"))
87
+ )
98
88
  }
99
89
 
100
- export function reportMessage(message: string, extras?: Record<string, unknown>) {
101
- return Effect.gen(function*() {
102
- const context = yield* getRC
103
- const scope = new Sentry.Scope()
104
- if (context) scope.setContext("context", { ...context })
105
- if (extras) scope.setContext("extras", extras)
106
- Sentry.captureMessage(message, scope)
90
+ export const reportMessage = Effect.fnUntraced(function*(message: string, extras?: Record<string, unknown>) {
91
+ const context = yield* getRC
92
+ const scope = new Sentry.Scope()
93
+ if (context) scope.setContext("context", { ...context })
94
+ if (extras) scope.setContext("extras", extras)
95
+ Sentry.captureMessage(message, scope)
107
96
 
108
- console.warn(message, extras)
109
- })
110
- }
97
+ console.warn(message, extras)
98
+ })
@@ -7,7 +7,7 @@ export function getRequestContextFromFiber(fiber: Fiber.Fiber<unknown, unknown>)
7
7
  const span = Option.fromNullishOr(fiber.currentSpan)
8
8
  const locale = fiber.getRef(LocaleRef)
9
9
  const namespace = fiber.getRef(storeId)
10
- return new RequestContext({
10
+ return RequestContext.make({
11
11
  span: Option.map(span, (s) => ({ spanId: s.spanId, traceId: s.traceId, sampled: s.sampled })).pipe(
12
12
  Option.getOrElse(() => ({ spanId: "bogus", sampled: true, traceId: "bogus" }))
13
13
  ),