@effect-app/infra 4.0.0-beta.22 → 4.0.0-beta.221

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 (337) hide show
  1. package/CHANGELOG.md +1648 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +12 -7
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +16 -12
  6. package/dist/Emailer/Sendgrid.d.ts +15 -15
  7. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  8. package/dist/Emailer/Sendgrid.js +20 -16
  9. package/dist/Emailer/fake.d.ts +1 -1
  10. package/dist/Emailer/fake.js +2 -2
  11. package/dist/Emailer/service.d.ts +13 -4
  12. package/dist/Emailer/service.d.ts.map +1 -1
  13. package/dist/Emailer/service.js +4 -3
  14. package/dist/Emailer.d.ts +1 -1
  15. package/dist/MainFiberSet.d.ts +12 -9
  16. package/dist/MainFiberSet.d.ts.map +1 -1
  17. package/dist/MainFiberSet.js +7 -3
  18. package/dist/Model/Repository/Registry.d.ts +21 -0
  19. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  20. package/dist/Model/Repository/Registry.js +18 -0
  21. package/dist/Model/Repository/ext.d.ts +35 -16
  22. package/dist/Model/Repository/ext.d.ts.map +1 -1
  23. package/dist/Model/Repository/ext.js +60 -3
  24. package/dist/Model/Repository/internal/internal.d.ts +9 -6
  25. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  26. package/dist/Model/Repository/internal/internal.js +115 -51
  27. package/dist/Model/Repository/legacy.d.ts +4 -2
  28. package/dist/Model/Repository/legacy.d.ts.map +1 -1
  29. package/dist/Model/Repository/makeRepo.d.ts +10 -6
  30. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  31. package/dist/Model/Repository/makeRepo.js +5 -2
  32. package/dist/Model/Repository/service.d.ts +32 -24
  33. package/dist/Model/Repository/service.d.ts.map +1 -1
  34. package/dist/Model/Repository/validation.d.ts +47 -18
  35. package/dist/Model/Repository/validation.d.ts.map +1 -1
  36. package/dist/Model/Repository/validation.js +6 -6
  37. package/dist/Model/Repository.d.ts +2 -1
  38. package/dist/Model/Repository.d.ts.map +1 -1
  39. package/dist/Model/Repository.js +2 -1
  40. package/dist/Model/dsl.d.ts +6 -5
  41. package/dist/Model/dsl.d.ts.map +1 -1
  42. package/dist/Model/dsl.js +2 -3
  43. package/dist/Model/filter/filterApi.d.ts +5 -5
  44. package/dist/Model/filter/filterApi.d.ts.map +1 -1
  45. package/dist/Model/filter/types/errors.d.ts +1 -1
  46. package/dist/Model/filter/types/fields.d.ts +1 -1
  47. package/dist/Model/filter/types/path/common.d.ts +1 -1
  48. package/dist/Model/filter/types/path/eager.d.ts +1 -1
  49. package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
  50. package/dist/Model/filter/types/path/eager.js +1 -1
  51. package/dist/Model/filter/types/path/index.d.ts +1 -1
  52. package/dist/Model/filter/types/utils.d.ts +1 -1
  53. package/dist/Model/filter/types/validator.d.ts +1 -1
  54. package/dist/Model/filter/types.d.ts +1 -1
  55. package/dist/Model/query/dsl.d.ts +142 -17
  56. package/dist/Model/query/dsl.d.ts.map +1 -1
  57. package/dist/Model/query/dsl.js +190 -5
  58. package/dist/Model/query/new-kid-interpreter.d.ts +77 -8
  59. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  60. package/dist/Model/query/new-kid-interpreter.js +127 -6
  61. package/dist/Model/query.d.ts +1 -1
  62. package/dist/Model.d.ts +2 -1
  63. package/dist/Model.d.ts.map +1 -1
  64. package/dist/Model.js +2 -1
  65. package/dist/QueueMaker/SQLQueue.d.ts +7 -8
  66. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  67. package/dist/QueueMaker/SQLQueue.js +135 -117
  68. package/dist/QueueMaker/errors.d.ts +5 -3
  69. package/dist/QueueMaker/errors.d.ts.map +1 -1
  70. package/dist/QueueMaker/errors.js +4 -2
  71. package/dist/QueueMaker/memQueue.d.ts +9 -5
  72. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  73. package/dist/QueueMaker/memQueue.js +81 -65
  74. package/dist/QueueMaker/sbqueue.d.ts +8 -4
  75. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  76. package/dist/QueueMaker/sbqueue.js +57 -55
  77. package/dist/QueueMaker/service.d.ts +4 -2
  78. package/dist/QueueMaker/service.d.ts.map +1 -1
  79. package/dist/QueueMaker/service.js +1 -1
  80. package/dist/RequestContext.d.ts +75 -35
  81. package/dist/RequestContext.d.ts.map +1 -1
  82. package/dist/RequestContext.js +14 -14
  83. package/dist/RequestFiberSet.d.ts +10 -7
  84. package/dist/RequestFiberSet.d.ts.map +1 -1
  85. package/dist/RequestFiberSet.js +8 -3
  86. package/dist/Store/ContextMapContainer.d.ts +22 -3
  87. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  88. package/dist/Store/ContextMapContainer.js +17 -3
  89. package/dist/Store/Cosmos/query.d.ts +7 -2
  90. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  91. package/dist/Store/Cosmos/query.js +115 -35
  92. package/dist/Store/Cosmos.d.ts +2 -2
  93. package/dist/Store/Cosmos.d.ts.map +1 -1
  94. package/dist/Store/Cosmos.js +343 -244
  95. package/dist/Store/Disk.d.ts +3 -3
  96. package/dist/Store/Disk.d.ts.map +1 -1
  97. package/dist/Store/Disk.js +76 -36
  98. package/dist/Store/Memory.d.ts +7 -4
  99. package/dist/Store/Memory.d.ts.map +1 -1
  100. package/dist/Store/Memory.js +251 -58
  101. package/dist/Store/SQL/Pg.d.ts +4 -0
  102. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  103. package/dist/Store/SQL/Pg.js +233 -0
  104. package/dist/Store/SQL/query.d.ts +43 -0
  105. package/dist/Store/SQL/query.d.ts.map +1 -0
  106. package/dist/Store/SQL/query.js +478 -0
  107. package/dist/Store/SQL.d.ts +21 -0
  108. package/dist/Store/SQL.d.ts.map +1 -0
  109. package/dist/Store/SQL.js +450 -0
  110. package/dist/Store/codeFilter.d.ts +2 -2
  111. package/dist/Store/codeFilter.d.ts.map +1 -1
  112. package/dist/Store/codeFilter.js +6 -3
  113. package/dist/Store/index.d.ts +6 -3
  114. package/dist/Store/index.d.ts.map +1 -1
  115. package/dist/Store/index.js +18 -4
  116. package/dist/Store/service.d.ts +26 -8
  117. package/dist/Store/service.d.ts.map +1 -1
  118. package/dist/Store/service.js +25 -6
  119. package/dist/Store/utils.d.ts +3 -2
  120. package/dist/Store/utils.d.ts.map +1 -1
  121. package/dist/Store/utils.js +5 -5
  122. package/dist/Store.d.ts +1 -1
  123. package/dist/adapters/SQL/Model.d.ts +32 -43
  124. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  125. package/dist/adapters/SQL/Model.js +30 -39
  126. package/dist/adapters/SQL.d.ts +1 -1
  127. package/dist/adapters/ServiceBus.d.ts +14 -11
  128. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  129. package/dist/adapters/ServiceBus.js +30 -21
  130. package/dist/adapters/cosmos-client.d.ts +5 -3
  131. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  132. package/dist/adapters/cosmos-client.js +5 -3
  133. package/dist/adapters/index.d.ts +8 -2
  134. package/dist/adapters/index.d.ts.map +1 -1
  135. package/dist/adapters/index.js +8 -2
  136. package/dist/adapters/logger.d.ts +2 -2
  137. package/dist/adapters/logger.d.ts.map +1 -1
  138. package/dist/adapters/memQueue.d.ts +5 -3
  139. package/dist/adapters/memQueue.d.ts.map +1 -1
  140. package/dist/adapters/memQueue.js +6 -5
  141. package/dist/adapters/mongo-client.d.ts +4 -3
  142. package/dist/adapters/mongo-client.d.ts.map +1 -1
  143. package/dist/adapters/mongo-client.js +5 -3
  144. package/dist/adapters/redis-client.d.ts +6 -3
  145. package/dist/adapters/redis-client.d.ts.map +1 -1
  146. package/dist/adapters/redis-client.js +7 -3
  147. package/dist/api/ContextProvider.d.ts +12 -8
  148. package/dist/api/ContextProvider.d.ts.map +1 -1
  149. package/dist/api/ContextProvider.js +9 -7
  150. package/dist/api/codec.d.ts +1 -1
  151. package/dist/api/internal/RequestContextMiddleware.d.ts +3 -3
  152. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  153. package/dist/api/internal/RequestContextMiddleware.js +10 -6
  154. package/dist/api/internal/auth.d.ts +45 -7
  155. package/dist/api/internal/auth.d.ts.map +1 -1
  156. package/dist/api/internal/auth.js +162 -29
  157. package/dist/api/internal/events.d.ts +6 -4
  158. package/dist/api/internal/events.d.ts.map +1 -1
  159. package/dist/api/internal/events.js +16 -9
  160. package/dist/api/internal/health.d.ts +1 -1
  161. package/dist/api/layerUtils.d.ts +10 -6
  162. package/dist/api/layerUtils.d.ts.map +1 -1
  163. package/dist/api/layerUtils.js +7 -6
  164. package/dist/api/middlewares.d.ts +1 -1
  165. package/dist/api/reportError.d.ts +2 -2
  166. package/dist/api/reportError.d.ts.map +1 -1
  167. package/dist/api/reportError.js +3 -2
  168. package/dist/api/routing/middleware/RouterMiddleware.d.ts +5 -4
  169. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  170. package/dist/api/routing/middleware/middleware.d.ts +42 -3
  171. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  172. package/dist/api/routing/middleware/middleware.js +53 -17
  173. package/dist/api/routing/middleware.d.ts +1 -2
  174. package/dist/api/routing/middleware.d.ts.map +1 -1
  175. package/dist/api/routing/middleware.js +1 -2
  176. package/dist/api/routing/schema/jwt.d.ts +1 -1
  177. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  178. package/dist/api/routing/schema/jwt.js +3 -2
  179. package/dist/api/routing/tsort.d.ts +1 -1
  180. package/dist/api/routing/tsort.d.ts.map +1 -1
  181. package/dist/api/routing/utils.d.ts +4 -4
  182. package/dist/api/routing/utils.d.ts.map +1 -1
  183. package/dist/api/routing/utils.js +3 -2
  184. package/dist/api/routing.d.ts +84 -37
  185. package/dist/api/routing.d.ts.map +1 -1
  186. package/dist/api/routing.js +115 -45
  187. package/dist/api/setupRequest.d.ts +10 -6
  188. package/dist/api/setupRequest.d.ts.map +1 -1
  189. package/dist/api/setupRequest.js +15 -7
  190. package/dist/api/util.d.ts +1 -1
  191. package/dist/arbs.d.ts +2 -2
  192. package/dist/arbs.d.ts.map +1 -1
  193. package/dist/arbs.js +5 -3
  194. package/dist/errorReporter.d.ts +7 -5
  195. package/dist/errorReporter.d.ts.map +1 -1
  196. package/dist/errorReporter.js +22 -26
  197. package/dist/errors.d.ts +1 -1
  198. package/dist/fileUtil.d.ts +2 -2
  199. package/dist/fileUtil.d.ts.map +1 -1
  200. package/dist/fileUtil.js +2 -2
  201. package/dist/index.d.ts +1 -1
  202. package/dist/logger/jsonLogger.d.ts +2 -2
  203. package/dist/logger/jsonLogger.d.ts.map +1 -1
  204. package/dist/logger/jsonLogger.js +4 -2
  205. package/dist/logger/logFmtLogger.d.ts +2 -2
  206. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  207. package/dist/logger/logFmtLogger.js +2 -2
  208. package/dist/logger/shared.d.ts +2 -2
  209. package/dist/logger/shared.d.ts.map +1 -1
  210. package/dist/logger/shared.js +3 -3
  211. package/dist/logger.d.ts +1 -1
  212. package/dist/logger.d.ts.map +1 -1
  213. package/dist/otel.d.ts +75 -0
  214. package/dist/otel.d.ts.map +1 -0
  215. package/dist/otel.js +65 -0
  216. package/dist/rateLimit.d.ts +12 -4
  217. package/dist/rateLimit.d.ts.map +1 -1
  218. package/dist/rateLimit.js +7 -12
  219. package/dist/test.d.ts +3 -3
  220. package/dist/test.d.ts.map +1 -1
  221. package/dist/test.js +2 -2
  222. package/dist/vitest.d.ts +1 -1
  223. package/examples/query.ts +46 -38
  224. package/package.json +46 -37
  225. package/src/CUPS.ts +15 -11
  226. package/src/Emailer/Sendgrid.ts +21 -15
  227. package/src/Emailer/fake.ts +1 -1
  228. package/src/Emailer/service.ts +13 -3
  229. package/src/MainFiberSet.ts +9 -6
  230. package/src/Model/Repository/Registry.ts +34 -0
  231. package/src/Model/Repository/ext.ts +103 -11
  232. package/src/Model/Repository/internal/internal.ts +231 -149
  233. package/src/Model/Repository/legacy.ts +3 -1
  234. package/src/Model/Repository/makeRepo.ts +15 -10
  235. package/src/Model/Repository/service.ts +35 -23
  236. package/src/Model/Repository/validation.ts +5 -5
  237. package/src/Model/Repository.ts +1 -0
  238. package/src/Model/dsl.ts +5 -4
  239. package/src/Model/filter/types/path/eager.ts +1 -2
  240. package/src/Model/query/dsl.ts +353 -19
  241. package/src/Model/query/new-kid-interpreter.ts +211 -6
  242. package/src/Model.ts +1 -0
  243. package/src/QueueMaker/SQLQueue.ts +150 -153
  244. package/src/QueueMaker/errors.ts +3 -1
  245. package/src/QueueMaker/memQueue.ts +111 -105
  246. package/src/QueueMaker/sbqueue.ts +76 -88
  247. package/src/QueueMaker/service.ts +3 -1
  248. package/src/RequestContext.ts +15 -16
  249. package/src/RequestFiberSet.ts +8 -2
  250. package/src/Store/ContextMapContainer.ts +45 -2
  251. package/src/Store/Cosmos/query.ts +143 -44
  252. package/src/Store/Cosmos.ts +491 -350
  253. package/src/Store/Disk.ts +106 -66
  254. package/src/Store/Memory.ts +285 -87
  255. package/src/Store/SQL/Pg.ts +364 -0
  256. package/src/Store/SQL/query.ts +540 -0
  257. package/src/Store/SQL.ts +736 -0
  258. package/src/Store/codeFilter.ts +5 -2
  259. package/src/Store/index.ts +20 -3
  260. package/src/Store/service.ts +45 -10
  261. package/src/Store/utils.ts +25 -23
  262. package/src/adapters/SQL/Model.ts +42 -41
  263. package/src/adapters/ServiceBus.ts +131 -121
  264. package/src/adapters/cosmos-client.ts +4 -2
  265. package/src/adapters/index.ts +7 -0
  266. package/src/adapters/memQueue.ts +5 -4
  267. package/src/adapters/mongo-client.ts +4 -2
  268. package/src/adapters/redis-client.ts +6 -2
  269. package/src/api/ContextProvider.ts +17 -13
  270. package/src/api/internal/RequestContextMiddleware.ts +16 -5
  271. package/src/api/internal/auth.ts +248 -44
  272. package/src/api/internal/events.ts +19 -10
  273. package/src/api/layerUtils.ts +12 -8
  274. package/src/api/reportError.ts +2 -1
  275. package/src/api/routing/middleware/RouterMiddleware.ts +5 -4
  276. package/src/api/routing/middleware/middleware.ts +60 -15
  277. package/src/api/routing/middleware.ts +0 -2
  278. package/src/api/routing/schema/jwt.ts +2 -1
  279. package/src/api/routing/utils.ts +2 -1
  280. package/src/api/routing.ts +304 -131
  281. package/src/api/setupRequest.ts +31 -8
  282. package/src/arbs.ts +5 -3
  283. package/src/errorReporter.ts +65 -75
  284. package/src/fileUtil.ts +1 -1
  285. package/src/logger/jsonLogger.ts +3 -1
  286. package/src/logger/logFmtLogger.ts +1 -1
  287. package/src/logger/shared.ts +3 -2
  288. package/src/otel.ts +152 -0
  289. package/src/rateLimit.ts +34 -23
  290. package/src/test.ts +2 -2
  291. package/test/auth.test.ts +101 -0
  292. package/test/contextProvider.test.ts +14 -11
  293. package/test/controller.test.ts +25 -29
  294. package/test/dist/auth.test.d.ts.map +1 -0
  295. package/test/dist/contextProvider.test.d.ts.map +1 -1
  296. package/test/dist/controller.test.d.ts.map +1 -1
  297. package/test/dist/date-query.test.d.ts.map +1 -0
  298. package/test/dist/fixtures.d.ts +30 -12
  299. package/test/dist/fixtures.d.ts.map +1 -1
  300. package/test/dist/fixtures.js +17 -10
  301. package/test/dist/query.test.d.ts.map +1 -1
  302. package/test/dist/rawQuery.test.d.ts.map +1 -1
  303. package/test/dist/repository-ext.test.d.ts.map +1 -0
  304. package/test/dist/requires.test.d.ts.map +1 -1
  305. package/test/dist/router-generator.test.d.ts.map +1 -0
  306. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  307. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  308. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  309. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  310. package/test/dist/sql-store.test.d.ts.map +1 -0
  311. package/test/fixtures.ts +16 -9
  312. package/test/layerUtils.test.ts +1 -1
  313. package/test/query.test.ts +819 -38
  314. package/test/rawQuery.test.ts +312 -20
  315. package/test/repository-ext.test.ts +62 -0
  316. package/test/requires.test.ts +10 -5
  317. package/test/router-generator.test.ts +187 -0
  318. package/test/routing-interruptibility.test.ts +66 -0
  319. package/test/rpc-e2e-invalidation.test.ts +256 -0
  320. package/test/rpc-multi-middleware.test.ts +84 -9
  321. package/test/rpc-stream-fullstack.test.ts +304 -0
  322. package/test/sql-store.test.ts +1592 -0
  323. package/test/validateSample.test.ts +17 -12
  324. package/tsconfig.examples.json +1 -1
  325. package/tsconfig.json +0 -1
  326. package/tsconfig.json.bak +2 -2
  327. package/tsconfig.src.json +35 -35
  328. package/tsconfig.test.json +2 -2
  329. package/dist/Operations.d.ts +0 -55
  330. package/dist/Operations.d.ts.map +0 -1
  331. package/dist/Operations.js +0 -102
  332. package/dist/OperationsRepo.d.ts +0 -41
  333. package/dist/OperationsRepo.d.ts.map +0 -1
  334. package/dist/OperationsRepo.js +0 -14
  335. package/eslint.config.mjs +0 -24
  336. package/src/Operations.ts +0 -235
  337. package/src/OperationsRepo.ts +0 -16
