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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/CHANGELOG.md +1514 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +15 -7
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +10 -12
  6. package/dist/Emailer/Sendgrid.d.ts +14 -14
  7. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  8. package/dist/Emailer/Sendgrid.js +16 -15
  9. package/dist/Emailer/fake.d.ts +1 -1
  10. package/dist/Emailer/service.d.ts +11 -5
  11. package/dist/Emailer/service.d.ts.map +1 -1
  12. package/dist/Emailer/service.js +3 -3
  13. package/dist/Emailer.d.ts +1 -1
  14. package/dist/MainFiberSet.d.ts +9 -9
  15. package/dist/MainFiberSet.d.ts.map +1 -1
  16. package/dist/MainFiberSet.js +3 -3
  17. package/dist/Model/Repository/Registry.d.ts +20 -0
  18. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  19. package/dist/Model/Repository/Registry.js +17 -0
  20. package/dist/Model/Repository/ext.d.ts +33 -15
  21. package/dist/Model/Repository/ext.d.ts.map +1 -1
  22. package/dist/Model/Repository/ext.js +54 -2
  23. package/dist/Model/Repository/internal/internal.d.ts +6 -6
  24. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  25. package/dist/Model/Repository/internal/internal.js +43 -32
  26. package/dist/Model/Repository/legacy.d.ts +1 -1
  27. package/dist/Model/Repository/makeRepo.d.ts +7 -6
  28. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  29. package/dist/Model/Repository/makeRepo.js +5 -1
  30. package/dist/Model/Repository/service.d.ts +28 -23
  31. package/dist/Model/Repository/service.d.ts.map +1 -1
  32. package/dist/Model/Repository/validation.d.ts +142 -17
  33. package/dist/Model/Repository/validation.d.ts.map +1 -1
  34. package/dist/Model/Repository/validation.js +5 -5
  35. package/dist/Model/Repository.d.ts +2 -1
  36. package/dist/Model/Repository.d.ts.map +1 -1
  37. package/dist/Model/Repository.js +2 -1
  38. package/dist/Model/dsl.d.ts +4 -4
  39. package/dist/Model/dsl.d.ts.map +1 -1
  40. package/dist/Model/filter/filterApi.d.ts +5 -5
  41. package/dist/Model/filter/filterApi.d.ts.map +1 -1
  42. package/dist/Model/filter/types/errors.d.ts +1 -1
  43. package/dist/Model/filter/types/fields.d.ts +1 -1
  44. package/dist/Model/filter/types/path/common.d.ts +1 -1
  45. package/dist/Model/filter/types/path/eager.d.ts +1 -1
  46. package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
  47. package/dist/Model/filter/types/path/eager.js +1 -1
  48. package/dist/Model/filter/types/path/index.d.ts +1 -1
  49. package/dist/Model/filter/types/utils.d.ts +1 -1
  50. package/dist/Model/filter/types/validator.d.ts +1 -1
  51. package/dist/Model/filter/types.d.ts +1 -1
  52. package/dist/Model/query/dsl.d.ts +25 -25
  53. package/dist/Model/query/dsl.d.ts.map +1 -1
  54. package/dist/Model/query/new-kid-interpreter.d.ts +6 -6
  55. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  56. package/dist/Model/query/new-kid-interpreter.js +3 -3
  57. package/dist/Model/query.d.ts +1 -1
  58. package/dist/Model.d.ts +2 -1
  59. package/dist/Model.d.ts.map +1 -1
  60. package/dist/Model.js +2 -1
  61. package/dist/QueueMaker/SQLQueue.d.ts +6 -8
  62. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  63. package/dist/QueueMaker/SQLQueue.js +106 -115
  64. package/dist/QueueMaker/errors.d.ts +2 -2
  65. package/dist/QueueMaker/errors.d.ts.map +1 -1
  66. package/dist/QueueMaker/memQueue.d.ts +7 -4
  67. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  68. package/dist/QueueMaker/memQueue.js +52 -62
  69. package/dist/QueueMaker/sbqueue.d.ts +6 -3
  70. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  71. package/dist/QueueMaker/sbqueue.js +39 -53
  72. package/dist/QueueMaker/service.d.ts +1 -1
  73. package/dist/RequestContext.d.ts +117 -31
  74. package/dist/RequestContext.d.ts.map +1 -1
  75. package/dist/RequestContext.js +7 -8
  76. package/dist/RequestFiberSet.d.ts +7 -7
  77. package/dist/RequestFiberSet.d.ts.map +1 -1
  78. package/dist/RequestFiberSet.js +5 -5
  79. package/dist/Store/ContextMapContainer.d.ts +20 -4
  80. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  81. package/dist/Store/ContextMapContainer.js +13 -3
  82. package/dist/Store/Cosmos/query.d.ts +1 -1
  83. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  84. package/dist/Store/Cosmos/query.js +10 -12
  85. package/dist/Store/Cosmos.d.ts +1 -1
  86. package/dist/Store/Cosmos.d.ts.map +1 -1
  87. package/dist/Store/Cosmos.js +318 -240
  88. package/dist/Store/Disk.d.ts +2 -2
  89. package/dist/Store/Disk.d.ts.map +1 -1
  90. package/dist/Store/Disk.js +25 -22
  91. package/dist/Store/Memory.d.ts +4 -4
  92. package/dist/Store/Memory.d.ts.map +1 -1
  93. package/dist/Store/Memory.js +27 -22
  94. package/dist/Store/SQL/Pg.d.ts +4 -0
  95. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  96. package/dist/Store/SQL/Pg.js +189 -0
  97. package/dist/Store/SQL/query.d.ts +38 -0
  98. package/dist/Store/SQL/query.d.ts.map +1 -0
  99. package/dist/Store/SQL/query.js +367 -0
  100. package/dist/Store/SQL.d.ts +20 -0
  101. package/dist/Store/SQL.d.ts.map +1 -0
  102. package/dist/Store/SQL.js +381 -0
  103. package/dist/Store/codeFilter.d.ts +1 -1
  104. package/dist/Store/codeFilter.d.ts.map +1 -1
  105. package/dist/Store/codeFilter.js +2 -1
  106. package/dist/Store/index.d.ts +5 -2
  107. package/dist/Store/index.d.ts.map +1 -1
  108. package/dist/Store/index.js +15 -3
  109. package/dist/Store/service.d.ts +18 -7
  110. package/dist/Store/service.d.ts.map +1 -1
  111. package/dist/Store/service.js +24 -6
  112. package/dist/Store/utils.d.ts +1 -1
  113. package/dist/Store/utils.d.ts.map +1 -1
  114. package/dist/Store/utils.js +3 -4
  115. package/dist/Store.d.ts +1 -1
  116. package/dist/adapters/SQL/Model.d.ts +30 -47
  117. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  118. package/dist/adapters/SQL/Model.js +22 -14
  119. package/dist/adapters/SQL.d.ts +1 -1
  120. package/dist/adapters/ServiceBus.d.ts +11 -11
  121. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  122. package/dist/adapters/ServiceBus.js +15 -17
  123. package/dist/adapters/cosmos-client.d.ts +3 -3
  124. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  125. package/dist/adapters/cosmos-client.js +3 -3
  126. package/dist/adapters/index.d.ts +8 -2
  127. package/dist/adapters/index.d.ts.map +1 -1
  128. package/dist/adapters/index.js +8 -2
  129. package/dist/adapters/logger.d.ts +2 -2
  130. package/dist/adapters/logger.d.ts.map +1 -1
  131. package/dist/adapters/memQueue.d.ts +3 -3
  132. package/dist/adapters/memQueue.d.ts.map +1 -1
  133. package/dist/adapters/memQueue.js +3 -3
  134. package/dist/adapters/mongo-client.d.ts +3 -3
  135. package/dist/adapters/mongo-client.d.ts.map +1 -1
  136. package/dist/adapters/mongo-client.js +3 -3
  137. package/dist/adapters/redis-client.d.ts +4 -4
  138. package/dist/adapters/redis-client.d.ts.map +1 -1
  139. package/dist/adapters/redis-client.js +3 -3
  140. package/dist/api/ContextProvider.d.ts +8 -8
  141. package/dist/api/ContextProvider.d.ts.map +1 -1
  142. package/dist/api/ContextProvider.js +6 -6
  143. package/dist/api/codec.d.ts +1 -1
  144. package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
  145. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  146. package/dist/api/internal/RequestContextMiddleware.js +2 -2
  147. package/dist/api/internal/auth.d.ts +45 -7
  148. package/dist/api/internal/auth.d.ts.map +1 -1
  149. package/dist/api/internal/auth.js +160 -29
  150. package/dist/api/internal/events.d.ts +3 -3
  151. package/dist/api/internal/events.d.ts.map +1 -1
  152. package/dist/api/internal/events.js +12 -8
  153. package/dist/api/internal/health.d.ts +1 -1
  154. package/dist/api/layerUtils.d.ts +6 -6
  155. package/dist/api/layerUtils.d.ts.map +1 -1
  156. package/dist/api/layerUtils.js +5 -5
  157. package/dist/api/middlewares.d.ts +1 -1
  158. package/dist/api/reportError.d.ts +1 -1
  159. package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
  160. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  161. package/dist/api/routing/middleware/middleware.d.ts +50 -4
  162. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  163. package/dist/api/routing/middleware/middleware.js +79 -17
  164. package/dist/api/routing/middleware.d.ts +1 -2
  165. package/dist/api/routing/middleware.d.ts.map +1 -1
  166. package/dist/api/routing/middleware.js +1 -2
  167. package/dist/api/routing/schema/jwt.d.ts +2 -2
  168. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  169. package/dist/api/routing/schema/jwt.js +1 -1
  170. package/dist/api/routing/tsort.d.ts +1 -1
  171. package/dist/api/routing/tsort.d.ts.map +1 -1
  172. package/dist/api/routing/utils.d.ts +3 -3
  173. package/dist/api/routing/utils.d.ts.map +1 -1
  174. package/dist/api/routing.d.ts +32 -35
  175. package/dist/api/routing.d.ts.map +1 -1
  176. package/dist/api/routing.js +84 -36
  177. package/dist/api/setupRequest.d.ts +8 -5
  178. package/dist/api/setupRequest.d.ts.map +1 -1
  179. package/dist/api/setupRequest.js +14 -9
  180. package/dist/api/util.d.ts +1 -1
  181. package/dist/arbs.d.ts +1 -1
  182. package/dist/arbs.d.ts.map +1 -1
  183. package/dist/arbs.js +5 -3
  184. package/dist/errorReporter.d.ts +5 -5
  185. package/dist/errorReporter.d.ts.map +1 -1
  186. package/dist/errorReporter.js +20 -25
  187. package/dist/errors.d.ts +1 -1
  188. package/dist/fileUtil.d.ts +1 -1
  189. package/dist/fileUtil.d.ts.map +1 -1
  190. package/dist/fileUtil.js +3 -2
  191. package/dist/index.d.ts +1 -1
  192. package/dist/logger/jsonLogger.d.ts +1 -1
  193. package/dist/logger/logFmtLogger.d.ts +1 -1
  194. package/dist/logger/shared.d.ts +1 -1
  195. package/dist/logger/shared.js +2 -2
  196. package/dist/logger.d.ts +1 -1
  197. package/dist/logger.d.ts.map +1 -1
  198. package/dist/rateLimit.d.ts +9 -3
  199. package/dist/rateLimit.d.ts.map +1 -1
  200. package/dist/rateLimit.js +5 -11
  201. package/dist/test.d.ts +2 -2
  202. package/dist/test.d.ts.map +1 -1
  203. package/dist/test.js +1 -1
  204. package/dist/vitest.d.ts +1 -1
  205. package/examples/query.ts +39 -35
  206. package/package.json +41 -37
  207. package/src/CUPS.ts +9 -11
  208. package/src/Emailer/Sendgrid.ts +18 -15
  209. package/src/Emailer/service.ts +9 -3
  210. package/src/MainFiberSet.ts +5 -6
  211. package/src/Model/Repository/Registry.ts +33 -0
  212. package/src/Model/Repository/ext.ts +96 -10
  213. package/src/Model/Repository/internal/internal.ts +97 -88
  214. package/src/Model/Repository/makeRepo.ts +12 -10
  215. package/src/Model/Repository/service.ts +31 -22
  216. package/src/Model/Repository/validation.ts +4 -4
  217. package/src/Model/Repository.ts +1 -0
  218. package/src/Model/dsl.ts +3 -3
  219. package/src/Model/filter/types/path/eager.ts +1 -2
  220. package/src/Model/query/dsl.ts +18 -18
  221. package/src/Model/query/new-kid-interpreter.ts +2 -2
  222. package/src/Model.ts +1 -0
  223. package/src/QueueMaker/SQLQueue.ts +123 -154
  224. package/src/QueueMaker/memQueue.ts +85 -107
  225. package/src/QueueMaker/sbqueue.ts +54 -81
  226. package/src/RequestContext.ts +8 -10
  227. package/src/RequestFiberSet.ts +4 -4
  228. package/src/Store/ContextMapContainer.ts +41 -2
  229. package/src/Store/Cosmos/query.ts +16 -20
  230. package/src/Store/Cosmos.ts +452 -342
  231. package/src/Store/Disk.ts +52 -49
  232. package/src/Store/Memory.ts +55 -51
  233. package/src/Store/SQL/Pg.ts +318 -0
  234. package/src/Store/SQL/query.ts +409 -0
  235. package/src/Store/SQL.ts +668 -0
  236. package/src/Store/codeFilter.ts +1 -0
  237. package/src/Store/index.ts +17 -2
  238. package/src/Store/service.ts +32 -8
  239. package/src/Store/utils.ts +23 -22
  240. package/src/adapters/SQL/Model.ts +83 -72
  241. package/src/adapters/ServiceBus.ts +114 -118
  242. package/src/adapters/cosmos-client.ts +2 -2
  243. package/src/adapters/index.ts +7 -0
  244. package/src/adapters/memQueue.ts +2 -2
  245. package/src/adapters/mongo-client.ts +2 -2
  246. package/src/adapters/redis-client.ts +2 -2
  247. package/src/api/ContextProvider.ts +12 -13
  248. package/src/api/internal/RequestContextMiddleware.ts +1 -1
  249. package/src/api/internal/auth.ts +246 -44
  250. package/src/api/internal/events.ts +15 -10
  251. package/src/api/layerUtils.ts +8 -8
  252. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  253. package/src/api/routing/middleware/middleware.ts +112 -15
  254. package/src/api/routing/middleware.ts +0 -2
  255. package/src/api/routing/schema/jwt.ts +2 -3
  256. package/src/api/routing.ts +153 -79
  257. package/src/api/setupRequest.ts +30 -10
  258. package/src/arbs.ts +4 -2
  259. package/src/errorReporter.ts +63 -75
  260. package/src/fileUtil.ts +2 -1
  261. package/src/logger/shared.ts +1 -1
  262. package/src/rateLimit.ts +30 -22
  263. package/src/test.ts +1 -1
  264. package/test/auth.test.ts +101 -0
  265. package/test/contextProvider.test.ts +11 -11
  266. package/test/controller.test.ts +27 -21
  267. package/test/dist/auth.test.d.ts.map +1 -0
  268. package/test/dist/contextProvider.test.d.ts.map +1 -1
  269. package/test/dist/controller.test.d.ts.map +1 -1
  270. package/test/dist/fixtures.d.ts +26 -12
  271. package/test/dist/fixtures.d.ts.map +1 -1
  272. package/test/dist/fixtures.js +12 -10
  273. package/test/dist/query.test.d.ts.map +1 -1
  274. package/test/dist/rawQuery.test.d.ts.map +1 -1
  275. package/test/dist/repository-ext.test.d.ts.map +1 -0
  276. package/test/dist/requires.test.d.ts.map +1 -1
  277. package/test/dist/router-generator.test.d.ts.map +1 -0
  278. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  279. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  280. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  281. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  282. package/test/dist/sql-store.test.d.ts.map +1 -0
  283. package/test/fixtures.ts +11 -9
  284. package/test/query.test.ts +216 -36
  285. package/test/rawQuery.test.ts +23 -19
  286. package/test/repository-ext.test.ts +60 -0
  287. package/test/requires.test.ts +6 -6
  288. package/test/router-generator.test.ts +180 -0
  289. package/test/routing-interruptibility.test.ts +63 -0
  290. package/test/rpc-e2e-invalidation.test.ts +507 -0
  291. package/test/rpc-multi-middleware.test.ts +79 -10
  292. package/test/rpc-stream-fullstack.test.ts +325 -0
  293. package/test/sql-store.test.ts +1064 -0
  294. package/test/validateSample.test.ts +15 -12
  295. package/tsconfig.examples.json +1 -1
  296. package/tsconfig.json +0 -1
  297. package/tsconfig.json.bak +2 -2
  298. package/tsconfig.src.json +35 -35
  299. package/tsconfig.test.json +2 -2
  300. package/dist/Operations.d.ts +0 -55
  301. package/dist/Operations.d.ts.map +0 -1
  302. package/dist/Operations.js +0 -102
  303. package/dist/OperationsRepo.d.ts +0 -41
  304. package/dist/OperationsRepo.d.ts.map +0 -1
  305. package/dist/OperationsRepo.js +0 -14
  306. package/eslint.config.mjs +0 -24
  307. package/src/Operations.ts +0 -235
  308. package/src/OperationsRepo.ts +0 -16
