@effect-app/infra 4.0.0-beta.21 → 4.0.0-beta.210

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 (309) hide show
  1. package/CHANGELOG.md +1548 -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 +59 -41
  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 +16 -16
  53. package/dist/Model/query/dsl.d.ts.map +1 -1
  54. package/dist/Model/query/new-kid-interpreter.d.ts +6 -6
  55. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  56. package/dist/Model/query/new-kid-interpreter.js +3 -3
  57. package/dist/Model/query.d.ts +1 -1
  58. package/dist/Model.d.ts +2 -1
  59. package/dist/Model.d.ts.map +1 -1
  60. package/dist/Model.js +2 -1
  61. package/dist/QueueMaker/SQLQueue.d.ts +5 -7
  62. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  63. package/dist/QueueMaker/SQLQueue.js +130 -116
  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 +75 -63
  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 +52 -53
  72. package/dist/QueueMaker/service.d.ts +1 -1
  73. package/dist/RequestContext.d.ts +74 -35
  74. package/dist/RequestContext.d.ts.map +1 -1
  75. package/dist/RequestContext.js +13 -14
  76. package/dist/RequestFiberSet.d.ts +7 -7
  77. package/dist/RequestFiberSet.d.ts.map +1 -1
  78. package/dist/RequestFiberSet.js +3 -3
  79. package/dist/Store/ContextMapContainer.d.ts +19 -3
  80. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  81. package/dist/Store/ContextMapContainer.js +13 -3
  82. package/dist/Store/Cosmos/query.d.ts +1 -1
  83. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  84. package/dist/Store/Cosmos/query.js +10 -12
  85. package/dist/Store/Cosmos.d.ts +1 -1
  86. package/dist/Store/Cosmos.d.ts.map +1 -1
  87. package/dist/Store/Cosmos.js +335 -243
  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 +72 -35
  91. package/dist/Store/Memory.d.ts +6 -4
  92. package/dist/Store/Memory.d.ts.map +1 -1
  93. package/dist/Store/Memory.js +90 -57
  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 +231 -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 +464 -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 +4 -2
  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 +31 -42
  117. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  118. package/dist/adapters/SQL/Model.js +29 -38
  119. package/dist/adapters/SQL.d.ts +1 -1
  120. package/dist/adapters/ServiceBus.d.ts +11 -11
  121. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  122. package/dist/adapters/ServiceBus.js +13 -15
  123. package/dist/adapters/cosmos-client.d.ts +3 -3
  124. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  125. package/dist/adapters/cosmos-client.js +3 -3
  126. package/dist/adapters/index.d.ts +8 -2
  127. package/dist/adapters/index.d.ts.map +1 -1
  128. package/dist/adapters/index.js +8 -2
  129. package/dist/adapters/logger.d.ts +2 -2
  130. package/dist/adapters/logger.d.ts.map +1 -1
  131. package/dist/adapters/memQueue.d.ts +3 -3
  132. package/dist/adapters/memQueue.d.ts.map +1 -1
  133. package/dist/adapters/memQueue.js +3 -3
  134. package/dist/adapters/mongo-client.d.ts +3 -3
  135. package/dist/adapters/mongo-client.d.ts.map +1 -1
  136. package/dist/adapters/mongo-client.js +3 -3
  137. package/dist/adapters/redis-client.d.ts +3 -3
  138. package/dist/adapters/redis-client.d.ts.map +1 -1
  139. package/dist/adapters/redis-client.js +3 -3
  140. package/dist/api/ContextProvider.d.ts +8 -8
  141. package/dist/api/ContextProvider.d.ts.map +1 -1
  142. package/dist/api/ContextProvider.js +6 -6
  143. package/dist/api/codec.d.ts +1 -1
  144. package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
  145. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  146. package/dist/api/internal/RequestContextMiddleware.js +2 -2
  147. package/dist/api/internal/auth.d.ts +44 -6
  148. package/dist/api/internal/auth.d.ts.map +1 -1
  149. package/dist/api/internal/auth.js +160 -29
  150. package/dist/api/internal/events.d.ts +3 -3
  151. package/dist/api/internal/events.d.ts.map +1 -1
  152. package/dist/api/internal/events.js +10 -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 +39 -3
  162. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  163. package/dist/api/routing/middleware/middleware.js +48 -16
  164. package/dist/api/routing/middleware.d.ts +1 -2
  165. package/dist/api/routing/middleware.d.ts.map +1 -1
  166. package/dist/api/routing/middleware.js +1 -2
  167. package/dist/api/routing/schema/jwt.d.ts +1 -1
  168. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  169. package/dist/api/routing/tsort.d.ts +1 -1
  170. package/dist/api/routing/tsort.d.ts.map +1 -1
  171. package/dist/api/routing/utils.d.ts +3 -3
  172. package/dist/api/routing/utils.d.ts.map +1 -1
  173. package/dist/api/routing.d.ts +80 -37
  174. package/dist/api/routing.d.ts.map +1 -1
  175. package/dist/api/routing.js +112 -40
  176. package/dist/api/setupRequest.d.ts +8 -5
  177. package/dist/api/setupRequest.d.ts.map +1 -1
  178. package/dist/api/setupRequest.js +12 -7
  179. package/dist/api/util.d.ts +1 -1
  180. package/dist/arbs.d.ts +1 -1
  181. package/dist/arbs.d.ts.map +1 -1
  182. package/dist/arbs.js +5 -3
  183. package/dist/errorReporter.d.ts +4 -4
  184. package/dist/errorReporter.d.ts.map +1 -1
  185. package/dist/errorReporter.js +20 -25
  186. package/dist/errors.d.ts +1 -1
  187. package/dist/fileUtil.d.ts +1 -1
  188. package/dist/fileUtil.d.ts.map +1 -1
  189. package/dist/index.d.ts +1 -1
  190. package/dist/logger/jsonLogger.d.ts +1 -1
  191. package/dist/logger/logFmtLogger.d.ts +1 -1
  192. package/dist/logger/shared.d.ts +1 -1
  193. package/dist/logger/shared.js +2 -2
  194. package/dist/logger.d.ts +1 -1
  195. package/dist/logger.d.ts.map +1 -1
  196. package/dist/otel.d.ts +66 -0
  197. package/dist/otel.d.ts.map +1 -0
  198. package/dist/otel.js +56 -0
  199. package/dist/rateLimit.d.ts +9 -3
  200. package/dist/rateLimit.d.ts.map +1 -1
  201. package/dist/rateLimit.js +5 -11
  202. package/dist/test.d.ts +2 -2
  203. package/dist/test.d.ts.map +1 -1
  204. package/dist/test.js +1 -1
  205. package/dist/vitest.d.ts +1 -1
  206. package/examples/query.ts +39 -35
  207. package/package.json +45 -37
  208. package/src/CUPS.ts +9 -11
  209. package/src/Emailer/Sendgrid.ts +17 -14
  210. package/src/Emailer/service.ts +9 -3
  211. package/src/MainFiberSet.ts +5 -6
  212. package/src/Model/Repository/Registry.ts +33 -0
  213. package/src/Model/Repository/ext.ts +96 -10
  214. package/src/Model/Repository/internal/internal.ts +159 -139
  215. package/src/Model/Repository/makeRepo.ts +12 -10
  216. package/src/Model/Repository/service.ts +31 -22
  217. package/src/Model/Repository/validation.ts +4 -4
  218. package/src/Model/Repository.ts +1 -0
  219. package/src/Model/dsl.ts +3 -3
  220. package/src/Model/filter/types/path/eager.ts +1 -2
  221. package/src/Model/query/dsl.ts +18 -18
  222. package/src/Model/query/new-kid-interpreter.ts +2 -2
  223. package/src/Model.ts +1 -0
  224. package/src/QueueMaker/SQLQueue.ts +144 -152
  225. package/src/QueueMaker/memQueue.ts +104 -103
  226. package/src/QueueMaker/sbqueue.ts +70 -86
  227. package/src/RequestContext.ts +14 -16
  228. package/src/RequestFiberSet.ts +2 -2
  229. package/src/Store/ContextMapContainer.ts +41 -2
  230. package/src/Store/Cosmos/query.ts +16 -20
  231. package/src/Store/Cosmos.ts +473 -348
  232. package/src/Store/Disk.ts +102 -65
  233. package/src/Store/Memory.ts +118 -83
  234. package/src/Store/SQL/Pg.ts +352 -0
  235. package/src/Store/SQL/query.ts +409 -0
  236. package/src/Store/SQL.ts +734 -0
  237. package/src/Store/codeFilter.ts +3 -1
  238. package/src/Store/index.ts +17 -2
  239. package/src/Store/service.ts +32 -8
  240. package/src/Store/utils.ts +23 -22
  241. package/src/adapters/SQL/Model.ts +41 -40
  242. package/src/adapters/ServiceBus.ts +112 -116
  243. package/src/adapters/cosmos-client.ts +2 -2
  244. package/src/adapters/index.ts +7 -0
  245. package/src/adapters/memQueue.ts +2 -2
  246. package/src/adapters/mongo-client.ts +2 -2
  247. package/src/adapters/redis-client.ts +2 -2
  248. package/src/api/ContextProvider.ts +12 -13
  249. package/src/api/internal/RequestContextMiddleware.ts +1 -1
  250. package/src/api/internal/auth.ts +246 -44
  251. package/src/api/internal/events.ts +13 -9
  252. package/src/api/layerUtils.ts +8 -8
  253. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  254. package/src/api/routing/middleware/middleware.ts +55 -14
  255. package/src/api/routing/middleware.ts +0 -2
  256. package/src/api/routing.ts +298 -128
  257. package/src/api/setupRequest.ts +28 -8
  258. package/src/arbs.ts +4 -2
  259. package/src/errorReporter.ts +62 -74
  260. package/src/logger/shared.ts +1 -1
  261. package/src/otel.ts +141 -0
  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 +21 -30
  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/date-query.test.d.ts.map +1 -0
  271. package/test/dist/fixtures.d.ts +26 -12
  272. package/test/dist/fixtures.d.ts.map +1 -1
  273. package/test/dist/fixtures.js +12 -10
  274. package/test/dist/query.test.d.ts.map +1 -1
  275. package/test/dist/rawQuery.test.d.ts.map +1 -1
  276. package/test/dist/repository-ext.test.d.ts.map +1 -0
  277. package/test/dist/requires.test.d.ts.map +1 -1
  278. package/test/dist/router-generator.test.d.ts.map +1 -0
  279. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  280. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  281. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  282. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  283. package/test/dist/sql-store.test.d.ts.map +1 -0
  284. package/test/fixtures.ts +11 -9
  285. package/test/query.test.ts +216 -34
  286. package/test/rawQuery.test.ts +23 -19
  287. package/test/repository-ext.test.ts +60 -0
  288. package/test/requires.test.ts +6 -6
  289. package/test/router-generator.test.ts +183 -0
  290. package/test/routing-interruptibility.test.ts +63 -0
  291. package/test/rpc-e2e-invalidation.test.ts +251 -0
  292. package/test/rpc-multi-middleware.test.ts +78 -9
  293. package/test/rpc-stream-fullstack.test.ts +300 -0
  294. package/test/sql-store.test.ts +1064 -0
  295. package/test/validateSample.test.ts +15 -12
  296. package/tsconfig.examples.json +1 -1
  297. package/tsconfig.json +0 -1
  298. package/tsconfig.json.bak +2 -2
  299. package/tsconfig.src.json +35 -35
  300. package/tsconfig.test.json +2 -2
  301. package/dist/Operations.d.ts +0 -55
  302. package/dist/Operations.d.ts.map +0 -1
  303. package/dist/Operations.js +0 -102
  304. package/dist/OperationsRepo.d.ts +0 -41
  305. package/dist/OperationsRepo.d.ts.map +0 -1
  306. package/dist/OperationsRepo.js +0 -14
  307. package/eslint.config.mjs +0 -24
  308. package/src/Operations.ts +0 -235
  309. package/src/OperationsRepo.ts +0 -16
