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

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