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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/CHANGELOG.md +1648 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +12 -7
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +16 -12
  6. package/dist/Emailer/Sendgrid.d.ts +15 -15
  7. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  8. package/dist/Emailer/Sendgrid.js +20 -16
  9. package/dist/Emailer/fake.d.ts +1 -1
  10. package/dist/Emailer/fake.js +2 -2
  11. package/dist/Emailer/service.d.ts +13 -4
  12. package/dist/Emailer/service.d.ts.map +1 -1
  13. package/dist/Emailer/service.js +4 -3
  14. package/dist/Emailer.d.ts +1 -1
  15. package/dist/MainFiberSet.d.ts +12 -9
  16. package/dist/MainFiberSet.d.ts.map +1 -1
  17. package/dist/MainFiberSet.js +7 -3
  18. package/dist/Model/Repository/Registry.d.ts +21 -0
  19. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  20. package/dist/Model/Repository/Registry.js +18 -0
  21. package/dist/Model/Repository/ext.d.ts +35 -16
  22. package/dist/Model/Repository/ext.d.ts.map +1 -1
  23. package/dist/Model/Repository/ext.js +60 -3
  24. package/dist/Model/Repository/internal/internal.d.ts +9 -6
  25. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  26. package/dist/Model/Repository/internal/internal.js +115 -51
  27. package/dist/Model/Repository/legacy.d.ts +4 -2
  28. package/dist/Model/Repository/legacy.d.ts.map +1 -1
  29. package/dist/Model/Repository/makeRepo.d.ts +10 -6
  30. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  31. package/dist/Model/Repository/makeRepo.js +5 -2
  32. package/dist/Model/Repository/service.d.ts +32 -24
  33. package/dist/Model/Repository/service.d.ts.map +1 -1
  34. package/dist/Model/Repository/validation.d.ts +47 -18
  35. package/dist/Model/Repository/validation.d.ts.map +1 -1
  36. package/dist/Model/Repository/validation.js +6 -6
  37. package/dist/Model/Repository.d.ts +2 -1
  38. package/dist/Model/Repository.d.ts.map +1 -1
  39. package/dist/Model/Repository.js +2 -1
  40. package/dist/Model/dsl.d.ts +6 -5
  41. package/dist/Model/dsl.d.ts.map +1 -1
  42. package/dist/Model/dsl.js +2 -3
  43. package/dist/Model/filter/filterApi.d.ts +5 -5
  44. package/dist/Model/filter/filterApi.d.ts.map +1 -1
  45. package/dist/Model/filter/types/errors.d.ts +1 -1
  46. package/dist/Model/filter/types/fields.d.ts +1 -1
  47. package/dist/Model/filter/types/path/common.d.ts +1 -1
  48. package/dist/Model/filter/types/path/eager.d.ts +1 -1
  49. package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
  50. package/dist/Model/filter/types/path/eager.js +1 -1
  51. package/dist/Model/filter/types/path/index.d.ts +1 -1
  52. package/dist/Model/filter/types/utils.d.ts +1 -1
  53. package/dist/Model/filter/types/validator.d.ts +1 -1
  54. package/dist/Model/filter/types.d.ts +1 -1
  55. package/dist/Model/query/dsl.d.ts +142 -17
  56. package/dist/Model/query/dsl.d.ts.map +1 -1
  57. package/dist/Model/query/dsl.js +190 -5
  58. package/dist/Model/query/new-kid-interpreter.d.ts +77 -8
  59. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  60. package/dist/Model/query/new-kid-interpreter.js +127 -6
  61. package/dist/Model/query.d.ts +1 -1
  62. package/dist/Model.d.ts +2 -1
  63. package/dist/Model.d.ts.map +1 -1
  64. package/dist/Model.js +2 -1
  65. package/dist/QueueMaker/SQLQueue.d.ts +7 -8
  66. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  67. package/dist/QueueMaker/SQLQueue.js +135 -117
  68. package/dist/QueueMaker/errors.d.ts +5 -3
  69. package/dist/QueueMaker/errors.d.ts.map +1 -1
  70. package/dist/QueueMaker/errors.js +4 -2
  71. package/dist/QueueMaker/memQueue.d.ts +9 -5
  72. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  73. package/dist/QueueMaker/memQueue.js +81 -65
  74. package/dist/QueueMaker/sbqueue.d.ts +8 -4
  75. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  76. package/dist/QueueMaker/sbqueue.js +57 -55
  77. package/dist/QueueMaker/service.d.ts +4 -2
  78. package/dist/QueueMaker/service.d.ts.map +1 -1
  79. package/dist/QueueMaker/service.js +1 -1
  80. package/dist/RequestContext.d.ts +75 -35
  81. package/dist/RequestContext.d.ts.map +1 -1
  82. package/dist/RequestContext.js +14 -14
  83. package/dist/RequestFiberSet.d.ts +10 -7
  84. package/dist/RequestFiberSet.d.ts.map +1 -1
  85. package/dist/RequestFiberSet.js +8 -3
  86. package/dist/Store/ContextMapContainer.d.ts +22 -3
  87. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  88. package/dist/Store/ContextMapContainer.js +17 -3
  89. package/dist/Store/Cosmos/query.d.ts +7 -2
  90. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  91. package/dist/Store/Cosmos/query.js +115 -35
  92. package/dist/Store/Cosmos.d.ts +2 -2
  93. package/dist/Store/Cosmos.d.ts.map +1 -1
  94. package/dist/Store/Cosmos.js +343 -244
  95. package/dist/Store/Disk.d.ts +3 -3
  96. package/dist/Store/Disk.d.ts.map +1 -1
  97. package/dist/Store/Disk.js +76 -36
  98. package/dist/Store/Memory.d.ts +7 -4
  99. package/dist/Store/Memory.d.ts.map +1 -1
  100. package/dist/Store/Memory.js +251 -58
  101. package/dist/Store/SQL/Pg.d.ts +4 -0
  102. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  103. package/dist/Store/SQL/Pg.js +233 -0
  104. package/dist/Store/SQL/query.d.ts +43 -0
  105. package/dist/Store/SQL/query.d.ts.map +1 -0
  106. package/dist/Store/SQL/query.js +478 -0
  107. package/dist/Store/SQL.d.ts +21 -0
  108. package/dist/Store/SQL.d.ts.map +1 -0
  109. package/dist/Store/SQL.js +450 -0
  110. package/dist/Store/codeFilter.d.ts +2 -2
  111. package/dist/Store/codeFilter.d.ts.map +1 -1
  112. package/dist/Store/codeFilter.js +6 -3
  113. package/dist/Store/index.d.ts +6 -3
  114. package/dist/Store/index.d.ts.map +1 -1
  115. package/dist/Store/index.js +18 -4
  116. package/dist/Store/service.d.ts +26 -8
  117. package/dist/Store/service.d.ts.map +1 -1
  118. package/dist/Store/service.js +25 -6
  119. package/dist/Store/utils.d.ts +3 -2
  120. package/dist/Store/utils.d.ts.map +1 -1
  121. package/dist/Store/utils.js +5 -5
  122. package/dist/Store.d.ts +1 -1
  123. package/dist/adapters/SQL/Model.d.ts +32 -43
  124. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  125. package/dist/adapters/SQL/Model.js +30 -39
  126. package/dist/adapters/SQL.d.ts +1 -1
  127. package/dist/adapters/ServiceBus.d.ts +14 -11
  128. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  129. package/dist/adapters/ServiceBus.js +30 -21
  130. package/dist/adapters/cosmos-client.d.ts +5 -3
  131. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  132. package/dist/adapters/cosmos-client.js +5 -3
  133. package/dist/adapters/index.d.ts +8 -2
  134. package/dist/adapters/index.d.ts.map +1 -1
  135. package/dist/adapters/index.js +8 -2
  136. package/dist/adapters/logger.d.ts +2 -2
  137. package/dist/adapters/logger.d.ts.map +1 -1
  138. package/dist/adapters/memQueue.d.ts +5 -3
  139. package/dist/adapters/memQueue.d.ts.map +1 -1
  140. package/dist/adapters/memQueue.js +6 -5
  141. package/dist/adapters/mongo-client.d.ts +4 -3
  142. package/dist/adapters/mongo-client.d.ts.map +1 -1
  143. package/dist/adapters/mongo-client.js +5 -3
  144. package/dist/adapters/redis-client.d.ts +6 -3
  145. package/dist/adapters/redis-client.d.ts.map +1 -1
  146. package/dist/adapters/redis-client.js +7 -3
  147. package/dist/api/ContextProvider.d.ts +12 -8
  148. package/dist/api/ContextProvider.d.ts.map +1 -1
  149. package/dist/api/ContextProvider.js +9 -7
  150. package/dist/api/codec.d.ts +1 -1
  151. package/dist/api/internal/RequestContextMiddleware.d.ts +3 -3
  152. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  153. package/dist/api/internal/RequestContextMiddleware.js +10 -6
  154. package/dist/api/internal/auth.d.ts +45 -7
  155. package/dist/api/internal/auth.d.ts.map +1 -1
  156. package/dist/api/internal/auth.js +162 -29
  157. package/dist/api/internal/events.d.ts +6 -4
  158. package/dist/api/internal/events.d.ts.map +1 -1
  159. package/dist/api/internal/events.js +16 -9
  160. package/dist/api/internal/health.d.ts +1 -1
  161. package/dist/api/layerUtils.d.ts +10 -6
  162. package/dist/api/layerUtils.d.ts.map +1 -1
  163. package/dist/api/layerUtils.js +7 -6
  164. package/dist/api/middlewares.d.ts +1 -1
  165. package/dist/api/reportError.d.ts +2 -2
  166. package/dist/api/reportError.d.ts.map +1 -1
  167. package/dist/api/reportError.js +3 -2
  168. package/dist/api/routing/middleware/RouterMiddleware.d.ts +5 -4
  169. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  170. package/dist/api/routing/middleware/middleware.d.ts +42 -3
  171. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  172. package/dist/api/routing/middleware/middleware.js +53 -17
  173. package/dist/api/routing/middleware.d.ts +1 -2
  174. package/dist/api/routing/middleware.d.ts.map +1 -1
  175. package/dist/api/routing/middleware.js +1 -2
  176. package/dist/api/routing/schema/jwt.d.ts +1 -1
  177. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  178. package/dist/api/routing/schema/jwt.js +3 -2
  179. package/dist/api/routing/tsort.d.ts +1 -1
  180. package/dist/api/routing/tsort.d.ts.map +1 -1
  181. package/dist/api/routing/utils.d.ts +4 -4
  182. package/dist/api/routing/utils.d.ts.map +1 -1
  183. package/dist/api/routing/utils.js +3 -2
  184. package/dist/api/routing.d.ts +84 -37
  185. package/dist/api/routing.d.ts.map +1 -1
  186. package/dist/api/routing.js +115 -45
  187. package/dist/api/setupRequest.d.ts +10 -6
  188. package/dist/api/setupRequest.d.ts.map +1 -1
  189. package/dist/api/setupRequest.js +15 -7
  190. package/dist/api/util.d.ts +1 -1
  191. package/dist/arbs.d.ts +2 -2
  192. package/dist/arbs.d.ts.map +1 -1
  193. package/dist/arbs.js +5 -3
  194. package/dist/errorReporter.d.ts +7 -5
  195. package/dist/errorReporter.d.ts.map +1 -1
  196. package/dist/errorReporter.js +22 -26
  197. package/dist/errors.d.ts +1 -1
  198. package/dist/fileUtil.d.ts +2 -2
  199. package/dist/fileUtil.d.ts.map +1 -1
  200. package/dist/fileUtil.js +2 -2
  201. package/dist/index.d.ts +1 -1
  202. package/dist/logger/jsonLogger.d.ts +2 -2
  203. package/dist/logger/jsonLogger.d.ts.map +1 -1
  204. package/dist/logger/jsonLogger.js +4 -2
  205. package/dist/logger/logFmtLogger.d.ts +2 -2
  206. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  207. package/dist/logger/logFmtLogger.js +2 -2
  208. package/dist/logger/shared.d.ts +2 -2
  209. package/dist/logger/shared.d.ts.map +1 -1
  210. package/dist/logger/shared.js +3 -3
  211. package/dist/logger.d.ts +1 -1
  212. package/dist/logger.d.ts.map +1 -1
  213. package/dist/otel.d.ts +75 -0
  214. package/dist/otel.d.ts.map +1 -0
  215. package/dist/otel.js +65 -0
  216. package/dist/rateLimit.d.ts +12 -4
  217. package/dist/rateLimit.d.ts.map +1 -1
  218. package/dist/rateLimit.js +7 -12
  219. package/dist/test.d.ts +3 -3
  220. package/dist/test.d.ts.map +1 -1
  221. package/dist/test.js +2 -2
  222. package/dist/vitest.d.ts +1 -1
  223. package/examples/query.ts +46 -38
  224. package/package.json +46 -37
  225. package/src/CUPS.ts +15 -11
  226. package/src/Emailer/Sendgrid.ts +21 -15
  227. package/src/Emailer/fake.ts +1 -1
  228. package/src/Emailer/service.ts +13 -3
  229. package/src/MainFiberSet.ts +9 -6
  230. package/src/Model/Repository/Registry.ts +34 -0
  231. package/src/Model/Repository/ext.ts +103 -11
  232. package/src/Model/Repository/internal/internal.ts +231 -149
  233. package/src/Model/Repository/legacy.ts +3 -1
  234. package/src/Model/Repository/makeRepo.ts +15 -10
  235. package/src/Model/Repository/service.ts +35 -23
  236. package/src/Model/Repository/validation.ts +5 -5
  237. package/src/Model/Repository.ts +1 -0
  238. package/src/Model/dsl.ts +5 -4
  239. package/src/Model/filter/types/path/eager.ts +1 -2
  240. package/src/Model/query/dsl.ts +353 -19
  241. package/src/Model/query/new-kid-interpreter.ts +211 -6
  242. package/src/Model.ts +1 -0
  243. package/src/QueueMaker/SQLQueue.ts +150 -153
  244. package/src/QueueMaker/errors.ts +3 -1
  245. package/src/QueueMaker/memQueue.ts +111 -105
  246. package/src/QueueMaker/sbqueue.ts +76 -88
  247. package/src/QueueMaker/service.ts +3 -1
  248. package/src/RequestContext.ts +15 -16
  249. package/src/RequestFiberSet.ts +8 -2
  250. package/src/Store/ContextMapContainer.ts +45 -2
  251. package/src/Store/Cosmos/query.ts +143 -44
  252. package/src/Store/Cosmos.ts +491 -350
  253. package/src/Store/Disk.ts +106 -66
  254. package/src/Store/Memory.ts +285 -87
  255. package/src/Store/SQL/Pg.ts +364 -0
  256. package/src/Store/SQL/query.ts +540 -0
  257. package/src/Store/SQL.ts +736 -0
  258. package/src/Store/codeFilter.ts +5 -2
  259. package/src/Store/index.ts +20 -3
  260. package/src/Store/service.ts +45 -10
  261. package/src/Store/utils.ts +25 -23
  262. package/src/adapters/SQL/Model.ts +42 -41
  263. package/src/adapters/ServiceBus.ts +131 -121
  264. package/src/adapters/cosmos-client.ts +4 -2
  265. package/src/adapters/index.ts +7 -0
  266. package/src/adapters/memQueue.ts +5 -4
  267. package/src/adapters/mongo-client.ts +4 -2
  268. package/src/adapters/redis-client.ts +6 -2
  269. package/src/api/ContextProvider.ts +17 -13
  270. package/src/api/internal/RequestContextMiddleware.ts +16 -5
  271. package/src/api/internal/auth.ts +248 -44
  272. package/src/api/internal/events.ts +19 -10
  273. package/src/api/layerUtils.ts +12 -8
  274. package/src/api/reportError.ts +2 -1
  275. package/src/api/routing/middleware/RouterMiddleware.ts +5 -4
  276. package/src/api/routing/middleware/middleware.ts +60 -15
  277. package/src/api/routing/middleware.ts +0 -2
  278. package/src/api/routing/schema/jwt.ts +2 -1
  279. package/src/api/routing/utils.ts +2 -1
  280. package/src/api/routing.ts +304 -131
  281. package/src/api/setupRequest.ts +31 -8
  282. package/src/arbs.ts +5 -3
  283. package/src/errorReporter.ts +65 -75
  284. package/src/fileUtil.ts +1 -1
  285. package/src/logger/jsonLogger.ts +3 -1
  286. package/src/logger/logFmtLogger.ts +1 -1
  287. package/src/logger/shared.ts +3 -2
  288. package/src/otel.ts +152 -0
  289. package/src/rateLimit.ts +34 -23
  290. package/src/test.ts +2 -2
  291. package/test/auth.test.ts +101 -0
  292. package/test/contextProvider.test.ts +14 -11
  293. package/test/controller.test.ts +25 -29
  294. package/test/dist/auth.test.d.ts.map +1 -0
  295. package/test/dist/contextProvider.test.d.ts.map +1 -1
  296. package/test/dist/controller.test.d.ts.map +1 -1
  297. package/test/dist/date-query.test.d.ts.map +1 -0
  298. package/test/dist/fixtures.d.ts +30 -12
  299. package/test/dist/fixtures.d.ts.map +1 -1
  300. package/test/dist/fixtures.js +17 -10
  301. package/test/dist/query.test.d.ts.map +1 -1
  302. package/test/dist/rawQuery.test.d.ts.map +1 -1
  303. package/test/dist/repository-ext.test.d.ts.map +1 -0
  304. package/test/dist/requires.test.d.ts.map +1 -1
  305. package/test/dist/router-generator.test.d.ts.map +1 -0
  306. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  307. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  308. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  309. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  310. package/test/dist/sql-store.test.d.ts.map +1 -0
  311. package/test/fixtures.ts +16 -9
  312. package/test/layerUtils.test.ts +1 -1
  313. package/test/query.test.ts +819 -38
  314. package/test/rawQuery.test.ts +312 -20
  315. package/test/repository-ext.test.ts +62 -0
  316. package/test/requires.test.ts +10 -5
  317. package/test/router-generator.test.ts +187 -0
  318. package/test/routing-interruptibility.test.ts +66 -0
  319. package/test/rpc-e2e-invalidation.test.ts +256 -0
  320. package/test/rpc-multi-middleware.test.ts +84 -9
  321. package/test/rpc-stream-fullstack.test.ts +304 -0
  322. package/test/sql-store.test.ts +1592 -0
  323. package/test/validateSample.test.ts +17 -12
  324. package/tsconfig.examples.json +1 -1
  325. package/tsconfig.json +0 -1
  326. package/tsconfig.json.bak +2 -2
  327. package/tsconfig.src.json +35 -35
  328. package/tsconfig.test.json +2 -2
  329. package/dist/Operations.d.ts +0 -55
  330. package/dist/Operations.d.ts.map +0 -1
  331. package/dist/Operations.js +0 -102
  332. package/dist/OperationsRepo.d.ts +0 -41
  333. package/dist/OperationsRepo.d.ts.map +0 -1
  334. package/dist/OperationsRepo.js +0 -14
  335. package/eslint.config.mjs +0 -24
  336. package/src/Operations.ts +0 -235
  337. package/src/OperationsRepo.ts +0 -16