@@ -1,14 +1,20 @@
1
- import { Cause, Tracer } from "effect"
2
- import { Effect, Fiber, flow, type NonEmptyReadonlyArray, S } from "effect-app"
1
+ import type { NonEmptyReadonlyArray } from "effect-app/Array"
2
+ import * as Effect from "effect-app/Effect"
3
+ import * as S from "effect-app/Schema"
3
4
  import { pretty } from "effect-app/utils"
5
+ import * as Cause from "effect/Cause"
6
+ import * as Fiber from "effect/Fiber"
7
+ import { flow } from "effect/Function"
4
8
  import * as Q from "effect/Queue"
9
+ import * as Tracer from "effect/Tracer"
5
10
  import { MemQueue } from "../adapters/memQueue.js"
6
11
  import { getRequestContext, setupRequestContextWithCustomSpan } from "../api/setupRequest.js"
7
12
  import { InfraLogger } from "../logger.js"
13
+ import { messagingSpanArgs } from "../otel.js"
8
14
  import { reportNonInterruptedFailure, reportNonInterruptedFailureCause } from "./errors.js"
9
- import { type QueueBase, QueueMeta } from "./service.js"
15
+ import { QueueMeta } from "./service.js"
10
16
 
11
- export function makeMemQueue<
17
+ export const makeMemQueue = Effect.fnUntraced(function*<
12
18
  Evt extends { id: S.StringId; _tag: string },
13
19
  DrainEvt extends { id: S.StringId; _tag: string },
14
20
  EvtE,
@@ -19,112 +25,112 @@ export function makeMemQueue<
19
25
  schema: S.Codec<Evt, EvtE>,
20
26
  drainSchema: S.Codec<DrainEvt, DrainEvtE>
21
27
  ) {
22
- return Effect.gen(function*() {
23
- const mem = yield* MemQueue
24
- const q = yield* mem.getOrCreateQueue(queueName)
25
- const qDrain = yield* mem.getOrCreateQueue(queueDrainName)
28
+ const mem = yield* MemQueue
29
+ const q = yield* mem.getOrCreateQueue(queueName)
30
+ const qDrain = yield* mem.getOrCreateQueue(queueDrainName)
26
31
 
27
- const wireSchema = S.Struct({ body: schema, meta: QueueMeta })
28
- const wireSchemaJson = S.fromJsonString(wireSchema)
29
- const encodePublish = S.encodeEffect(wireSchemaJson)
30
- const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
31
- const drainWJson = S.fromJsonString(drainW)
32
+ const wireSchema = S.Struct({ body: schema, meta: QueueMeta })
33
+ const wireSchemaJson = S.fromJsonString(S.toCodecJson(wireSchema))
34
+ const encodePublish = S.encodeEffect(wireSchemaJson)
35
+ const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
36
+ const drainWJson = S.fromJsonString(S.toCodecJson(drainW))
32
37
 
33
- const parseDrain = flow(S.decodeUnknownEffect(drainWJson), Effect.orDie)
38
+ const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
34
39
 
35
- const queue = {
36
- publish: (...messages: NonEmptyReadonlyArray<Evt>) =>
37
- getRequestContext
38
- .pipe(
39
- Effect.flatMap((requestContext) =>
40
- Effect
41
- .forEach(messages, (m) =>
42
- // we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
43
- encodePublish({ body: m, meta: requestContext }).pipe(
44
- Effect.orDie,
45
- // .tap((msg) => info("Publishing Mem Message: " + utils.inspect(msg)))
46
- Effect.flatMap((_) => Q.offer(q, _))
47
- ), { discard: true })
48
- ),
49
- Effect.withSpan("queue.publish: " + queueName, {
50
- kind: "producer",
51
- attributes: { "message_tags": messages.map((_) => _._tag) }
52
- }, { captureStackTrace: false })
53
- ),
54
- drain: <DrainE, DrainR>(
55
- handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
56
- sessionId?: string
57
- ) => {
58
- const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
59
- const reportError = reportNonInterruptedFailureCause({ name: "MemQueue.drain." + queueDrainName })
60
- const processMessage = (msg: string) =>
61
- // we JSON parse, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
62
- parseDrain(msg).pipe(
40
+ const queue = {
41
+ publish: Effect.fn(`publish ${queueName}`, {
42
+ kind: "producer",
43
+ attributes: {
44
+ "messaging.system": "memory",
45
+ "messaging.operation.name": "publish",
46
+ "messaging.destination.name": queueName
47
+ }
48
+ })(function*(
49
+ ...messages: NonEmptyReadonlyArray<Evt>
50
+ ) {
51
+ yield* Effect.annotateCurrentSpan({
52
+ "messaging.batch.message_count": messages.length,
53
+ "messaging.message.types": messages.map((_) => _._tag)
54
+ })
55
+ const requestContext = yield* getRequestContext
56
+ // we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
57
+ yield* Effect.forEach(
58
+ messages,
59
+ (m) =>
60
+ encodePublish({ body: m, meta: requestContext }).pipe(
63
61
  Effect.orDie,
64
- Effect
65
- .flatMap(({ body, meta }) => {
66
- let effect = InfraLogger
67
- .logDebug(`[${queueDrainName}] Processing incoming message`)
68
- .pipe(
69
- Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
70
- Effect.andThen(handleEvent(body)),
71
- silenceAndReportError,
72
- (_) =>
73
- setupRequestContextWithCustomSpan(
74
- _,
75
- meta,
76
- `queue.drain: ${queueDrainName}.${body._tag}`,
77
- {
78
- captureStackTrace: false,
79
- kind: "consumer",
80
- attributes: {
81
- "queue.name": queueDrainName,
82
- "queue.sessionId": sessionId,
83
- "queue.input": body
84
- }
85
- }
86
- )
87
- )
88
- if (meta.span) {
89
- effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
90
- }
91
- return effect
92
- })
93
- )
94
- return Q
95
- .take(qDrain)
62
+ Effect.flatMap((_) => Q.offer(q, _))
63
+ ),
64
+ { discard: true }
65
+ )
66
+ }),
67
+ drain: <DrainE, DrainR>(
68
+ handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
69
+ sessionId?: string
70
+ ) => {
71
+ const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
72
+ const reportError = reportNonInterruptedFailureCause({ name: "MemQueue.drain." + queueDrainName })
73
+ const processMessage = Effect.fnUntraced(function*(msg: string) {
74
+ // we JSON parse, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
75
+ const { body, meta } = yield* parseDrain(msg).pipe(Effect.orDie)
76
+ let effect = InfraLogger
77
+ .logDebug(`[${queueDrainName}] Processing incoming message`)
96
78
  .pipe(
97
- Effect
98
- .flatMap((x) =>
99
- processMessage(x).pipe(
100
- Effect.uninterruptible,
101
- Effect.forkChild,
102
- Effect.flatMap(Fiber.join),
103
- // normally a failed item would be returned to the queue and retried up to X times.
104
- Effect.flatMap((_) =>
105
- _._tag === "Failure" && !Cause.hasInterruptsOnly(_.cause)
106
- ? Q.offer(qDrain, x).pipe(
107
- // TODO: retry count tracking and max retries.
108
- Effect.delay("5 seconds"),
109
- Effect.tapCause(reportError),
110
- Effect.forkDetach
111
- )
112
- : Effect.void
113
- )
114
- )
115
- ),
79
+ Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
80
+ Effect.andThen(handleEvent(body)),
116
81
  silenceAndReportError,
117
- Effect.withSpan(`queue.drain: ${queueDrainName}`, {
118
- attributes: {
119
- "queue.type": "mem",
120
- "queue.name": queueDrainName,
121
- "queue.sessionId": sessionId
122
- }
123
- }),
124
- Effect.forever
82
+ (_) => {
83
+ const args = messagingSpanArgs({
84
+ operation: "process",
85
+ system: "memory",
86
+ destination: queueDrainName,
87
+ messageId: body.id,
88
+ conversationId: sessionId,
89
+ extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
90
+ }, "consumer")
91
+ return setupRequestContextWithCustomSpan(
92
+ _,
93
+ meta,
94
+ args.name,
95
+ {
96
+ captureStackTrace: false,
97
+ kind: args.kind,
98
+ attributes: args.attributes
99
+ }
100
+ )
101
+ }
125
102
  )
126
- }
103
+ if (meta.span) {
104
+ effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
105
+ }
106
+ return yield* effect
107
+ })
108
+ return Effect.fn(`receive ${queueDrainName}`, {
109
+ kind: "consumer",
110
+ attributes: {
111
+ "messaging.system": "memory",
112
+ "messaging.operation.name": "receive",
113
+ "messaging.destination.name": queueDrainName,
114
+ ...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
115
+ }
116
+ })(function*() {
117
+ const x = yield* Q.take(qDrain)
118
+ const exit = yield* processMessage(x).pipe(
119
+ Effect.uninterruptible,
120
+ Effect.forkChild,
121
+ Effect.flatMap(Fiber.join)
122
+ )
123
+ if (exit._tag === "Failure" && !Cause.hasInterruptsOnly(exit.cause)) {
124
+ // normally a failed item would be returned to the queue and retried up to X times.
125
+ yield* Q.offer(qDrain, x).pipe(
126
+ // TODO: retry count tracking and max retries.
127
+ Effect.delay("5 seconds"),
128
+ Effect.tapCause(reportError),
129
+ Effect.forkDetach
130
+ )
131
+ }
132
+ }, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
127
133
  }
128
- return queue as QueueBase<Evt, DrainEvt>
129
- })
130
- }
134
+ }
135
+ return queue
136
+ })
@@ -1,12 +1,17 @@
1
- import { Tracer } from "effect"
2
- import { Cause, Effect, flow, type NonEmptyReadonlyArray, S } from "effect-app"
1
+ import type { NonEmptyReadonlyArray } from "effect-app/Array"
2
+ import * as Effect from "effect-app/Effect"
3
+ import * as S from "effect-app/Schema"
3
4
  import type { StringId } from "effect-app/Schema"