@@ -3,17 +3,18 @@ import { reportNonInterruptedFailure } from "@effect-app/infra/QueueMaker/errors
3
3
  import { type QueueBase, QueueMeta } from "@effect-app/infra/QueueMaker/service"
4
4
  import { subMinutes } from "date-fns"
5
5
  import { Effect, Fiber, type NonEmptyReadonlyArray, Option, S, Tracer } from "effect-app"
6
- import type { NonEmptyString255 } from "effect-app/Schema"
6
+ import { type NonEmptyString255 } from "effect-app/Schema"
7
7
  import { pretty } from "effect-app/utils"
8
8
  import { SqlClient } from "effect/unstable/sql"
9
9
  import { SQLModel } from "../adapters/SQL.js"
10
10
  import { InfraLogger } from "../logger.js"
11
+ import { messagingSpanArgs } from "../otel.js"
11
12
 
12
- export const QueueId = S.Number.pipe(S.brand("QueueId"))
13
+ export const QueueId = S.Finite.pipe(S.brand("QueueId"))
13
14
  export type QueueId = typeof QueueId.Type
14
15
 
15
16
  // TODO: let the model track and Auto Generate versionColumn on every update instead
16
- export function makeSQLQueue<
17
+ export const makeSQLQueue = Effect.fnUntraced(function*<
17
18
  Evt extends { id: S.StringId; _tag: string },
18
19
  DrainEvt extends { id: S.StringId; _tag: string },
19
20
  EvtE,
@@ -24,166 +25,157 @@ export function makeSQLQueue<
24
25
  schema: S.Codec<Evt, EvtE>,
25
26
  drainSchema: S.Codec<DrainEvt, DrainEvtE>
26
27
  ) {
27
- return Effect.gen(function*() {
28
- const base = {
29
- id: SQLModel.Generated(QueueId),
30
- meta: SQLModel.JsonFromString(QueueMeta),
31
- name: S.NonEmptyString255,
32
- createdAt: SQLModel.DateTimeInsert,
33
- updatedAt: SQLModel.DateTimeUpdate,
34
- // TODO: at+owner
35
- processingAt: SQLModel.FieldOption(S.Date),
36
- finishedAt: SQLModel.FieldOption(S.Date),
37
- etag: S.String // TODO: use a SQLModel thing that auto updates it?
38
- // TODO: record locking.. / optimistic locking
39
- // rowVersion: SQLModel.DateTimeFromNumberWithNow
40
- }
41
- class Queue extends SQLModel.Class<Queue>("Queue")({
42
- body: SQLModel.JsonFromString(schema),
43
- ...base
44
- }) {}
45
- class Drain extends SQLModel.Class<Drain>("Drain")({
46
- body: SQLModel.JsonFromString(drainSchema),
47
- ...base
48
- }) {}
49
- const sql = yield* SqlClient.SqlClient
28
+ const base = {
29
+ id: SQLModel.Generated(QueueId),
30
+ meta: SQLModel.JsonFromString(QueueMeta),
31
+ name: S.NonEmptyString255,
32
+ createdAt: SQLModel.DateTimeInsert,
33
+ updatedAt: SQLModel.DateTimeUpdate,
34
+ // TODO: at+owner
35
+ processingAt: SQLModel.FieldOption(S.Date),
36
+ finishedAt: SQLModel.FieldOption(S.Date),
37
+ etag: S.String // TODO: use a SQLModel thing that auto updates it?
38
+ // TODO: record locking.. / optimistic locking
39
+ // rowVersion: SQLModel.DateTimeFromNumberWithNow
40
+ }
41
+ class Queue extends SQLModel.Class<Queue>("Queue")({
42
+ body: SQLModel.JsonFromString(schema),
43
+ ...base
44
+ }) {}
45
+ class Drain extends SQLModel.Class<Drain>("Drain")({
46
+ body: SQLModel.JsonFromString(drainSchema),
47
+ ...base
48
+ }) {}
49
+ const sql = yield* SqlClient.SqlClient
50
50
 
51
- const queueRepo = yield* SQLModel.makeRepository(Queue, {
52
- tableName: "queue",
53
- spanPrefix: "QueueRepo",
54
- idColumn: "id",
55
- versionColumn: "etag"
56
- })
51
+ const queueRepo = yield* SQLModel.makeRepository(Queue, {
52
+ tableName: "queue",
53
+ spanPrefix: "QueueRepo",
54
+ idColumn: "id",
55
+ versionColumn: "etag"
56
+ })
57
57
 
58
- const drainRepo = yield* SQLModel.makeRepository(Drain, {
59
- tableName: "queue",
60
- spanPrefix: "DrainRepo",
61
- idColumn: "id",
62
- versionColumn: "etag"
63
- })
58
+ const drainRepo = yield* SQLModel.makeRepository(Drain, {
59
+ tableName: "queue",
60
+ spanPrefix: "DrainRepo",
61
+ idColumn: "id",
62
+ versionColumn: "etag"
63
+ })
64
64
 
65
- const decodeDrain = S.decodeEffect(Drain)
65
+ const decodeDrain = S.decodeEffectConcurrently(Drain)
66
66
 
67
- const drain = Effect
68
- .sync(() => subMinutes(new Date(), 15))
69
- .pipe(
70
- Effect
71
- .andThen((limit) =>
72
- sql<typeof Drain.Encoded>`SELECT *
67
+ const drain = Effect.gen(function*() {
68
+ const limit = subMinutes(new Date(), 15)
69
+ return yield* sql<typeof Drain.Encoded>`SELECT *
73
70
  FROM queue
74
71
  WHERE name = ${queueDrainName} AND finishedAt IS NULL AND (processingAt IS NULL OR processingAt < ${limit.getTime()})
75
72
  LIMIT 1`
76
- )
77
- )
73
+ })
78
74
 
79
- const q = {
80
- offer: Effect.fnUntraced(function*(body: Evt, meta: typeof QueueMeta.Type) {
81
- yield* queueRepo.insertVoid({
82
- body,
83
- meta,
84
- name: queueName,
85
- processingAt: Option.none(),
86
- finishedAt: Option.none(),
87
- etag: crypto.randomUUID()
88
- })
89
- }),
90
- take: Effect.gen(function*() {
91
- while (true) {
92
- const [first] = yield* drain.pipe(Effect.withTracerEnabled(false)) // disable sql tracer otherwise we spam it..
93
- if (first) {
94
- const dec = yield* decodeDrain(first)
95
- const { createdAt, updatedAt, ...rest } = dec
96
- return yield* drainRepo.update(
97
- { ...rest, processingAt: Option.some(new Date()) } // auto in lib , etag: crypto.randomUUID()
98
- )
99
- }
100
- if (first) return first
101
- yield* Effect.sleep(250)
75
+ const q = {
76
+ offer: Effect.fnUntraced(function*(body: Evt, meta: typeof QueueMeta.Type) {
77
+ yield* queueRepo.insertVoid(Queue.insert.make({
78
+ body,
79
+ meta,
80
+ name: queueName,
81
+ processingAt: Option.none(),
82
+ finishedAt: Option.none(),
83
+ etag: crypto.randomUUID()
84
+ }))
85
+ }),
86
+ take: Effect.gen(function*() {
87
+ while (true) {
88
+ const [first] = yield* drain.pipe(Effect.withTracerEnabled(false)) // disable sql tracer otherwise we spam it..
89
+ if (first) {
90
+ const dec = yield* decodeDrain(first)
91
+ const { createdAt, updatedAt, ...rest } = dec
92
+ return yield* drainRepo.update(
93
+ Drain.update.make({ ...rest, processingAt: Option.some(new Date()) }) // auto in lib , etag: crypto.randomUUID()
94
+ )
102
95
  }
103
- }),
104
- finish: ({ createdAt, updatedAt, ...q }: Drain) =>
105
- drainRepo.updateVoid({ ...q, finishedAt: Option.some(new Date()) }) // auto in lib , etag: crypto.randomUUID()
106
- }
107
- const queue = {
108
- publish: (...messages: NonEmptyReadonlyArray<Evt>) =>
109
- getRequestContext
96
+ if (first) return first
97
+ yield* Effect.sleep(250)
98
+ }
99
+ }),
100
+ finish: Effect.fn(function*({ createdAt, updatedAt, ...q }: Drain) {
101
+ return yield* drainRepo.updateVoid(Drain.update.make({ ...q, finishedAt: Option.some(new Date()) })) // auto in lib , etag: crypto.randomUUID()
102
+ })
103
+ }
104
+ const queue = {
105
+ publish: Effect.fn(`publish ${queueName}`, {
106
+ kind: "producer",
107
+ attributes: {
108
+ "messaging.system": "sql",
109
+ "messaging.operation.name": "publish",
110
+ "messaging.destination.name": queueName
111
+ }
112
+ })(function*(
113
+ ...messages: NonEmptyReadonlyArray<Evt>
114
+ ) {
115
+ yield* Effect.annotateCurrentSpan({
116
+ "messaging.batch.message_count": messages.length,
117
+ "messaging.message.types": messages.map((_) => _._tag)
118
+ })
119
+ const requestContext = yield* getRequestContext
120
+ yield* Effect.forEach(messages, (m) => q.offer(m, requestContext), { discard: true })
121
+ }),
122
+ drain: <DrainE, DrainR>(
123
+ handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
124
+ sessionId?: string
125
+ ) => {
126
+ const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
127
+ const processMessage = Effect.fnUntraced(function*({ body, meta }: Drain) {
128
+ let effect = InfraLogger
129
+ .logDebug(`[${queueDrainName}] Processing incoming message`)
110
130
  .pipe(
111
- Effect.flatMap((requestContext) =>
112
- Effect
113
- .forEach(
114
- messages,
115
- (m) => q.offer(m, requestContext),
116
- {
117
- discard: true
118
- }
119
- )
120
- ),
121
- Effect.withSpan("queue.publish: " + queueName, {
122
- kind: "producer",
123
- attributes: { "message_tags": messages.map((_) => _._tag) }
124
- }, { captureStackTrace: false })
125
- ),
126
- drain: <DrainE, DrainR>(
127
- handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
128
- sessionId?: string
129
- ) => {
130
- const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
131
- const processMessage = (msg: Drain) =>
132
- Effect
133
- .succeed(msg)
134
- .pipe(Effect
135
- .flatMap(({ body, meta }) => {
136
- let effect = InfraLogger
137
- .logDebug(`[${queueDrainName}] Processing incoming message`)
138
- .pipe(
139
- Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
140
- Effect.andThen(handleEvent(body)),
141
- silenceAndReportError,
142
- (_) =>
143
- setupRequestContextWithCustomSpan(
144
- _,
145
- meta,
146
- `queue.drain: ${queueDrainName}.${body._tag}`,
147
- {
148
- captureStackTrace: false,
149
- kind: "consumer",
150
- attributes: {
151
- "queue.name": queueDrainName,
152
- "queue.sessionId": sessionId,
153
- "queue.input": body
154
- }
155
- }
156
- )
157
- )
158
- if (meta.span) {
159
- effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
131
+ Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
132
+ Effect.andThen(handleEvent(body)),
133
+ silenceAndReportError,
134
+ (_) => {
135
+ const args = messagingSpanArgs({
136
+ operation: "process",
137
+ system: "sql",
138
+ destination: queueDrainName,
139
+ messageId: body.id,
140
+ conversationId: sessionId,
141
+ extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
142
+ }, "consumer")
143
+ return setupRequestContextWithCustomSpan(
144
+ _,
145
+ meta,
146
+ args.name,
147
+ {
148
+ captureStackTrace: false,
149
+ kind: args.kind,
150
+ attributes: args.attributes
160
151
  }
161
- return effect
162
- }))
163
-
164
- return q
165
- .take
166
- .pipe(
167
- Effect.flatMap((x) =>
168
- processMessage(x).pipe(
169
- Effect.uninterruptible,
170
- Effect.forkChild,
171
- Effect.flatMap(Fiber.join),
172
- Effect.tap(q.finish(x))
173
152
  )
174
- ),
175
- silenceAndReportError,
176
- Effect.withSpan(`queue.drain: ${queueDrainName}`, {
177
- attributes: {
178
- "queue.type": "sql",
179
- "queue.name": queueDrainName,
180
- "queue.sessionId": sessionId
181
- }
182
- }),
183
- Effect.forever
153
+ }
184
154
  )
185
- }
155
+ if (meta.span) {
156
+ effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
157
+ }
158
+ return yield* effect
159
+ })
160
+
161
+ return Effect.fn(`receive ${queueDrainName}`, {
162
+ kind: "consumer",
163
+ attributes: {
164
+ "messaging.system": "sql",
165
+ "messaging.operation.name": "receive",
166
+ "messaging.destination.name": queueDrainName,
167
+ ...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
168
+ }
169
+ })(function*() {
170
+ const x = yield* q.take
171
+ yield* processMessage(x).pipe(
172
+ Effect.uninterruptible,
173
+ Effect.forkChild,
174
+ Effect.flatMap(Fiber.join),
175
+ Effect.tap(q.finish(x))
176
+ )
177
+ }, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
186
178
  }
187
- return queue as QueueBase<Evt, DrainEvt>
188
- })
189
- }
179
+ }
180
+ return queue as QueueBase<Evt, DrainEvt>
181
+ })
@@ -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
+ })