@@ -1,13 +1,94 @@
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 * as Array from "effect-app/Array"
4
4
  import { toNonEmptyArray } from "effect-app/Array"
5
+ import * as Option from "effect-app/Option"
6
+ import * as S from "effect-app/Schema"
5
7
  import { dropUndefinedT } from "effect-app/utils"
8
+ import { identity, pipe } from "effect/Function"
9
+ import * as Match from "effect/Match"
10
+ import * as SchemaAST from "effect/SchemaAST"
6
11
  import type { FilterResult } from "../filter/filterApi.js"
7
12
  import type { FieldValues } from "../filter/types.js"
8
13
  import type { FieldPath } from "../filter/types/path/eager.js"
9
14
  import { make, type Q, type QAll } from "../query/dsl.js"
10
15
 
16
+ export type ComputedProjectionMathIrExpression =
17
+ | {
18
+ readonly _tag: "field"
19
+ readonly field: string
20
+ }
21
+ | {
22
+ readonly _tag: "mul"
23
+ readonly left: ComputedProjectionMathIrExpression
24
+ readonly right: ComputedProjectionMathIrExpression
25
+ }
26
+
27
+ export type ComputedProjectionIrExpression =
28
+ | {
29
+ readonly _tag: "relation-count"
30
+ readonly path: string
31
+ readonly filter: readonly FilterResult[]
32
+ }
33
+ | {
34
+ readonly _tag: "relation-any"
35
+ readonly path: string
36
+ readonly filter: readonly FilterResult[]
37
+ }
38
+ | {
39
+ readonly _tag: "relation-every"
40
+ readonly path: string
41
+ readonly filter: readonly FilterResult[]
42
+ }
43
+ | {
44
+ readonly _tag: "relation-distinct-count"
45
+ readonly path: string
46
+ readonly field: string
47
+ readonly filter: readonly FilterResult[]
48
+ }
49
+ | {
50
+ readonly _tag: "relation-sum"
51
+ readonly path: string
52
+ readonly field: string
53
+ readonly filter: readonly FilterResult[]
54
+ }
55
+ | {
56
+ readonly _tag: "relation-sum-expr"
57
+ readonly path: string
58
+ readonly expression: ComputedProjectionMathIrExpression
59
+ readonly filter: readonly FilterResult[]
60
+ }
61
+ | {
62
+ readonly _tag: "relation-sum-expr-by"
63
+ readonly path: string
64
+ readonly expression: ComputedProjectionMathIrExpression
65
+ readonly unit: string
66
+ readonly filter: readonly FilterResult[]
67
+ }
68
+ | {
69
+ readonly _tag: "relation-sum-expr-normalized"
70
+ readonly path: string
71
+ readonly expression: ComputedProjectionMathIrExpression
72
+ readonly unit: string
73
+ readonly toBase: string
74
+ readonly factors: Readonly<Record<string, number>>
75
+ readonly filter: readonly FilterResult[]
76
+ }
77
+ | {
78
+ readonly _tag: "relation-collect"
79
+ readonly path: string
80
+ readonly field: string
81
+ readonly distinct: boolean
82
+ readonly filter: readonly FilterResult[]
83
+ }
84
+ | {
85
+ readonly _tag: "relation-collect-fields"
86
+ readonly path: string
87
+ readonly fields: readonly string[]
88
+ readonly distinct: boolean
89
+ readonly filter: readonly FilterResult[]
90
+ }
91
+
11
92
  type Result<TFieldValues extends FieldValues, A = TFieldValues, R = never> = {
12
93
  filter: FilterResult[]
13
94
  schema: S.Codec<A, TFieldValues, R> | undefined
@@ -16,6 +97,7 @@ type Result<TFieldValues extends FieldValues, A = TFieldValues, R = never> = {
16
97
  order: { key: FieldPath<TFieldValues>; direction: "ASC" | "DESC" }[]
17
98
  ttype: "one" | "many" | "count" | undefined
18
99
  mode: "collect" | "project" | "transform" | undefined
100
+ computed: Record<string, ComputedProjectionIrExpression> | undefined
19
101
  }
20
102
 
21
103
  const interpret = <
@@ -33,7 +115,8 @@ const interpret = <
33
115
  skip: undefined,
34
116
  order: [],
35
117
  ttype: undefined,
36
- mode: undefined
118
+ mode: undefined,
119
+ computed: undefined
37
120
  }