4
5
  import { pretty } from "effect-app/utils"
6
+ import * as Cause from "effect/Cause"
7
+ import { flow } from "effect/Function"
8
+ import * as Tracer from "effect/Tracer"
5
9
  import { Receiver, Sender } from "../adapters/ServiceBus.js"
6
10
  import { getRequestContext, setupRequestContextWithCustomSpan } from "../api/setupRequest.js"
7
11
  import { InfraLogger } from "../logger.js"
12
+ import { messagingSpanArgs } from "../otel.js"
8
13
  import { reportNonInterruptedFailure, reportNonInterruptedFailureCause, reportQueueError } from "./errors.js"
9
- import { type QueueBase, QueueMeta } from "./service.js"
14
+ import { QueueMeta } from "./service.js"
10
15
 
11
16
  export function makeServiceBusQueue<
12
17
  Evt extends { id: StringId; _tag: string },
@@ -21,11 +26,11 @@ export function makeServiceBusQueue<
21
26
  body: schema,
22
27
  meta: QueueMeta
23
28
  })
24
- const wireSchemaJson = S.fromJsonString(wireSchema)
29
+ const wireSchemaJson = S.fromJsonString(S.toCodecJson(wireSchema))
25
30
  const encodePublish = S.encodeEffect(wireSchemaJson)
