@effect-app/infra 4.0.0-beta.17 → 4.0.0-beta.171

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 (295) hide show
  1. package/CHANGELOG.md +1123 -0
  2. package/dist/CUPS.d.ts +15 -7
  3. package/dist/CUPS.d.ts.map +1 -1
  4. package/dist/CUPS.js +10 -12
  5. package/dist/Emailer/Sendgrid.d.ts +14 -14
  6. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  7. package/dist/Emailer/Sendgrid.js +16 -15
  8. package/dist/Emailer/fake.d.ts +1 -1
  9. package/dist/Emailer/service.d.ts +9 -3
  10. package/dist/Emailer/service.d.ts.map +1 -1
  11. package/dist/Emailer/service.js +3 -3
  12. package/dist/Emailer.d.ts +1 -1
  13. package/dist/MainFiberSet.d.ts +5 -5
  14. package/dist/MainFiberSet.d.ts.map +1 -1
  15. package/dist/MainFiberSet.js +3 -3
  16. package/dist/Model/Repository/Registry.d.ts +20 -0
  17. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  18. package/dist/Model/Repository/Registry.js +17 -0
  19. package/dist/Model/Repository/ext.d.ts +33 -15
  20. package/dist/Model/Repository/ext.d.ts.map +1 -1
  21. package/dist/Model/Repository/ext.js +54 -2
  22. package/dist/Model/Repository/internal/internal.d.ts +6 -6
  23. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  24. package/dist/Model/Repository/internal/internal.js +43 -32
  25. package/dist/Model/Repository/legacy.d.ts +1 -1
  26. package/dist/Model/Repository/makeRepo.d.ts +7 -6
  27. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  28. package/dist/Model/Repository/makeRepo.js +5 -1
  29. package/dist/Model/Repository/service.d.ts +28 -23
  30. package/dist/Model/Repository/service.d.ts.map +1 -1
  31. package/dist/Model/Repository/validation.d.ts +142 -17
  32. package/dist/Model/Repository/validation.d.ts.map +1 -1
  33. package/dist/Model/Repository/validation.js +5 -5
  34. package/dist/Model/Repository.d.ts +2 -1
  35. package/dist/Model/Repository.d.ts.map +1 -1
  36. package/dist/Model/Repository.js +2 -1
  37. package/dist/Model/dsl.d.ts +4 -4
  38. package/dist/Model/dsl.d.ts.map +1 -1
  39. package/dist/Model/filter/filterApi.d.ts +5 -5
  40. package/dist/Model/filter/filterApi.d.ts.map +1 -1
  41. package/dist/Model/filter/types/errors.d.ts +1 -1
  42. package/dist/Model/filter/types/fields.d.ts +1 -1
  43. package/dist/Model/filter/types/path/common.d.ts +1 -1
  44. package/dist/Model/filter/types/path/eager.d.ts +1 -1
  45. package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
  46. package/dist/Model/filter/types/path/index.d.ts +1 -1
  47. package/dist/Model/filter/types/utils.d.ts +1 -1
  48. package/dist/Model/filter/types/validator.d.ts +1 -1
  49. package/dist/Model/filter/types.d.ts +1 -1
  50. package/dist/Model/query/dsl.d.ts +1 -1
  51. package/dist/Model/query/dsl.d.ts.map +1 -1
  52. package/dist/Model/query/new-kid-interpreter.d.ts +6 -6
  53. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  54. package/dist/Model/query/new-kid-interpreter.js +3 -3
  55. package/dist/Model/query.d.ts +1 -1
  56. package/dist/Model.d.ts +2 -1
  57. package/dist/Model.d.ts.map +1 -1
  58. package/dist/Model.js +2 -1
  59. package/dist/Operations.d.ts +6 -6
  60. package/dist/Operations.d.ts.map +1 -1
  61. package/dist/Operations.js +56 -59
  62. package/dist/OperationsRepo.d.ts +11 -29
  63. package/dist/OperationsRepo.d.ts.map +1 -1
  64. package/dist/OperationsRepo.js +3 -3
  65. package/dist/QueueMaker/SQLQueue.d.ts +5 -7
  66. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  67. package/dist/QueueMaker/SQLQueue.js +105 -114
  68. package/dist/QueueMaker/errors.d.ts +2 -2
  69. package/dist/QueueMaker/errors.d.ts.map +1 -1
  70. package/dist/QueueMaker/memQueue.d.ts +7 -4
  71. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  72. package/dist/QueueMaker/memQueue.js +51 -62
  73. package/dist/QueueMaker/sbqueue.d.ts +6 -3
  74. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  75. package/dist/QueueMaker/sbqueue.js +37 -53
  76. package/dist/QueueMaker/service.d.ts +1 -1
  77. package/dist/RequestContext.d.ts +114 -26
  78. package/dist/RequestContext.d.ts.map +1 -1
  79. package/dist/RequestContext.js +7 -7
  80. package/dist/RequestFiberSet.d.ts +7 -7
  81. package/dist/RequestFiberSet.d.ts.map +1 -1
  82. package/dist/RequestFiberSet.js +5 -5
  83. package/dist/Store/ContextMapContainer.d.ts +19 -3
  84. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  85. package/dist/Store/ContextMapContainer.js +13 -3
  86. package/dist/Store/Cosmos/query.d.ts +1 -1
  87. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  88. package/dist/Store/Cosmos/query.js +8 -10
  89. package/dist/Store/Cosmos.d.ts +1 -1
  90. package/dist/Store/Cosmos.d.ts.map +1 -1
  91. package/dist/Store/Cosmos.js +308 -242
  92. package/dist/Store/Disk.d.ts +2 -2
  93. package/dist/Store/Disk.d.ts.map +1 -1
  94. package/dist/Store/Disk.js +25 -22
  95. package/dist/Store/Memory.d.ts +4 -4
  96. package/dist/Store/Memory.d.ts.map +1 -1
  97. package/dist/Store/Memory.js +27 -22
  98. package/dist/Store/SQL/Pg.d.ts +4 -0
  99. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  100. package/dist/Store/SQL/Pg.js +189 -0
  101. package/dist/Store/SQL/query.d.ts +38 -0
  102. package/dist/Store/SQL/query.d.ts.map +1 -0
  103. package/dist/Store/SQL/query.js +367 -0
  104. package/dist/Store/SQL.d.ts +20 -0
  105. package/dist/Store/SQL.d.ts.map +1 -0
  106. package/dist/Store/SQL.js +381 -0
  107. package/dist/Store/codeFilter.d.ts +1 -1
  108. package/dist/Store/codeFilter.d.ts.map +1 -1
  109. package/dist/Store/codeFilter.js +2 -1
  110. package/dist/Store/index.d.ts +5 -2
  111. package/dist/Store/index.d.ts.map +1 -1
  112. package/dist/Store/index.js +15 -3
  113. package/dist/Store/service.d.ts +17 -6
  114. package/dist/Store/service.d.ts.map +1 -1
  115. package/dist/Store/service.js +24 -6
  116. package/dist/Store/utils.d.ts +1 -1
  117. package/dist/Store/utils.d.ts.map +1 -1
  118. package/dist/Store/utils.js +3 -4
  119. package/dist/Store.d.ts +1 -1
  120. package/dist/adapters/SQL/Model.d.ts +28 -42
  121. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  122. package/dist/adapters/SQL/Model.js +2 -2
  123. package/dist/adapters/SQL.d.ts +1 -1
  124. package/dist/adapters/ServiceBus.d.ts +9 -9
  125. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  126. package/dist/adapters/ServiceBus.js +13 -15
  127. package/dist/adapters/cosmos-client.d.ts +3 -3
  128. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  129. package/dist/adapters/cosmos-client.js +3 -3
  130. package/dist/adapters/index.d.ts +8 -2
  131. package/dist/adapters/index.d.ts.map +1 -1
  132. package/dist/adapters/index.js +8 -2
  133. package/dist/adapters/logger.d.ts +1 -1
  134. package/dist/adapters/logger.d.ts.map +1 -1
  135. package/dist/adapters/memQueue.d.ts +3 -3
  136. package/dist/adapters/memQueue.d.ts.map +1 -1
  137. package/dist/adapters/memQueue.js +3 -3
  138. package/dist/adapters/mongo-client.d.ts +3 -3
  139. package/dist/adapters/mongo-client.d.ts.map +1 -1
  140. package/dist/adapters/mongo-client.js +3 -3
  141. package/dist/adapters/redis-client.d.ts +3 -3
  142. package/dist/adapters/redis-client.d.ts.map +1 -1
  143. package/dist/adapters/redis-client.js +3 -3
  144. package/dist/api/ContextProvider.d.ts +7 -7
  145. package/dist/api/ContextProvider.d.ts.map +1 -1
  146. package/dist/api/ContextProvider.js +6 -6
  147. package/dist/api/codec.d.ts +1 -1
  148. package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
  149. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  150. package/dist/api/internal/RequestContextMiddleware.js +2 -2
  151. package/dist/api/internal/auth.d.ts +44 -6
  152. package/dist/api/internal/auth.d.ts.map +1 -1
  153. package/dist/api/internal/auth.js +160 -29
  154. package/dist/api/internal/events.d.ts +3 -3
  155. package/dist/api/internal/events.d.ts.map +1 -1
  156. package/dist/api/internal/events.js +9 -7
  157. package/dist/api/internal/health.d.ts +1 -1
  158. package/dist/api/layerUtils.d.ts +6 -6
  159. package/dist/api/layerUtils.d.ts.map +1 -1
  160. package/dist/api/layerUtils.js +5 -5
  161. package/dist/api/middlewares.d.ts +1 -1
  162. package/dist/api/reportError.d.ts +1 -1
  163. package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
  164. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  165. package/dist/api/routing/middleware/middleware.d.ts +39 -3
  166. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  167. package/dist/api/routing/middleware/middleware.js +45 -14
  168. package/dist/api/routing/middleware.d.ts +1 -2
  169. package/dist/api/routing/middleware.d.ts.map +1 -1
  170. package/dist/api/routing/middleware.js +1 -2
  171. package/dist/api/routing/schema/jwt.d.ts +1 -1
  172. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  173. package/dist/api/routing/tsort.d.ts +1 -1
  174. package/dist/api/routing/tsort.d.ts.map +1 -1
  175. package/dist/api/routing/utils.d.ts +3 -3
  176. package/dist/api/routing/utils.d.ts.map +1 -1
  177. package/dist/api/routing.d.ts +12 -14
  178. package/dist/api/routing.d.ts.map +1 -1
  179. package/dist/api/routing.js +17 -6
  180. package/dist/api/setupRequest.d.ts +8 -5
  181. package/dist/api/setupRequest.d.ts.map +1 -1
  182. package/dist/api/setupRequest.js +12 -7
  183. package/dist/api/util.d.ts +1 -1
  184. package/dist/arbs.d.ts +1 -1
  185. package/dist/arbs.d.ts.map +1 -1
  186. package/dist/arbs.js +5 -3
  187. package/dist/errorReporter.d.ts +4 -4
  188. package/dist/errorReporter.d.ts.map +1 -1
  189. package/dist/errorReporter.js +16 -23
  190. package/dist/errors.d.ts +1 -1
  191. package/dist/fileUtil.d.ts +1 -1
  192. package/dist/fileUtil.d.ts.map +1 -1
  193. package/dist/index.d.ts +1 -1
  194. package/dist/logger/jsonLogger.d.ts +1 -1
  195. package/dist/logger/logFmtLogger.d.ts +1 -1
  196. package/dist/logger/shared.d.ts +1 -1
  197. package/dist/logger/shared.js +2 -2
  198. package/dist/logger.d.ts +1 -1
  199. package/dist/logger.d.ts.map +1 -1
  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 +1 -1
  204. package/dist/test.d.ts.map +1 -1
  205. package/dist/vitest.d.ts +1 -1
  206. package/eslint.config.mjs +3 -3
  207. package/examples/query.ts +39 -35
  208. package/package.json +42 -28
  209. package/src/CUPS.ts +9 -11
  210. package/src/Emailer/Sendgrid.ts +17 -14
  211. package/src/Emailer/service.ts +8 -2
  212. package/src/MainFiberSet.ts +3 -3
  213. package/src/Model/Repository/Registry.ts +33 -0
  214. package/src/Model/Repository/ext.ts +93 -6
  215. package/src/Model/Repository/internal/internal.ts +97 -88
  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/query/new-kid-interpreter.ts +2 -2
  222. package/src/Model.ts +1 -0
  223. package/src/Operations.ts +78 -113
  224. package/src/OperationsRepo.ts +2 -2
  225. package/src/QueueMaker/SQLQueue.ts +121 -151
  226. package/src/QueueMaker/memQueue.ts +82 -103
  227. package/src/QueueMaker/sbqueue.ts +56 -86
  228. package/src/RequestContext.ts +8 -8
  229. package/src/RequestFiberSet.ts +4 -4
  230. package/src/Store/ContextMapContainer.ts +41 -2
  231. package/src/Store/Cosmos/query.ts +9 -11
  232. package/src/Store/Cosmos.ts +437 -343
  233. package/src/Store/Disk.ts +52 -49
  234. package/src/Store/Memory.ts +54 -48
  235. package/src/Store/SQL/Pg.ts +318 -0
  236. package/src/Store/SQL/query.ts +409 -0
  237. package/src/Store/SQL.ts +668 -0
  238. package/src/Store/codeFilter.ts +1 -0
  239. package/src/Store/index.ts +17 -2
  240. package/src/Store/service.ts +31 -7
  241. package/src/Store/utils.ts +23 -22
  242. package/src/adapters/SQL/Model.ts +10 -4
  243. package/src/adapters/ServiceBus.ts +111 -115
  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 +11 -11
  250. package/src/api/internal/RequestContextMiddleware.ts +1 -1
  251. package/src/api/internal/auth.ts +246 -44
  252. package/src/api/internal/events.ts +12 -8
  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 +52 -12
  256. package/src/api/routing/middleware.ts +0 -2
  257. package/src/api/routing.ts +21 -7
  258. package/src/api/setupRequest.ts +28 -8
  259. package/src/arbs.ts +4 -2
  260. package/src/errorReporter.ts +58 -72
  261. package/src/logger/shared.ts +1 -1
  262. package/src/rateLimit.ts +30 -22
  263. package/test/auth.test.ts +101 -0
  264. package/test/contextProvider.test.ts +11 -11
  265. package/test/controller.test.ts +18 -14
  266. package/test/dist/auth.test.d.ts.map +1 -0
  267. package/test/dist/contextProvider.test.d.ts.map +1 -1
  268. package/test/dist/controller.test.d.ts.map +1 -1
  269. package/test/dist/date-query.test.d.ts.map +1 -0
  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-multi-middleware.test.d.ts.map +1 -1
  280. package/test/dist/sql-store.test.d.ts.map +1 -0
  281. package/test/fixtures.ts +11 -9
  282. package/test/query.test.ts +216 -34
  283. package/test/rawQuery.test.ts +23 -19
  284. package/test/repository-ext.test.ts +60 -0
  285. package/test/requires.test.ts +6 -6
  286. package/test/router-generator.test.ts +180 -0
  287. package/test/routing-interruptibility.test.ts +63 -0
  288. package/test/rpc-multi-middleware.test.ts +78 -9
  289. package/test/sql-store.test.ts +1064 -0
  290. package/test/validateSample.test.ts +15 -12
  291. package/tsconfig.examples.json +1 -1
  292. package/tsconfig.json +0 -1
  293. package/tsconfig.json.bak +2 -2
  294. package/tsconfig.src.json +35 -35
  295. package/tsconfig.test.json +2 -2
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3
- import { Array, identity, Match, Option, pipe, S } from "effect-app"
3
+ import { Array, identity, Match, Option, pipe, S, SchemaAST } from "effect-app"
4
4
  import { toNonEmptyArray } from "effect-app/Array"