38
121
 
39
122
  const upd = (
@@ -46,6 +129,7 @@ const interpret = <
46
129
  if (v.ttype !== undefined) data.ttype = v.ttype
47
130
  if (v.schema !== undefined) data.schema = v.schema
48
131
  if (v.mode !== undefined) data.mode = v.mode
132
+ if (v.computed !== undefined) data.computed = v.computed
49
133
  }
50
134
 
51
135
  const applyPath = (path: string) => (_: FilterResult): FilterResult =>
@@ -135,8 +219,84 @@ const interpret = <
135
219
  },
136
220
  project: (v) => {
137
221
  upd(interpret(v.current))
222
+ if (v.computed && v.mode === "transform") {
223
+ throw new Error("Computed projections require mode 'project' or 'collect', not 'transform'")
224
+ }
138
225
  data.schema = v.schema
139
- data.mode = v.mode
226
+ data.mode = v.computed
227
+ ? v.mode === "collect" ? "collect" : "project"
228
+ : v.mode
229
+ data.computed = v.computed
230
+ ? Object.fromEntries(
231
+ Object.entries(v.computed).map(([key, expression]) => {
232
+ const e = expression
233
+ const filter = e.operation ? interpret(e.operation(make())).filter.map(applyPath(e.path)) : []
234
+ switch (e._tag) {
235
+ case "relation-count":
236
+ case "relation-any":
237
+ case "relation-every":
238
+ return [key, { _tag: e._tag, path: e.path, filter } as ComputedProjectionIrExpression]
239
+ case "relation-distinct-count":
240
+ case "relation-sum":
241
+ return [
242
+ key,
243
+ { _tag: e._tag, path: e.path, field: e.field, filter } as ComputedProjectionIrExpression
244
+ ]
245
+ case "relation-sum-expr":
246
+ return [
247
+ key,
248
+ { _tag: e._tag, path: e.path, expression: e.expression, filter } as ComputedProjectionIrExpression
249
+ ]
250
+ case "relation-sum-expr-by":
251
+ return [
252
+ key,
253
+ {
254
+ _tag: e._tag,
255
+ path: e.path,
256
+ expression: e.expression,
257
+ unit: e.unit,
258
+ filter
259
+ } as ComputedProjectionIrExpression
260
+ ]
261
+ case "relation-sum-expr-normalized":
262
+ return [
263
+ key,
264
+ {
265
+ _tag: e._tag,
266
+ path: e.path,
267
+ expression: e.expression,
268
+ unit: e.unit,
269
+ toBase: e.toBase,
270
+ factors: e.factors,
271
+ filter
272
+ } as ComputedProjectionIrExpression
273
+ ]
274
+ case "relation-collect":
275
+ return [
276
+ key,
277
+ {
278
+ _tag: e._tag,
279
+ path: e.path,
280
+ field: e.field,
281
+ distinct: e.distinct,
282
+ filter
283
+ } as ComputedProjectionIrExpression
284
+ ]
285
+ case "relation-collect-fields":
286
+ return [
287
+ key,
288
+ {
289
+ _tag: e._tag,
290
+ path: e.path,
291
+ fields: e.fields,
292
+ distinct: e.distinct,
293
+ filter
294
+ } as ComputedProjectionIrExpression
295
+ ]
296
+ }
297
+ })
298
+ )
299
+ : undefined
140
300
  }