26
31
  const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
27
- const drainWJson = S.fromJsonString(drainW)
28
- const parseDrain = flow(S.decodeUnknownEffect(drainWJson), Effect.orDie)
32
+ const drainWJson = S.fromJsonString(S.toCodecJson(drainW))
33
+ const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
29
34
 
30
35
  return Effect.gen(function*() {
31
36
  const sender = yield* Sender
@@ -42,96 +47,79 @@ export function makeServiceBusQueue<
42
47
  handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
43
48
  sessionId?: string
44
49
  ) => {
45
- function processMessage(messageBody: unknown) {
46
- return parseDrain(messageBody).pipe(
47
- Effect.orDie,
48
- Effect
49
- .flatMap(({ body, meta }) => {
50
- let effect = InfraLogger
51
- .logDebug(`[${receiver.name}] Processing incoming message`)
52
- .pipe(
53
- Effect.annotateLogs({
54
- body: pretty(body),
55
- meta: pretty(meta)
56
- }),
57
- Effect.andThen(handleEvent(body)),
58
- Effect.orDie
59
- )
60
- // we silenceAndReportError here, so that the error is reported, and moves into the Exit.
61
- .pipe(
62
- silenceAndReportError,
63
- (_) =>
64
- setupRequestContextWithCustomSpan(
65
- _,
66
- meta,
67
- `queue.drain: ${receiver.name}${sessionId ? `#${sessionId}` : ""}.${body._tag}`,
68
- {
69
- captureStackTrace: false,
70
- kind: "consumer",
71
- attributes: {
72
- "queue.name": receiver.name,
73
- "queue.sessionId": sessionId,
74
- "queue.input": body
75
- }
76
- }
77
- )
78
- )
79
- if (meta.span) {
80
- effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
81
- }
82
- return effect
83
- }),
84
- Effect
85
- // we reportError here, so that we report the error only, and keep flowing
86
- .tapCause(reportError),
87
- // we still need to flatten the Exit.
88
- Effect.flatMap((_) => _)
89
- )
90
- }
50
+ const processMessage = Effect.fnUntraced(function*(messageBody: unknown) {
51
+ const { body, meta } = yield* parseDrain(messageBody).pipe(Effect.orDie)
52
+ let effect = InfraLogger
53
+ .logDebug(`[${receiver.name}] Processing incoming message`)
54
+ .pipe(
55
+ Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
56
+ Effect.andThen(handleEvent(body)),
57
+ Effect.orDie,
58
+ // we silenceAndReportError here, so that the error is reported, and moves into the Exit.
59
+ silenceAndReportError,
60
+ (_) => {
61
+ const args = messagingSpanArgs({
62
+ operation: "process",
63
+ system: "servicebus",
64
+ destination: receiver.name,
65
+ messageId: body.id,
66
+ conversationId: sessionId,
67
+ extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
68
+ }, "consumer")
69
+ return setupRequestContextWithCustomSpan(
70
+ _,
71
+ meta,
72
+ args.name,
73
+ {
74
+ captureStackTrace: false,
75
+ kind: args.kind,
76
+ attributes: args.attributes
77
+ }
78
+ )
79
+ }
80
+ )
81
+ if (meta.span) {
82
+ effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
83
+ }
84
+ // we reportError here, so that we report the error only, and keep flowing
85
+ const exit = yield* Effect.tapCause(effect, reportError)
86
+ return yield* exit
87
+ })
91
88
 
92
89
  return receiver
93
90
  .subscribe({
94
91
  processMessage: (x) => processMessage(x.body).pipe(Effect.uninterruptible),
95
92
  processError: (err) => reportQueueError(Cause.fail(err.error))
96
- // Deferred.completeWith(
97
- // deferred,
98
- // reportFatalQueueError(Cause.fail(err.error))
99
- // .pipe(Effect.andThen(Effect.fail(err.error)))
100
- // )
101
93
  }, sessionId)
102
- // .pipe(Effect.andThen(Deferred.await(deferred).pipe(Effect.orDie))),
103
- .pipe(
104
- Effect.andThen(Effect.never)
105
- )
94
+ .pipe(Effect.andThen(Effect.never))
106
95
  },
107
96
 
108
- publish: (...messages: NonEmptyReadonlyArray<Evt>) =>
109
- getRequestContext
110
- .pipe(
111
- Effect.flatMap((requestContext) =>
112
- Effect
113
- .forEach(messages, (m) =>
114
- encodePublish({
115
- body: m,
116
- meta: requestContext
117
- })
118
- .pipe(
119
- Effect.orDie,
120
- Effect.map((body) => ({
121
- body,
122
- messageId: m.id, /* correllationid: requestId */
123
- contentType: "application/json",
124
- sessionId: "sessionId" in m ? m.sessionId as string : undefined as unknown as string // TODO: optional
125
- }))
126
- ))
127
- .pipe(Effect.flatMap((msgs) => sender.sendMessages(msgs)))
128
- ),
129
- Effect.withSpan("queue.publish: " + sender.name, {
130
- kind: "producer",
131
- attributes: { "message_tags": messages.map((_) => _._tag) }
132
- }, { captureStackTrace: false })
133
- )
97
+ publish: Effect.fn(`publish ${sender.name}`, {
98
+ kind: "producer",
99
+ attributes: {
100
+ "messaging.system": "servicebus",
101
+ "messaging.operation.name": "publish",
102
+ "messaging.destination.name": sender.name
103
+ }
104
+ })(function*(...messages: NonEmptyReadonlyArray<Evt>) {
105
+ yield* Effect.annotateCurrentSpan({
106
+ "messaging.batch.message_count": messages.length,
107
+ "messaging.message.types": messages.map((_) => _._tag)
108
+ })
109
+ const requestContext = yield* getRequestContext
110
+ const msgs = yield* Effect.forEach(messages, (m) =>
111
+ encodePublish({ body: m, meta: requestContext }).pipe(
112
+ Effect.orDie,
113
+ Effect.map((body) => ({
114
+ body,
115
+ messageId: m.id, /* correllationid: requestId */
116
+ contentType: "application/json",
117
+ sessionId: "sessionId" in m ? m.sessionId as string : undefined as unknown as string // TODO: optional
118
+ }))
119
+ ))
120
+ yield* sender.sendMessages(msgs)
121
+ })
134
122
  }