5
5
  import { dropUndefinedT } from "effect-app/utils"
6
6
  import type { FilterResult } from "../filter/filterApi.js"
@@ -165,7 +165,7 @@ export const toFilter = <
165
165
  let select: (keyof TFieldValues | { key: string; subKeys: string[] })[] = []
166
166
  // TODO: support more complex (nested) schemas?
167
167
  if (schema) {
168
- const t = walkTransformation(schema.ast)
168
+ const t = walkTransformation(SchemaAST.toEncoded(schema.ast))
169
169
  if (S.AST.isObjects(t)) {
170
170
  select = t.propertySignatures.map((_) => _.name as string)
171
171
  for (const prop of t.propertySignatures) {
package/src/Model.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./Model/dsl.js"
2
2
  export * as Q from "./Model/query.js"
3
3
  export { makeRepo } from "./Model/Repository.js"
4
+ export { type RegisteredRepository, RepositoryRegistry, RepositoryRegistryLive } from "./Model/Repository.js"
package/src/Operations.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { reportError } from "@effect-app/infra/errorReporter"
2
2
  import { subHours } from "date-fns"
3
- import { Cause, copy, Duration, Effect, Exit, type Fiber, Layer, Option, S, Schedule, ServiceMap } from "effect-app"
3
+ import { Cause, Context, copy, Duration, Effect, Exit, type Fiber, Layer, Option, S, Schedule } from "effect-app"
4
4
  import { annotateLogscoped } from "effect-app/Effect"
5
5
  import { dual, pipe } from "effect-app/Function"
6
6
  import { Operation, OperationFailure, OperationId, type OperationProgress, OperationSuccess } from "effect-app/Operations"
@@ -20,6 +20,33 @@ const make = Effect.gen(function*() {
20
20
  const reqFiberSet = yield* RequestFiberSet
21
21
  const makeOp = Effect.sync(() => OperationId.make())
22
22
 
23
+ const addOp = Effect.fnUntraced(function*(id: OperationId, title: NonEmptyString2k) {
24
+ return yield* repo.save(Operation.make({ id, title })).pipe(Effect.orDie)
25
+ })
26
+
27
+ const finishOp = Effect.fnUntraced(function*(id: OperationId, exit: Exit.Exit<unknown, unknown>) {
28
+ const op = yield* repo.get(id).pipe(Effect.orDie)
29
+ const result = Exit.isSuccess(exit)
30
+ ? OperationSuccess.make({})
31
+ : OperationFailure.make({
32
+ message: Cause.hasInterruptsOnly(exit.cause)
33
+ ? NonEmptyString2k("Interrupted")
34
+ : Cause.hasDies(exit.cause)
35
+ ? NonEmptyString2k("Unknown error")
36
+ : Cause
37
+ .findErrorOption(exit.cause)
38
+ .pipe(
39
+ Option.flatMap((_) =>
40
+ typeof _ === "object" && _ !== null && "message" in _ && S.is(NonEmptyString2k)(_.message)
41
+ ? Option.some(_.message)
42
+ : Option.none()
43
+ ),
44
+ Option.getOrNull
45
+ )
46
+ })
47
+ return yield* repo.save(copy(op, { updatedAt: new Date(), result })).pipe(Effect.orDie)
48
+ })
49
+
23
50
  const register = (title: NonEmptyString2k) =>
24
51
  Effect.tap(
25
52
  makeOp,
@@ -30,53 +57,20 @@ const make = Effect.gen(function*() {
30
57
  )
31
58
  )
32
59
 
33
- const cleanup = Effect.sync(() => subHours(new Date(), 1)).pipe(
34
- Effect.andThen((before) => repo.query(where("updatedAt", "lt", before.toISOString()))),
35
- Effect.andThen((ops) => pipe(ops, batch(100, Effect.succeed, (items) => repo.removeAndPublish(items)))),
36
- setupRequestContextFromCurrent("Operations.cleanup")
37
- )
60
+ const cleanup = Effect
61
+ .gen(function*() {
62
+ const before = subHours(new Date(), 1)
63
+ const ops = yield* repo.query(where("updatedAt", "lt", before.toISOString()))
64
+ return yield* pipe(ops, batch(100, Effect.succeed, (items) => repo.removeAndPublish(items)))
65
+ })
66
+ .pipe(setupRequestContextFromCurrent("Operations.cleanup"))
38
67
 
39
- function addOp(id: OperationId, title: NonEmptyString2k) {
40
- return repo.save(new Operation({ id, title })).pipe(Effect.orDie)
41
- }
42
- function findOp(id: OperationId) {
43
- return repo.find(id)
44
- }
45
- function finishOp(id: OperationId, exit: Exit.Exit<unknown, unknown>) {
46
- return Effect
47
- .flatMap(repo.get(id).pipe(Effect.orDie), (_) =>
48
- repo
49
- .save(
50
- copy(_, {
51
- updatedAt: new Date(),
52
- result: Exit.isSuccess(exit)
53
- ? new OperationSuccess()
54
- : new OperationFailure({
55
- message: Cause.hasInterruptsOnly(exit.cause)
56
- ? NonEmptyString2k("Interrupted")
57
- : Cause.hasDies(exit.cause)
58
- ? NonEmptyString2k("Unknown error")
59
- : Cause
60
- .findErrorOption(exit.cause)
61
- .pipe(
62
- Option.flatMap((_) =>
63
- typeof _ === "object" && _ !== null && "message" in _ && S.is(NonEmptyString2k)(_.message)
64
- ? Option.some(_.message)
65
- : Option.none()
66
- ),
67
- Option.getOrNull
68
- )
69
- })
70
- })
71
- )
72
- .pipe(Effect.orDie))
73
- }
74
- function update(id: OperationId, progress: OperationProgress) {
75
- return Effect.flatMap(
76
- repo.get(id).pipe(Effect.orDie),
77
- (_) => repo.save(copy(_, { updatedAt: new Date(), progress })).pipe(Effect.orDie)
78
- )
79
- }
68
+ const findOp = (id: OperationId) => repo.find(id)
69
+
70
+ const update = Effect.fnUntraced(function*(id: OperationId, progress: OperationProgress) {
71
+ const op = yield* repo.get(id).pipe(Effect.orDie)
72
+ return yield* repo.save(copy(op, { updatedAt: new Date(), progress })).pipe(Effect.orDie)
73
+ })
80
74
 
81
75
  function fork<R, R2, E, E2, A, A2>(
82
76
  self: (id: OperationId) => Effect.Effect<A, E, R>,
@@ -87,29 +81,18 @@ const make = Effect.gen(function*() {
87
81
  never,
88
82
  Exclude<R, Scope.Scope> | Exclude<R2, Scope.Scope>
89
83
  > {
90
- return Effect
91
- .flatMap(
92
- Scope.make(),
93
- (scope) =>
94
- register(title)
95
- .pipe(
96
- Scope.provide(scope),
97
- Effect.flatMap((id) =>
98
- reqFiberSet
99
- .forkDaemonReportUnexpected(Scope.use(
100
- self(id).pipe(Effect.withSpan(title, {}, { captureStackTrace: false })),
101
- scope
102
- ))
103
- .pipe(Effect.map((fiber): RunningOperation<A, E> => ({ fiber, id })))
104
- ),
105
- Effect.tap(({ id }) =>
106
- Effect.interruptible(fnc(id)).pipe(
107
- Effect.forkScoped,
108
- Scope.provide(scope)
109
- )
110
- )
111
- )
84
+ return Effect.gen(function*() {
85
+ const scope = yield* Scope.make()
86
+ const id = yield* Scope.provide(register(title), scope)
87
+ const fiber = yield* reqFiberSet.forkDaemonReportUnexpected(
88
+ Scope.use(
89
+ self(id).pipe(Effect.withSpan(title, {}, { captureStackTrace: false })),
90
+ scope
91
+ )
112
92
  )
93
+ yield* Scope.provide(Effect.forkScoped(Effect.interruptible(fnc(id))), scope)
94
+ return { fiber, id } satisfies RunningOperation<A, E>
95
+ })
113
96
  }
114
97
 
115
98
  const fork2: {
@@ -122,24 +105,20 @@ const make = Effect.gen(function*() {
122
105
  ): Effect.Effect<RunningOperation<A, E>, never, Exclude<R, Scope.Scope>>
123
106
  } = dual(
124
107
  2,
125
- <R, E, A>(self: (opId: OperationId) => Effect.Effect<A, E, R>, title: NonEmptyString2k) =>
126
- Effect.flatMap(
127
- Scope.make(),
128
- (scope) =>
129
- register(title)
130
- .pipe(
131
- Scope.provide(scope),
132
- Effect
133
- .flatMap((id) =>
134
- reqFiberSet
135
- .forkDaemonReportUnexpected(Scope.use(
136
- self(id).pipe(Effect.withSpan(title, {}, { captureStackTrace: false })),
137
- scope
138
- ))
139
- .pipe(Effect.map((fiber): RunningOperation<A, E> => ({ fiber, id })))
140
- )
141
- )
108
+ Effect.fnUntraced(function*<R, E, A>(
109
+ self: (opId: OperationId) => Effect.Effect<A, E, R>,
110
+ title: NonEmptyString2k
111
+ ) {
112
+ const scope = yield* Scope.make()
113
+ const id = yield* Scope.provide(register(title), scope)
114
+ const fiber = yield* reqFiberSet.forkDaemonReportUnexpected(
115
+ Scope.use(
116
+ self(id).pipe(Effect.withSpan(title, {}, { captureStackTrace: false })),
117
+ scope
118
+ )
142
119
  )
120
+ return { fiber, id } satisfies RunningOperation<A, E>
121
+ })
143
122
  )
144
123
 
145
124
  const forkOperation: {
@@ -152,28 +131,21 @@ const make = Effect.gen(function*() {
152
131
  ): Effect.Effect<RunningOperation<A, E>, never, Exclude<R, Scope.Scope>>
153
132
  } = dual(
154
133
  2,
155
- <R, E, A>(self: Effect.Effect<A, E, R>, title: NonEmptyString2k) =>
156
- Effect.flatMap(
157
- Scope.make(),
158
- (scope) =>
159
- register(title)
160
- .pipe(
161
- Scope.provide(scope),
162
- Effect
163
- .flatMap((id) =>
164
- reqFiberSet
165
- .forkDaemonReportUnexpected(Scope.use(
166
- self.pipe(Effect.withSpan(title, {}, { captureStackTrace: false })),
167
- scope
168
- ))
169
- .pipe(Effect.map((fiber): RunningOperation<A, E> => ({ fiber, id })))
170
- )
171
- )
134
+ Effect.fnUntraced(function*<R, E, A>(self: Effect.Effect<A, E, R>, title: NonEmptyString2k) {
135
+ const scope = yield* Scope.make()
136
+ const id = yield* Scope.provide(register(title), scope)
137
+ const fiber = yield* reqFiberSet.forkDaemonReportUnexpected(
138
+ Scope.use(
139
+ self.pipe(Effect.withSpan(title, {}, { captureStackTrace: false })),
140
+ scope
141
+ )
172
142
  )
143
+ return { fiber, id } satisfies RunningOperation<A, E>
144
+ })
173
145
  )
174
146
 
175
147
  function forkOperationFunction<R, E, A, Inp>(fnc: (inp: Inp) => Effect.Effect<A, E, R>, title: NonEmptyString2k) {
176
- return (inp: Inp) => fnc(inp).pipe((_) => forkOperation(_, title))
148
+ return (inp: Inp) => forkOperation(fnc(inp), title)
177
149
  }
178
150
 
179
151
  return {
@@ -189,19 +161,12 @@ const make = Effect.gen(function*() {
189
161
  }
190
162
  })
191
163
 
192
- export class Operations extends ServiceMap.Opaque<Operations>()("effect-app/Operations", { make }) {
164
+ export class Operations extends Context.Opaque<Operations>()("effect-app/Operations", { make }) {
193
165
  private static readonly CleanupLive = this
194
166
  .use((_) =>
195
167
  _.cleanup.pipe(
196
168
  Effect.exit,
197
- Effect
198
- .flatMap((_) => {
199
- if (Exit.isSuccess(_)) {
200
- return Effect.void
201
- } else {
202
- return reportAppError(_.cause)
203
- }
204
- }),
169
+ Effect.flatMap((exit) => Exit.isSuccess(exit) ? Effect.void : reportAppError(exit.cause)),
205
170
  Effect.schedule(Schedule.fixed(Duration.minutes(20))),
206
171
  Effect.map((_) => _ as never),
207
172
  MainFiberSet.run
@@ -1,8 +1,8 @@
1
- import { Effect, ServiceMap } from "effect-app"
1
+ import { Context, Effect } from "effect-app"
2
2
  import { Operation } from "effect-app/Operations"
3
3
  import { makeRepo } from "./Model.js"
4
4
 
5
- export class OperationsRepo extends ServiceMap.Service<OperationsRepo>()(
5
+ export class OperationsRepo extends Context.Service<OperationsRepo>()(
6
6
  "OperationRepo",
7
7
  {
8
8
  make: Effect.gen(function*() {
@@ -3,17 +3,17 @@ 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
11
 
12
- export const QueueId = S.Number.pipe(S.brand("QueueId"))
12
+ export const QueueId = S.Finite.pipe(S.brand("QueueId"))
13
13
  export type QueueId = typeof QueueId.Type
14
14
 
15
15
  // TODO: let the model track and Auto Generate versionColumn on every update instead
16
- export function makeSQLQueue<
16
+ export const makeSQLQueue = Effect.fnUntraced(function*<
17
17
  Evt extends { id: S.StringId; _tag: string },
18
18
  DrainEvt extends { id: S.StringId; _tag: string },
19
19
  EvtE,
@@ -24,166 +24,136 @@ export function makeSQLQueue<
24
24
  schema: S.Codec<Evt, EvtE>,
25
25
  drainSchema: S.Codec<DrainEvt, DrainEvtE>
26
26
  ) {
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
27
+ const base = {
28
+ id: SQLModel.Generated(QueueId),
29
+ meta: SQLModel.JsonFromString(QueueMeta),
30
+ name: S.NonEmptyString255,
31
+ createdAt: SQLModel.DateTimeInsert,
32
+ updatedAt: SQLModel.DateTimeUpdate,
33
+ // TODO: at+owner
34
+ processingAt: SQLModel.FieldOption(S.Date),
35
+ finishedAt: SQLModel.FieldOption(S.Date),
36
+ etag: S.String // TODO: use a SQLModel thing that auto updates it?
37
+ // TODO: record locking.. / optimistic locking
38
+ // rowVersion: SQLModel.DateTimeFromNumberWithNow
39
+ }
40
+ class Queue extends SQLModel.Class<Queue>("Queue")({
41
+ body: SQLModel.JsonFromString(schema),
42
+ ...base
43
+ }) {}
44
+ class Drain extends SQLModel.Class<Drain>("Drain")({
45
+ body: SQLModel.JsonFromString(drainSchema),
46
+ ...base
47
+ }) {}
48
+ const sql = yield* SqlClient.SqlClient
50
49
 
51
- const queueRepo = yield* SQLModel.makeRepository(Queue, {
52
- tableName: "queue",
53
- spanPrefix: "QueueRepo",
54
- idColumn: "id",
55
- versionColumn: "etag"
56
- })
50
+ const queueRepo = yield* SQLModel.makeRepository(Queue, {
51
+ tableName: "queue",
52
+ spanPrefix: "QueueRepo",
53
+ idColumn: "id",
54
+ versionColumn: "etag"
55
+ })
57
56
 
58
- const drainRepo = yield* SQLModel.makeRepository(Drain, {
59
- tableName: "queue",
60
- spanPrefix: "DrainRepo",
61
- idColumn: "id",
62
- versionColumn: "etag"
63
- })
57
+ const drainRepo = yield* SQLModel.makeRepository(Drain, {
58
+ tableName: "queue",
59
+ spanPrefix: "DrainRepo",
60
+ idColumn: "id",
61
+ versionColumn: "etag"
62
+ })
64
63
 
65
- const decodeDrain = S.decodeEffect(Drain)
64
+ const decodeDrain = S.decodeEffectConcurrently(Drain)
66
65
 
67
- const drain = Effect
68
- .sync(() => subMinutes(new Date(), 15))
69
- .pipe(
70
- Effect
71
- .andThen((limit) =>
72
- sql<typeof Drain.Encoded>`SELECT *
66
+ const drain = Effect.gen(function*() {
67
+ const limit = subMinutes(new Date(), 15)
68
+ return yield* sql<typeof Drain.Encoded>`SELECT *
73
69
  FROM queue
74
70
  WHERE name = ${queueDrainName} AND finishedAt IS NULL AND (processingAt IS NULL OR processingAt < ${limit.getTime()})
75
71
  LIMIT 1`
76
- )
77
- )
72
+ })
78
73
 
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)
74
+ const q = {
75
+ offer: Effect.fnUntraced(function*(body: Evt, meta: typeof QueueMeta.Type) {
76
+ yield* queueRepo.insertVoid(Queue.insert.make({
77
+ body,
78
+ meta,
79
+ name: queueName,
80
+ processingAt: Option.none(),
81
+ finishedAt: Option.none(),
82
+ etag: crypto.randomUUID()
83
+ }))
84
+ }),
85
+ take: Effect.gen(function*() {
86
+ while (true) {
87
+ const [first] = yield* drain.pipe(Effect.withTracerEnabled(false)) // disable sql tracer otherwise we spam it..
88
+ if (first) {
89
+ const dec = yield* decodeDrain(first)
90
+ const { createdAt, updatedAt, ...rest } = dec
91
+ return yield* drainRepo.update(
92
+ Drain.update.make({ ...rest, processingAt: Option.some(new Date()) }) // auto in lib , etag: crypto.randomUUID()
93
+ )
102
94
  }
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
95
+ if (first) return first
96
+ yield* Effect.sleep(250)
97
+ }
98
+ }),
99
+ finish: Effect.fn(function*({ createdAt, updatedAt, ...q }: Drain) {
100
+ return yield* drainRepo.updateVoid(Drain.update.make({ ...q, finishedAt: Option.some(new Date()) })) // auto in lib , etag: crypto.randomUUID()
101
+ })
102
+ }
103
+ const queue = {
104
+ publish: Effect.fn("queue.publish: " + queueName, { kind: "producer" })(function*(
105
+ ...messages: NonEmptyReadonlyArray<Evt>
106
+ ) {
107
+ yield* Effect.annotateCurrentSpan({ "message_tags": messages.map((_) => _._tag) })
108
+ const requestContext = yield* getRequestContext
109
+ yield* Effect.forEach(messages, (m) => q.offer(m, requestContext), { discard: true })
110
+ }),
111
+ drain: <DrainE, DrainR>(
112
+ handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
113
+ sessionId?: string
114
+ ) => {
115
+ const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
116
+ const processMessage = Effect.fnUntraced(function*({ body, meta }: Drain) {
117
+ let effect = InfraLogger
118
+ .logDebug(`[${queueDrainName}] Processing incoming message`)
110
119
  .pipe(
111
- Effect.flatMap((requestContext) =>
112
- Effect
113
- .forEach(
114
- messages,
115
- (m) => q.offer(m, requestContext),
116
- {
117
- discard: true
120
+ Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
121
+ Effect.andThen(handleEvent(body)),
122
+ silenceAndReportError,
123
+ (_) =>
124
+ setupRequestContextWithCustomSpan(
125
+ _,
126
+ meta,
127
+ `queue.drain: ${queueDrainName}.${body._tag}`,
128
+ {
129
+ captureStackTrace: false,
130
+ kind: "consumer",
131
+ attributes: {
132
+ "queue.name": queueDrainName,
133
+ "queue.sessionId": sessionId,
134
+ "queue.input": body
118
135
  }
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))
160
136
  }
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
137
  )
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
184
138
  )
185
- }
139
+ if (meta.span) {
140
+ effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
141
+ }
142
+ return yield* effect
143
+ })
144
+
145
+ return Effect.fn(`queue.drain: ${queueDrainName}`, {
146
+ attributes: { "queue.type": "sql", "queue.name": queueDrainName, "queue.sessionId": sessionId }
147
+ })(function*() {
148
+ const x = yield* q.take
149
+ yield* processMessage(x).pipe(
150
+ Effect.uninterruptible,
151
+ Effect.forkChild,
152
+ Effect.flatMap(Fiber.join),
153
+ Effect.tap(q.finish(x))
154
+ )
155
+ }, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
186
156
  }
187
- return queue as QueueBase<Evt, DrainEvt>
188
- })
189
- }
157
+ }
158
+ return queue as QueueBase<Evt, DrainEvt>
159
+ })