141
301
  })
142
302
  )
@@ -157,15 +317,19 @@ export const toFilter = <
157
317
  R,
158
318
  TFieldValuesRefined extends TFieldValues = TFieldValues
159
319
  >(
160
- q: QAll<TFieldValues, TFieldValuesRefined, A, R>
320
+ q: QAll<TFieldValues, TFieldValuesRefined, A, R>,
321
+ baseSchema?: S.Schema<unknown>
161
322
  ) => {
162
323
  // TODO: Native interpreter for each db adapter, instead of the intermediate "new-kid" format
163
324
  const a = interpret(q)
164
325
  const schema = a.schema
165
- let select: (keyof TFieldValues | { key: string; subKeys: string[] })[] = []
326
+ let select: (keyof TFieldValues | { key: string; subKeys: string[] } | {
327
+ key: string
328
+ computed: ComputedProjectionIrExpression
329
+ })[] = []
166
330
  // TODO: support more complex (nested) schemas?
167
331
  if (schema) {
168
- const t = walkTransformation(schema.ast)
332
+ const t = walkTransformation(SchemaAST.toEncoded(schema.ast))
169
333
  if (S.AST.isObjects(t)) {
170
334
  select = t.propertySignatures.map((_) => _.name as string)
171
335
  for (const prop of t.propertySignatures) {
@@ -194,12 +358,53 @@ export const toFilter = <
194
358
  }
195
359
  }
196
360
  }
361
+ const computed = a.computed
362
+ const getSelectKey = (_: (typeof select)[number]) => {
363
+ if (typeof _ === "string") {
364
+ return _
365
+ }
366
+ if (typeof _ === "object" && _ !== null && "key" in _) {
367
+ return _.key
368
+ }
369
+ return String(_)
370
+ }
371
+ const schemaKeys = select.map(getSelectKey)
372
+ const nonEncodedSchemaKeys = (() => {
373
+ if (!baseSchema) {
374
+ return [] as string[]
375
+ }
376
+ const encoded = walkTransformation(SchemaAST.toEncoded(baseSchema.ast))
377
+ if (!S.AST.isObjects(encoded)) {
378
+ return [] as string[]
379
+ }
380
+ const encodedKeys = encoded.propertySignatures.map((_) => _.name as string)
381
+ return schemaKeys.filter((key) => !encodedKeys.includes(key))
382
+ })()
383
+ const missingComputedKeys = nonEncodedSchemaKeys.filter((key) => !(computed && key in computed))
384
+
385
+ if (Array.isArrayNonEmpty(missingComputedKeys)) {
386
+ throw new Error(`Missing computed projections for schema keys: ${missingComputedKeys.join(", ")}`)
387
+ }
388
+
389
+ if (computed) {
390
+ const computedKeys = Object.keys(computed)
391
+ const extraComputedKeys = computedKeys.filter((key) => !schemaKeys.includes(key))
392
+ if (Array.isArrayNonEmpty(extraComputedKeys)) {
393
+ throw new Error(`Computed projection keys must exist in projection schema: ${extraComputedKeys.join(", ")}`)
394
+ }
395
+ select = select.filter((_) => {
396
+ const key = getSelectKey(_)
397
+ return !(key in computed)
398
+ })
399
+ select.push(...Object.entries(computed).map(([key, expression]) => ({ key, computed: expression })))
400
+ }
197
401
  return dropUndefinedT({
198
402
  t: null as unknown as TFieldValues,
199
403
  limit: a.limit,
200
404
  skip: a.skip,
201
405
  select: Option.getOrUndefined(toNonEmptyArray(select)),
202
406
  schema,
407
+ computed,
203
408
  order: Option.getOrUndefined(toNonEmptyArray(a.order)),
204
409
  ttype: a.ttype,
205
410
  mode: a.mode ?? "transform",
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"
@@ -2,18 +2,24 @@ import { getRequestContext, setupRequestContextWithCustomSpan } from "@effect-ap
2
2
  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
- import { Effect, Fiber, type NonEmptyReadonlyArray, Option, S, Tracer } from "effect-app"
6
- import type { NonEmptyString255 } from "effect-app/Schema"
5
+ import type { NonEmptyReadonlyArray } from "effect-app/Array"
6
+ import * as Effect from "effect-app/Effect"
7
+ import * as Option from "effect-app/Option"
8
+ import * as S from "effect-app/Schema"
9
+ import { type NonEmptyString255 } from "effect-app/Schema"
7
10
  import { pretty } from "effect-app/utils"
11
+ import * as Fiber from "effect/Fiber"
12
+ import * as Tracer from "effect/Tracer"
8
13
  import { SqlClient } from "effect/unstable/sql"
9
14
  import { SQLModel } from "../adapters/SQL.js"
10
15
  import { InfraLogger } from "../logger.js"
16
+ import { messagingSpanArgs } from "../otel.js"
11
17
 
12
- export const QueueId = S.Number.pipe(S.brand("QueueId"))
18
+ export const QueueId = S.Finite.pipe(S.brand("QueueId"))
13
19
  export type QueueId = typeof QueueId.Type
14
20
 
15
21
  // TODO: let the model track and Auto Generate versionColumn on every update instead
16
- export function makeSQLQueue<
22
+ export const makeSQLQueue = Effect.fnUntraced(function*<
17
23
  Evt extends { id: S.StringId; _tag: string },
18
24
  DrainEvt extends { id: S.StringId; _tag: string },
19
25
  EvtE,
@@ -24,166 +30,157 @@ export function makeSQLQueue<
24
30
  schema: S.Codec<Evt, EvtE>,
25
31
  drainSchema: S.Codec<DrainEvt, DrainEvtE>
26
32
  ) {
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
33
+ const base = {
34
+ id: SQLModel.Generated(QueueId),
35
+ meta: SQLModel.JsonFromString(QueueMeta),
36
+ name: S.NonEmptyString255,
37
+ createdAt: SQLModel.DateTimeInsert,
38
+ updatedAt: SQLModel.DateTimeUpdate,
39
+ // TODO: at+owner
40
+ processingAt: SQLModel.FieldOption(S.Date),
41
+ finishedAt: SQLModel.FieldOption(S.Date),
42
+ etag: S.String // TODO: use a SQLModel thing that auto updates it?
43
+ // TODO: record locking.. / optimistic locking
44
+ // rowVersion: SQLModel.DateTimeFromNumberWithNow
45
+ }
46
+ class Queue extends SQLModel.Class<Queue>("Queue")({
47
+ body: SQLModel.JsonFromString(schema),
48
+ ...base
49
+ }) {}
50
+ class Drain extends SQLModel.Class<Drain>("Drain")({
51
+ body: SQLModel.JsonFromString(drainSchema),
52
+ ...base
53
+ }) {}
54
+ const sql = yield* SqlClient.SqlClient
50
55
 
51
- const queueRepo = yield* SQLModel.makeRepository(Queue, {
52
- tableName: "queue",
53
- spanPrefix: "QueueRepo",
54
- idColumn: "id",
55
- versionColumn: "etag"
56
- })
56
+ const queueRepo = yield* SQLModel.makeRepository(Queue, {
57
+ tableName: "queue",
58
+ spanPrefix: "QueueRepo",
59
+ idColumn: "id",
60
+ versionColumn: "etag"
61
+ })
57
62
 
58
- const drainRepo = yield* SQLModel.makeRepository(Drain, {
59
- tableName: "queue",
60
- spanPrefix: "DrainRepo",
61
- idColumn: "id",
62
- versionColumn: "etag"
63
- })
63
+ const drainRepo = yield* SQLModel.makeRepository(Drain, {
64
+ tableName: "queue",
65
+ spanPrefix: "DrainRepo",
66
+ idColumn: "id",
67
+ versionColumn: "etag"
68
+ })
64
69
 
65
- const decodeDrain = S.decodeEffect(Drain)
70
+ const decodeDrain = S.decodeEffectConcurrently(Drain)
66
71
 
67
- const drain = Effect
68
- .sync(() => subMinutes(new Date(), 15))
69
- .pipe(
70
- Effect
71
- .andThen((limit) =>
72
- sql<typeof Drain.Encoded>`SELECT *
72
+ const drain = Effect.gen(function*() {
73
+ const limit = subMinutes(new Date(), 15)
74
+ return yield* sql<typeof Drain.Encoded>`SELECT *
73
75
  FROM queue
74
76
  WHERE name = ${queueDrainName} AND finishedAt IS NULL AND (processingAt IS NULL OR processingAt < ${limit.getTime()})
75
77
  LIMIT 1`
76
- )
77
- )
78
+ })
78
79
 
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)
80
+ const q = {
81
+ offer: Effect.fnUntraced(function*(body: Evt, meta: typeof QueueMeta.Type) {
82
+ yield* queueRepo.insertVoid(Queue.insert.make({
83
+ body,
84
+ meta,
85
+ name: queueName,
86
+ processingAt: Option.none(),
87
+ finishedAt: Option.none(),
88
+ etag: crypto.randomUUID()
89
+ }))
90
+ }),
91
+ take: Effect.gen(function*() {
92
+ while (true) {
93
+ const [first] = yield* drain.pipe(Effect.withTracerEnabled(false)) // disable sql tracer otherwise we spam it..
94
+ if (first) {
95
+ const dec = yield* decodeDrain(first)
96
+ const { createdAt, updatedAt, ...rest } = dec
97
+ return yield* drainRepo.update(
98
+ Drain.update.make({ ...rest, processingAt: Option.some(new Date()) }) // auto in lib , etag: crypto.randomUUID()
99
+ )
102
100
  }
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
101
+ if (first) return first
102
+ yield* Effect.sleep(250)
103
+ }
104
+ }),
105
+ finish: Effect.fn(function*({ createdAt, updatedAt, ...q }: Drain) {
106
+ return yield* drainRepo.updateVoid(Drain.update.make({ ...q, finishedAt: Option.some(new Date()) })) // auto in lib , etag: crypto.randomUUID()
107
+ })
108
+ }
109
+ const queue = {
110
+ publish: Effect.fn(`publish ${queueName}`, {
111
+ kind: "producer",
112
+ attributes: {
113
+ "messaging.system": "sql",
114
+ "messaging.operation.name": "publish",
115
+ "messaging.destination.name": queueName
116
+ }
117
+ })(function*(
118
+ ...messages: NonEmptyReadonlyArray<Evt>
119
+ ) {
120
+ yield* Effect.annotateCurrentSpan({
121
+ "messaging.batch.message_count": messages.length,
122
+ "messaging.message.types": messages.map((_) => _._tag)
123
+ })
124
+ const requestContext = yield* getRequestContext
125
+ yield* Effect.forEach(messages, (m) => q.offer(m, requestContext), { discard: true })
126
+ }),
127
+ drain: <DrainE, DrainR>(
128
+ handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
129
+ sessionId?: string
130
+ ) => {
131
+ const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
132
+ const processMessage = Effect.fnUntraced(function*({ body, meta }: Drain) {
133
+ let effect = InfraLogger
134
+ .logDebug(`[${queueDrainName}] Processing incoming message`)
110
135
  .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))
136
+ Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
137
+ Effect.andThen(handleEvent(body)),
138
+ silenceAndReportError,
139
+ (_) => {
140
+ const args = messagingSpanArgs({
141
+ operation: "process",
142
+ system: "sql",
143
+ destination: queueDrainName,
144
+ messageId: body.id,
145
+ conversationId: sessionId,
146
+ extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
147
+ }, "consumer")
148
+ return setupRequestContextWithCustomSpan(
149
+ _,
150
+ meta,
151
+ args.name,
152
+ {
153
+ captureStackTrace: false,
154
+ kind: args.kind,
155
+ attributes: args.attributes
160
156
  }
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
157
  )
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
158
+ }
184
159
  )
185
- }
160
+ if (meta.span) {
161
+ effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
162
+ }
163
+ return yield* effect
164
+ })
165
+
166
+ return Effect.fn(`receive ${queueDrainName}`, {
167
+ kind: "consumer",
168
+ attributes: {
169
+ "messaging.system": "sql",
170
+ "messaging.operation.name": "receive",
171
+ "messaging.destination.name": queueDrainName,
172
+ ...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
173
+ }
174
+ })(function*() {
175
+ const x = yield* q.take
176
+ yield* processMessage(x).pipe(
177
+ Effect.uninterruptible,
178
+ Effect.forkChild,
179
+ Effect.flatMap(Fiber.join),
180
+ Effect.tap(q.finish(x))
181
+ )
182
+ }, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
186
183
  }
187
- return queue as QueueBase<Evt, DrainEvt>
188
- })
189
- }
184
+ }
185
+ return queue as QueueBase<Evt, DrainEvt>
186
+ })
@@ -1,5 +1,7 @@
1
1
  import { reportError } from "@effect-app/infra/errorReporter"
2
- import { Cause, Effect, Exit } from "effect-app"
2
+ import * as Effect from "effect-app/Effect"
3
+ import * as Cause from "effect/Cause"
4
+ import * as Exit from "effect/Exit"
3
5
 
4
6
  const reportQueueError_ = reportError("Queue")
5
7