@@ -1,15 +1,14 @@
1
1
  import { Cause, Tracer } from "effect"
2
- import { Effect, Fiber, flow, S } from "effect-app"
3
- import * as Q from "effect/Queue"
2
+ import { Effect, Fiber, flow, type NonEmptyReadonlyArray, S } from "effect-app"
4
3
  import { pretty } from "effect-app/utils"
4
+ 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
8
  import { reportNonInterruptedFailure, reportNonInterruptedFailureCause } from "./errors.js"
9
- import type { NonEmptyReadonlyArray } from "effect-app"
10
- import { type QueueBase, QueueMeta } from "./service.js"
9
+ import { QueueMeta } from "./service.js"
11
10
 
12
- export function makeMemQueue<
11
+ export const makeMemQueue = Effect.fnUntraced(function*<
13
12
  Evt extends { id: S.StringId; _tag: string },
14
13
  DrainEvt extends { id: S.StringId; _tag: string },
15
14
  EvtE,
@@ -20,112 +19,91 @@ export function makeMemQueue<
20
19
  schema: S.Codec<Evt, EvtE>,
21
20
  drainSchema: S.Codec<DrainEvt, DrainEvtE>
22
21
  ) {
23
- return Effect.gen(function*() {
24
- const mem = yield* MemQueue
25
- const q = yield* mem.getOrCreateQueue(queueName)
26
- const qDrain = yield* mem.getOrCreateQueue(queueDrainName)
22
+ const mem = yield* MemQueue
23
+ const q = yield* mem.getOrCreateQueue(queueName)
24
+ const qDrain = yield* mem.getOrCreateQueue(queueDrainName)
27
25
 
28
- const wireSchema = S.Struct({ body: schema, meta: QueueMeta })
29
- const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
30
- const parseDrain = flow(S.decodeUnknownEffect(drainW), Effect.orDie)
26
+ const wireSchema = S.Struct({ body: schema, meta: QueueMeta })
27
+ const wireSchemaJson = S.fromJsonString(S.toCodecJson(wireSchema))
28
+ const encodePublish = S.encodeEffect(wireSchemaJson)
29
+ const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
30
+ const drainWJson = S.fromJsonString(S.toCodecJson(drainW))
31
31
 
32
- const queue = {
33
- publish: (...messages: NonEmptyReadonlyArray<Evt>) =>
34
- getRequestContext
35
- .pipe(
36
- Effect.flatMap((requestContext) =>
37
- Effect
38
- .forEach(messages, (m) =>
39
- // we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
40
- S.encodeEffect(wireSchema)({ body: m, meta: requestContext }).pipe(
41
- Effect.orDie,
42
- Effect
43
- .map(JSON.stringify),
44
- // .tap((msg) => info("Publishing Mem Message: " + utils.inspect(msg)))
45
- Effect.flatMap((_) => Q.offer(q, _))
46
- ), { discard: true })
47
- ),
48
- Effect.withSpan("queue.publish: " + queueName, {
49
- kind: "producer",
50
- attributes: { "message_tags": messages.map((_) => _._tag) }
51
- }, { captureStackTrace: false })
32
+ const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
33
+
34
+ const queue = {
35
+ publish: Effect.fn("queue.publish: " + queueName, { kind: "producer" })(function*(
36
+ ...messages: NonEmptyReadonlyArray<Evt>
37
+ ) {
38
+ yield* Effect.annotateCurrentSpan({ "message_tags": messages.map((_) => _._tag) })
39
+ const requestContext = yield* getRequestContext
40
+ // we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
41
+ yield* Effect.forEach(
42
+ messages,
43
+ (m) =>
44
+ encodePublish({ body: m, meta: requestContext }).pipe(
45
+ Effect.orDie,
46
+ Effect.flatMap((_) => Q.offer(q, _))
52
47
  ),
53
- drain: <DrainE, DrainR>(
54
- handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
55
- sessionId?: string
56
- ) => {
57
- const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
58
- const reportError = reportNonInterruptedFailureCause({ name: "MemQueue.drain." + queueDrainName })
59
- const processMessage = (msg: string) =>
60
- // we JSON parse, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
61
- Effect
62
- .sync(() => JSON.parse(msg))
63
- .pipe(
64
- Effect.flatMap(parseDrain),
65
- Effect.orDie,
66
- Effect
67
- .flatMap(({ body, meta }) => {
68
- let effect = InfraLogger
69
- .logDebug(`[${queueDrainName}] Processing incoming message`)
70
- .pipe(
71
- Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
72
- Effect.andThen(handleEvent(body)),
73
- silenceAndReportError,
74
- (_) =>
75
- setupRequestContextWithCustomSpan(
76
- _,
77
- meta,
78
- `queue.drain: ${queueDrainName}.${body._tag}`,
79
- {
80
- captureStackTrace: false,
81
- kind: "consumer",
82
- attributes: {
83
- "queue.name": queueDrainName,
84
- "queue.sessionId": sessionId,
85
- "queue.input": body
86
- }
87
- }
88
- )
89
- )
90
- if (meta.span) {
91
- effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
92
- }
93
- return effect
94
- })
95
- )
96
- return Q.take(qDrain)
48
+ { discard: true }
49
+ )
50
+ }),
51
+ drain: <DrainE, DrainR>(
52
+ handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
53
+ sessionId?: string
54
+ ) => {
55
+ const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
56
+ const reportError = reportNonInterruptedFailureCause({ name: "MemQueue.drain." + queueDrainName })
57
+ const processMessage = Effect.fnUntraced(function*(msg: string) {
58
+ // we JSON parse, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
59
+ const { body, meta } = yield* parseDrain(msg).pipe(Effect.orDie)
60
+ let effect = InfraLogger
61
+ .logDebug(`[${queueDrainName}] Processing incoming message`)
97
62
  .pipe(
98
- Effect
99
- .flatMap((x) =>
100
- processMessage(x).pipe(
101
- Effect.uninterruptible,
102
- Effect.forkChild,
103
- Effect.flatMap(Fiber.join),
104
- // normally a failed item would be returned to the queue and retried up to X times.
105
- Effect.flatMap((_) =>
106
- _._tag === "Failure" && !Cause.hasInterruptsOnly(_.cause)
107
- ? Q.offer(qDrain, x).pipe(
108
- // TODO: retry count tracking and max retries.
109
- Effect.delay("5 seconds"),
110
- Effect.tapCause(reportError),
111
- Effect.forkDetach
112
- )
113
- : Effect.void
114
- )
115
- )
116
- ),
63
+ Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
64
+ Effect.andThen(handleEvent(body)),
117
65
  silenceAndReportError,
118
- Effect.withSpan(`queue.drain: ${queueDrainName}`, {
119
- attributes: {
120
- "queue.type": "mem",
121
- "queue.name": queueDrainName,
122
- "queue.sessionId": sessionId
123
- }
124
- }),
125
- Effect.forever
66
+ (_) =>
67
+ setupRequestContextWithCustomSpan(
68
+ _,
69
+ meta,
70
+ `queue.drain: ${queueDrainName}.${body._tag}`,
71
+ {
72
+ captureStackTrace: false,
73
+ kind: "consumer",
74
+ attributes: {
75
+ "queue.name": queueDrainName,
76
+ "queue.sessionId": sessionId,
77
+ "queue.input": body
78
+ }
79
+ }
80
+ )
81
+ )
82
+ if (meta.span) {
83
+ effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
84
+ }
85
+ return yield* effect
86
+ })
87
+ return Effect.fn(`queue.drain: ${queueDrainName}`, {
88
+ attributes: { "queue.type": "mem", "queue.name": queueDrainName, "queue.sessionId": sessionId }
89
+ })(function*() {
90
+ const x = yield* Q.take(qDrain)
91
+ const exit = yield* processMessage(x).pipe(
92
+ Effect.uninterruptible,
93
+ Effect.forkChild,
94
+ Effect.flatMap(Fiber.join)
95
+ )
96
+ if (exit._tag === "Failure" && !Cause.hasInterruptsOnly(exit.cause)) {
97
+ // normally a failed item would be returned to the queue and retried up to X times.
98
+ yield* Q.offer(qDrain, x).pipe(
99
+ // TODO: retry count tracking and max retries.
100
+ Effect.delay("5 seconds"),
101
+ Effect.tapCause(reportError),
102
+ Effect.forkDetach
126
103
  )
127
- }
104
+ }
105
+ }, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
128
106
  }
129
- return queue as QueueBase<Evt, DrainEvt>
130
- })
131
- }
107
+ }
108
+ return queue
109
+ })
@@ -1,13 +1,12 @@
1
1
  import { Tracer } from "effect"
2
- import { Cause, Effect, flow, S } from "effect-app"
2
+ import { Cause, Effect, flow, type NonEmptyReadonlyArray, S } from "effect-app"
3
3
  import type { StringId } from "effect-app/Schema"
4
4
  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
8
  import { reportNonInterruptedFailure, reportNonInterruptedFailureCause, reportQueueError } from "./errors.js"
9
- import type { NonEmptyReadonlyArray } from "effect-app"
10
- import { type QueueBase, QueueMeta } from "./service.js"
9
+ import { QueueMeta } from "./service.js"
11
10
 
12
11
  export function makeServiceBusQueue<
13
12
  Evt extends { id: StringId; _tag: string },
@@ -22,8 +21,11 @@ export function makeServiceBusQueue<
22
21
  body: schema,
23
22
  meta: QueueMeta
24
23
  })