135
- return queue as QueueBase<Evt, DrainEvt>
123
+ return queue
136
124
  })
137
125
  }
@@ -1,4 +1,6 @@
1
- import type { Effect, NonEmptyReadonlyArray, Scope } from "effect-app"
1
+ import type { NonEmptyReadonlyArray } from "effect-app/Array"
2
+ import type * as Effect from "effect-app/Effect"
3
+ import type * as Scope from "effect/Scope"
2
4
  import { RequestContext } from "../RequestContext.js"
3
5
 
4
6
  export interface QueueBase<Evt, DrainEvt> {
@@ -1,16 +1,17 @@
1
- import { S, ServiceMap } from "effect-app"
1
+ import * as Context from "effect-app/Context"
2
2
  import { UserProfileId } from "effect-app/ids"
3
+ import * as S from "effect-app/Schema"
3
4
  import { NonEmptyString255 } from "effect-app/Schema"
4
5
 
5
- export const Locale = S.Literal("en", "de")
6
+ export const Locale = S.Literals(["en", "de"])
6
7
  export type Locale = typeof Locale.Type
7
8
 
8
- export class LocaleRef extends ServiceMap.Reference("Locale", { defaultValue: (): Locale => "en" }) {}
9
+ export class LocaleRef extends Context.Reference("Locale", { defaultValue: (): Locale => "en" }) {}
9
10
 
10
- export class RequestContext extends S.ExtendedClass<
11
+ export class RequestContext extends S.Opaque<
11
12
  RequestContext,
12
13
  RequestContext.Encoded
13
- >("RequestContext")({
14
+ >()(S.Struct({
14
15
  span: S.Struct({
15
16
  traceId: S.String,
16
17
  spanId: S.String,
@@ -22,8 +23,8 @@ export class RequestContext extends S.ExtendedClass<
22
23
  namespace: NonEmptyString255,
23
24
  /** @deprecated */
24
25
  userProfile: S.optional(S.Struct({ sub: UserProfileId })) //
25
- }) {
26
- // static Tag = ServiceMap.Tag<RequestContext>()
26
+ })) {
27
+ // static Tag = Context.Tag<RequestContext>()
27
28
 
28
29
  static toMonitoring(this: void, self: RequestContext) {
29
30
  return {
@@ -34,16 +35,16 @@ export class RequestContext extends S.ExtendedClass<
34
35
  }
35
36
 
36
37
  export const spanAttributes = (ctx: Pick<RequestContext, "locale" | "namespace"> & Partial<RequestContext>) => ({
37
- "request.name": ctx.name,
38
- "request.locale": ctx.locale,
39
- "request.namespace": ctx.namespace,
40
- ...ctx.sourceId ? { "request.source.id": ctx.sourceId } : {},
38
+ "code.function.name": ctx.name,
39
+ "app.locale": ctx.locale,
40
+ "app.tenant.id": ctx.namespace,
41
+ ...ctx.sourceId ? { "client.id": ctx.sourceId } : {},
41
42
  ...(ctx.userProfile?.sub
42
43
  ? {
43
- "request.user.sub": ctx
44
+ "user.id": ctx
44
45
  .userProfile
45
46
  .sub,
46
- "request.user.roles": "roles" in ctx
47
+ "user.roles": "roles" in ctx
47
48
  .userProfile
48
49
  ? ctx.userProfile.roles
49
50
  : undefined
@@ -53,11 +54,9 @@ export const spanAttributes = (ctx: Pick<RequestContext, "locale" | "namespace">
53
54
 
54
55
  // codegen:start {preset: model}
55
56
  //
56
- /* eslint-disable */
57
57
  export namespace RequestContext {
58
- export interface Encoded extends S.Struct.Encoded<typeof RequestContext["fields"]> {}
58
+ export interface Encoded extends S.StructNestedEncoded<typeof RequestContext> {}
59
59
  }
60
- /* eslint-enable */
61
60
  //
62
61
  // codegen:end
63
62
  //
@@ -1,5 +1,11 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { Effect, Fiber, FiberSet, Layer, Option, ServiceMap, type Tracer } from "effect-app"
2
+ import * as Context from "effect-app/Context"
3
+ import * as Effect from "effect-app/Effect"
4
+ import * as Layer from "effect-app/Layer"
5
+ import * as Option from "effect-app/Option"
6
+ import * as Fiber from "effect/Fiber"
7
+ import * as FiberSet from "effect/FiberSet"
8
+ import type * as Tracer from "effect/Tracer"
3
9
  import { reportRequestError, reportUnknownRequestError } from "./api/reportError.js"
4
10
  import { InfraLogger } from "./logger.js"
5
11
 
@@ -92,7 +98,7 @@ const make = Effect.gen(function*() {
92
98
  * Whenever you fork a fiber for a Request, and you want to prevent dependent services to close prematurely on interruption,
93
99
  * like the ServiceBus Sender, you should register these fibers in this FiberSet.
94
100
  */
95
- export class RequestFiberSet extends ServiceMap.Service<RequestFiberSet>()("RequestFiberSet", { make }) {
101
+ export class RequestFiberSet extends Context.Service<RequestFiberSet>()("RequestFiberSet", { make }) {
96
102
  static readonly Live = Layer.effect(this, this.make)
97
103
  static readonly register = <A, E, R>(self: Effect.Effect<A, E, R>) =>
98
104
  this.asEffect().pipe(Effect.andThen((_) => _.register(self)))
@@ -1,4 +1,10 @@
1
- import { Data, Effect, Layer, ServiceMap } from "effect-app"
1
+ import * as Context from "effect-app/Context"
2
+ import * as Effect from "effect-app/Effect"
3
+ import * as Layer from "effect-app/Layer"
4
+ import * as Data from "effect/Data"
5
+ import { dual } from "effect/Function"
6
+ import type * as Request from "effect/Request"
7
+ import * as RequestResolver from "effect/RequestResolver"
2
8
  import { ContextMap } from "./service.js"
3
9
 
4
10
  // TODO: we have to create a new contextmap on every request.
@@ -7,7 +13,7 @@ import { ContextMap } from "./service.js"
7
13
  // we can call another start after startup. but it would be even better if we could Die on accessing rootmap
8
14
  // we could also make the ContextMap optional, and when missing, issue a warning instead?
9
15
 
10
- export class ContextMapContainer extends ServiceMap.Reference("ContextMapContainer", {
16
+ export class ContextMapContainer extends Context.Reference("ContextMapContainer", {
11
17
  defaultValue: (): ContextMap | "root" => "root"
12
18
  }) {
13
19
  static readonly layer = Layer.effect(this, ContextMap.make.pipe(Effect.map(ContextMap.of)))
@@ -18,3 +24,40 @@ export class ContextMapNotStartedError extends Data.TaggedError("ContextMapNotSt
18
24
  export const getContextMap = ContextMapContainer.asEffect().pipe(
19
25
  Effect.filterOrFail((_) => _ !== "root", () => new ContextMapNotStartedError())
20
26
  )
27
+
28
+ /**
29
+ * Uses the official `RequestResolver.withCache` internally,
30
+ * creating one cached resolver per ContextMap (i.e. per request).
31
+ * Uses a shared semaphore in the ContextMap to ensure safe single initialization.
32
+ */
33
+ export const withRequestResolverCache: {
34
+ <A extends Request.Request<any, any>>(options: {
35
+ readonly capacity: number
36
+ readonly strategy?: "lru" | "fifo" | undefined
37
+ }): (
38
+ self: RequestResolver.RequestResolver<A>
39
+ ) => Effect.Effect<RequestResolver.RequestResolver<A>, ContextMapNotStartedError>
40
+ <A extends Request.Request<any, any>>(
41
+ self: RequestResolver.RequestResolver<A>,
42
+ options: {
43
+ readonly capacity: number
44
+ readonly strategy?: "lru" | "fifo" | undefined
45
+ }
46
+ ): Effect.Effect<RequestResolver.RequestResolver<A>, ContextMapNotStartedError>
47
+ } = dual(2, <A extends Request.Request<any, any>>(
48
+ self: RequestResolver.RequestResolver<A>,
49
+ options: {
50
+ readonly capacity: number
51
+ readonly strategy?: "lru" | "fifo" | undefined
52
+ }
53
+ ): Effect.Effect<RequestResolver.RequestResolver<A>, ContextMapNotStartedError> => {
54
+ const cacheKey = Symbol()
55
+ return getContextMap.pipe(
56
+ Effect.flatMap((ctxMap) =>
57
+ ctxMap.getOrCreateStoreEffect(
58
+ cacheKey,
59
+ RequestResolver.withCache(self, options)
60
+ )
61
+ )
62
+ )
63
+ })