24
+ const wireSchemaJson = S.fromJsonString(S.toCodecJson(wireSchema))
25
+ const encodePublish = S.encodeEffect(wireSchemaJson)
25
26
  const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
26
- const parseDrain = flow(S.decodeUnknownEffect(drainW), Effect.orDie)
27
+ const drainWJson = S.fromJsonString(S.toCodecJson(drainW))
28
+ const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
27
29
 
28
30
  return Effect.gen(function*() {
29
31
  const sender = yield* Sender
@@ -40,95 +42,66 @@ export function makeServiceBusQueue<
40
42
  handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
41
43
  sessionId?: string
42
44
  ) => {
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- function processMessage(messageBody: any) {
45
- return Effect
46
- .sync(() => JSON.parse(messageBody))
45
+ const processMessage = Effect.fnUntraced(function*(messageBody: unknown) {
46
+ const { body, meta } = yield* parseDrain(messageBody).pipe(Effect.orDie)
47
+ let effect = InfraLogger
48
+ .logDebug(`[${receiver.name}] Processing incoming message`)
47
49
  .pipe(
48
- Effect.flatMap((x) => parseDrain(x)),
50
+ Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
51
+ Effect.andThen(handleEvent(body)),
49
52
  Effect.orDie,
50
- Effect
51
- .flatMap(({ body, meta }) => {
52
- let effect = InfraLogger
53
- .logDebug(`[${receiver.name}] Processing incoming message`)
54
- .pipe(
55
- Effect.annotateLogs({
56
- body: pretty(body),
57
- meta: pretty(meta)
58
- }),
59
- Effect.andThen(handleEvent(body)),
60
- Effect.orDie
61
- )
62
- // we silenceAndReportError here, so that the error is reported, and moves into the Exit.
63
- .pipe(
64
- silenceAndReportError,
65
- (_) =>
66
- setupRequestContextWithCustomSpan(
67
- _,
68
- meta,
69
- `queue.drain: ${receiver.name}${sessionId ? `#${sessionId}` : ""}.${body._tag}`,
70
- {
71
- captureStackTrace: false,
72
- kind: "consumer",
73
- attributes: {
74
- "queue.name": receiver.name,
75
- "queue.sessionId": sessionId,
76
- "queue.input": body
77
- }
78
- }
79
- )
80
- )
81
- if (meta.span) {
82
- effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
53
+ // we silenceAndReportError here, so that the error is reported, and moves into the Exit.
54
+ silenceAndReportError,
55
+ (_) =>
56
+ setupRequestContextWithCustomSpan(
57
+ _,
58
+ meta,
59
+ `queue.drain: ${receiver.name}${sessionId ? `#${sessionId}` : ""}.${body._tag}`,
60
+ {
61
+ captureStackTrace: false,
62
+ kind: "consumer",
63
+ attributes: {
64
+ "queue.name": receiver.name,
65
+ "queue.sessionId": sessionId,
66
+ "queue.input": body
67
+ }
83
68
  }
84
- return effect
85
- }),
86
- Effect
87
- // we reportError here, so that we report the error only, and keep flowing
88
- .tapCause(reportError),
89
- // we still need to flatten the Exit.
90
- Effect.flatMap((_) => _)
69
+ )
91
70
  )
92
- }
71
+ if (meta.span) {
72
+ effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
73
+ }
74
+ // we reportError here, so that we report the error only, and keep flowing
75
+ const exit = yield* Effect.tapCause(effect, reportError)
76
+ return yield* exit
77
+ })
93
78
 
94
79
  return receiver
95
80
  .subscribe({
96
81
  processMessage: (x) => processMessage(x.body).pipe(Effect.uninterruptible),
97
82
  processError: (err) => reportQueueError(Cause.fail(err.error))
98
- // Deferred.completeWith(
99
- // deferred,
100
- // reportFatalQueueError(Cause.fail(err.error))
101
- // .pipe(Effect.andThen(Effect.fail(err.error)))
102
- // )
103
83
  }, sessionId)
104
- // .pipe(Effect.andThen(Deferred.await(deferred).pipe(Effect.orDie))),
105
- .pipe(
106
- Effect.andThen(Effect.never)
107
- )
84
+ .pipe(Effect.andThen(Effect.never))
108
85
  },
109
86
 
110
- publish: (...messages: NonEmptyReadonlyArray<Evt>) =>
111
- getRequestContext
112
- .pipe(
113
- Effect.flatMap((requestContext) =>
114
- Effect.forEach(messages, (m) =>
115
- S.encodeEffect(wireSchema)({
116
- body: m,
117
- meta: requestContext
118
- }).pipe(Effect.orDie, Effect.map((encoded) => ({
119
- body: JSON.stringify(encoded),
120
- messageId: m.id, /* correllationid: requestId */
121
- contentType: "application/json",
122
- sessionId: "sessionId" in m ? m.sessionId as string : undefined as unknown as string // TODO: optional
123
- }))))
124
- .pipe(Effect.flatMap((msgs) => sender.sendMessages(msgs)))
125
- ),
126
- Effect.withSpan("queue.publish: " + sender.name, {
127
- kind: "producer",
128
- attributes: { "message_tags": messages.map((_) => _._tag) }
129
- }, { captureStackTrace: false })
130
- )
87
+ publish: Effect.fn("queue.publish: " + sender.name, {
88
+ kind: "producer"
89
+ })(function*(...messages: NonEmptyReadonlyArray<Evt>) {
90
+ yield* Effect.annotateCurrentSpan({ "message_tags": messages.map((_) => _._tag) })
91
+ const requestContext = yield* getRequestContext
92
+ const msgs = yield* Effect.forEach(messages, (m) =>
93
+ encodePublish({ body: m, meta: requestContext }).pipe(
94
+ Effect.orDie,
95
+ Effect.map((body) => ({
96
+ body,
97
+ messageId: m.id, /* correllationid: requestId */
98
+ contentType: "application/json",
99
+ sessionId: "sessionId" in m ? m.sessionId as string : undefined as unknown as string // TODO: optional
100
+ }))
101
+ ))
102
+ yield* sender.sendMessages(msgs)
103
+ })
131
104
  }
132
- return queue as QueueBase<Evt, DrainEvt>
105
+ return queue
133
106
  })
134
107
  }
@@ -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 {
@@ -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, 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
 
@@ -8,8 +8,8 @@ const getRootParentSpan = Effect.gen(function*() {
8
8
  Effect.catchTag("NoSuchElementError", () => Effect.succeed(null))
9
9
  )
10
10
  if (!span) return span
11
- while (span._tag === "Span" && span.parent !== undefined) {
12
- span = span.parent
11
+ while (span._tag === "Span" && Option.isSome(span.parent)) {
12
+ span = span.parent.value
13
13
  }
14
14
  return span
15
15
  })
@@ -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
+ })
@@ -279,16 +279,14 @@ export function buildWhereCosmosQuery3(
279
279
  query: `
280
280
  SELECT ${
281
281
  select
282
- ? `${
283
- select
284
- .map((s) =>
285
- typeof s === "string"
286
- ? dottedToAccess(s === idKey ? "f.id" : `f.${s}`) // x["y"} vs x.y, helps with reserved keywords like "value"
287
- : `ARRAY (SELECT ${s.subKeys.map((_) => dottedToAccess(`t.${_}`)).join(",")}
282
+ ? select
283
+ .map((s) =>
284
+ typeof s === "string"
285
+ ? dottedToAccess(s === idKey ? "f.id" : `f.${s}`) // x["y"} vs x.y, helps with reserved keywords like "value"
286
+ : `ARRAY (SELECT ${s.subKeys.map((_) => dottedToAccess(`t.${_}`)).join(",")}
288
287
  FROM t in ${dottedToAccess(`f.${s.key}`)}) AS ${s.key}`
289
- )
290
- .join(", ")
291
- }`
288
+ )
289
+ .join(", ")
292
290
  : "f"
293
291
  }
294
292
  FROM ${name} f
@@ -296,16 +294,14 @@ export function buildWhereCosmosQuery3(
296
294
  ${filter.length ? `WHERE (${print(filter, values.map((_) => _.value), null, false)})` : ""}
297
295
  ${order ? `ORDER BY ${order.map((_) => `${dottedToAccess(`f.${_.key}`)} ${_.direction}`).join(", ")}` : ""}
298
296
  ${skip !== undefined || limit !== undefined ? `OFFSET ${skip ?? 0} LIMIT ${limit ?? 999999}` : ""}`,
299
- parameters: [
300
- ...values
301
- .flatMap((x, i) =>
302
- [{
303
- name: `@v${i}`,
304
- value: x.value as any
305
- }]
306
- // TODO: only for arrays that are used with _ANY or _ALL
307
- .concat(Array.isArray(x.value) ? x.value.map((_, i2) => ({ name: `@v${i}__${i2}`, value: _ as any })) : [])
308
- )
309
- ]
297
+ parameters: values
298
+ .flatMap((x, i) =>
299
+ [{
300
+ name: `@v${i}`,
301
+ value: x.value as any
302
+ }]
303
+ // TODO: only for arrays that are used with _ANY or _ALL
304
+ .concat(Array.isArray(x.value) ? x.value.map((_, i2) => ({ name: `@v${i}__${i2}`, value: _ as any })) : [])
305
+ )
310
306
  }
311
307
  }