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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/CHANGELOG.md +1640 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +7 -7
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +10 -12
  6. package/dist/Emailer/Sendgrid.d.ts +14 -14
  7. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  8. package/dist/Emailer/Sendgrid.js +16 -15
  9. package/dist/Emailer/fake.d.ts +1 -1
  10. package/dist/Emailer/service.d.ts +10 -4
  11. package/dist/Emailer/service.d.ts.map +1 -1
  12. package/dist/Emailer/service.js +3 -3
  13. package/dist/Emailer.d.ts +1 -1
  14. package/dist/MainFiberSet.d.ts +9 -9
  15. package/dist/MainFiberSet.d.ts.map +1 -1
  16. package/dist/MainFiberSet.js +3 -3
  17. package/dist/Model/Repository/Registry.d.ts +20 -0
  18. package/dist/Model/Repository/Registry.d.ts.map +1 -0
  19. package/dist/Model/Repository/Registry.js +17 -0
  20. package/dist/Model/Repository/ext.d.ts +33 -15
  21. package/dist/Model/Repository/ext.d.ts.map +1 -1
  22. package/dist/Model/Repository/ext.js +54 -2
  23. package/dist/Model/Repository/internal/internal.d.ts +6 -6
  24. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  25. package/dist/Model/Repository/internal/internal.js +103 -51
  26. package/dist/Model/Repository/legacy.d.ts +1 -1
  27. package/dist/Model/Repository/makeRepo.d.ts +7 -6
  28. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  29. package/dist/Model/Repository/makeRepo.js +5 -1
  30. package/dist/Model/Repository/service.d.ts +28 -23
  31. package/dist/Model/Repository/service.d.ts.map +1 -1
  32. package/dist/Model/Repository/validation.d.ts +46 -17
  33. package/dist/Model/Repository/validation.d.ts.map +1 -1
  34. package/dist/Model/Repository/validation.js +5 -5
  35. package/dist/Model/Repository.d.ts +2 -1
  36. package/dist/Model/Repository.d.ts.map +1 -1
  37. package/dist/Model/Repository.js +2 -1
  38. package/dist/Model/dsl.d.ts +4 -4
  39. package/dist/Model/dsl.d.ts.map +1 -1
  40. package/dist/Model/filter/filterApi.d.ts +5 -5
  41. package/dist/Model/filter/filterApi.d.ts.map +1 -1
  42. package/dist/Model/filter/types/errors.d.ts +1 -1
  43. package/dist/Model/filter/types/fields.d.ts +1 -1
  44. package/dist/Model/filter/types/path/common.d.ts +1 -1
  45. package/dist/Model/filter/types/path/eager.d.ts +1 -1
  46. package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
  47. package/dist/Model/filter/types/path/eager.js +1 -1
  48. package/dist/Model/filter/types/path/index.d.ts +1 -1
  49. package/dist/Model/filter/types/utils.d.ts +1 -1
  50. package/dist/Model/filter/types/validator.d.ts +1 -1
  51. package/dist/Model/filter/types.d.ts +1 -1
  52. package/dist/Model/query/dsl.d.ts +139 -16
  53. package/dist/Model/query/dsl.d.ts.map +1 -1
  54. package/dist/Model/query/dsl.js +187 -1
  55. package/dist/Model/query/new-kid-interpreter.d.ts +76 -7
  56. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  57. package/dist/Model/query/new-kid-interpreter.js +122 -6
  58. package/dist/Model/query.d.ts +1 -1
  59. package/dist/Model.d.ts +2 -1
  60. package/dist/Model.d.ts.map +1 -1
  61. package/dist/Model.js +2 -1
  62. package/dist/QueueMaker/SQLQueue.d.ts +5 -7
  63. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  64. package/dist/QueueMaker/SQLQueue.js +130 -116
  65. package/dist/QueueMaker/errors.d.ts +2 -2
  66. package/dist/QueueMaker/errors.d.ts.map +1 -1
  67. package/dist/QueueMaker/memQueue.d.ts +7 -4
  68. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  69. package/dist/QueueMaker/memQueue.js +75 -63
  70. package/dist/QueueMaker/sbqueue.d.ts +6 -3
  71. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  72. package/dist/QueueMaker/sbqueue.js +52 -53
  73. package/dist/QueueMaker/service.d.ts +1 -1
  74. package/dist/RequestContext.d.ts +74 -35
  75. package/dist/RequestContext.d.ts.map +1 -1
  76. package/dist/RequestContext.js +13 -14
  77. package/dist/RequestFiberSet.d.ts +7 -7
  78. package/dist/RequestFiberSet.d.ts.map +1 -1
  79. package/dist/RequestFiberSet.js +3 -3
  80. package/dist/Store/ContextMapContainer.d.ts +19 -3
  81. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  82. package/dist/Store/ContextMapContainer.js +13 -3
  83. package/dist/Store/Cosmos/query.d.ts +5 -1
  84. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  85. package/dist/Store/Cosmos/query.js +113 -34
  86. package/dist/Store/Cosmos.d.ts +1 -1
  87. package/dist/Store/Cosmos.d.ts.map +1 -1
  88. package/dist/Store/Cosmos.js +335 -243
  89. package/dist/Store/Disk.d.ts +2 -2
  90. package/dist/Store/Disk.d.ts.map +1 -1
  91. package/dist/Store/Disk.js +72 -35
  92. package/dist/Store/Memory.d.ts +6 -4
  93. package/dist/Store/Memory.d.ts.map +1 -1
  94. package/dist/Store/Memory.js +242 -58
  95. package/dist/Store/SQL/Pg.d.ts +4 -0
  96. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  97. package/dist/Store/SQL/Pg.js +231 -0
  98. package/dist/Store/SQL/query.d.ts +42 -0
  99. package/dist/Store/SQL/query.d.ts.map +1 -0
  100. package/dist/Store/SQL/query.js +479 -0
  101. package/dist/Store/SQL.d.ts +20 -0
  102. package/dist/Store/SQL.d.ts.map +1 -0
  103. package/dist/Store/SQL.js +446 -0
  104. package/dist/Store/codeFilter.d.ts +1 -1
  105. package/dist/Store/codeFilter.d.ts.map +1 -1
  106. package/dist/Store/codeFilter.js +4 -2
  107. package/dist/Store/index.d.ts +5 -2
  108. package/dist/Store/index.d.ts.map +1 -1
  109. package/dist/Store/index.js +15 -3
  110. package/dist/Store/service.d.ts +22 -8
  111. package/dist/Store/service.d.ts.map +1 -1
  112. package/dist/Store/service.js +24 -6
  113. package/dist/Store/utils.d.ts +1 -1
  114. package/dist/Store/utils.d.ts.map +1 -1
  115. package/dist/Store/utils.js +3 -4
  116. package/dist/Store.d.ts +1 -1
  117. package/dist/adapters/SQL/Model.d.ts +31 -42
  118. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  119. package/dist/adapters/SQL/Model.js +29 -38
  120. package/dist/adapters/SQL.d.ts +1 -1
  121. package/dist/adapters/ServiceBus.d.ts +11 -11
  122. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  123. package/dist/adapters/ServiceBus.js +25 -21
  124. package/dist/adapters/cosmos-client.d.ts +3 -3
  125. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  126. package/dist/adapters/cosmos-client.js +3 -3
  127. package/dist/adapters/index.d.ts +8 -2
  128. package/dist/adapters/index.d.ts.map +1 -1
  129. package/dist/adapters/index.js +8 -2
  130. package/dist/adapters/logger.d.ts +1 -1
  131. package/dist/adapters/logger.d.ts.map +1 -1
  132. package/dist/adapters/memQueue.d.ts +3 -3
  133. package/dist/adapters/memQueue.d.ts.map +1 -1
  134. package/dist/adapters/memQueue.js +3 -3
  135. package/dist/adapters/mongo-client.d.ts +3 -3
  136. package/dist/adapters/mongo-client.d.ts.map +1 -1
  137. package/dist/adapters/mongo-client.js +3 -3
  138. package/dist/adapters/redis-client.d.ts +3 -3
  139. package/dist/adapters/redis-client.d.ts.map +1 -1
  140. package/dist/adapters/redis-client.js +3 -3
  141. package/dist/api/ContextProvider.d.ts +8 -8
  142. package/dist/api/ContextProvider.d.ts.map +1 -1
  143. package/dist/api/ContextProvider.js +6 -6
  144. package/dist/api/codec.d.ts +1 -1
  145. package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
  146. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  147. package/dist/api/internal/RequestContextMiddleware.js +9 -6
  148. package/dist/api/internal/auth.d.ts +44 -6
  149. package/dist/api/internal/auth.d.ts.map +1 -1
  150. package/dist/api/internal/auth.js +160 -29
  151. package/dist/api/internal/events.d.ts +3 -3
  152. package/dist/api/internal/events.d.ts.map +1 -1
  153. package/dist/api/internal/events.js +10 -8
  154. package/dist/api/internal/health.d.ts +1 -1
  155. package/dist/api/layerUtils.d.ts +6 -6
  156. package/dist/api/layerUtils.d.ts.map +1 -1
  157. package/dist/api/layerUtils.js +5 -5
  158. package/dist/api/middlewares.d.ts +1 -1
  159. package/dist/api/reportError.d.ts +1 -1
  160. package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
  161. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  162. package/dist/api/routing/middleware/middleware.d.ts +39 -3
  163. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  164. package/dist/api/routing/middleware/middleware.js +48 -16
  165. package/dist/api/routing/middleware.d.ts +1 -2
  166. package/dist/api/routing/middleware.d.ts.map +1 -1
  167. package/dist/api/routing/middleware.js +1 -2
  168. package/dist/api/routing/schema/jwt.d.ts +1 -1
  169. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  170. package/dist/api/routing/tsort.d.ts +1 -1
  171. package/dist/api/routing/tsort.d.ts.map +1 -1
  172. package/dist/api/routing/utils.d.ts +3 -3
  173. package/dist/api/routing/utils.d.ts.map +1 -1
  174. package/dist/api/routing.d.ts +80 -37
  175. package/dist/api/routing.d.ts.map +1 -1
  176. package/dist/api/routing.js +109 -41
  177. package/dist/api/setupRequest.d.ts +8 -5
  178. package/dist/api/setupRequest.d.ts.map +1 -1
  179. package/dist/api/setupRequest.js +12 -7
  180. package/dist/api/util.d.ts +1 -1
  181. package/dist/arbs.d.ts +1 -1
  182. package/dist/arbs.d.ts.map +1 -1
  183. package/dist/arbs.js +5 -3
  184. package/dist/errorReporter.d.ts +4 -4
  185. package/dist/errorReporter.d.ts.map +1 -1
  186. package/dist/errorReporter.js +20 -25
  187. package/dist/errors.d.ts +1 -1
  188. package/dist/fileUtil.d.ts +1 -1
  189. package/dist/fileUtil.d.ts.map +1 -1
  190. package/dist/index.d.ts +1 -1
  191. package/dist/logger/jsonLogger.d.ts +1 -1
  192. package/dist/logger/logFmtLogger.d.ts +1 -1
  193. package/dist/logger/shared.d.ts +1 -1
  194. package/dist/logger/shared.js +2 -2
  195. package/dist/logger.d.ts +1 -1
  196. package/dist/logger.d.ts.map +1 -1
  197. package/dist/otel.d.ts +75 -0
  198. package/dist/otel.d.ts.map +1 -0
  199. package/dist/otel.js +65 -0
  200. package/dist/rateLimit.d.ts +9 -3
  201. package/dist/rateLimit.d.ts.map +1 -1
  202. package/dist/rateLimit.js +5 -11
  203. package/dist/test.d.ts +2 -2
  204. package/dist/test.d.ts.map +1 -1
  205. package/dist/test.js +1 -1
  206. package/dist/vitest.d.ts +1 -1
  207. package/examples/query.ts +42 -38
  208. package/package.json +46 -37
  209. package/src/CUPS.ts +9 -11
  210. package/src/Emailer/Sendgrid.ts +17 -14
  211. package/src/Emailer/service.ts +9 -3
  212. package/src/MainFiberSet.ts +5 -6
  213. package/src/Model/Repository/Registry.ts +33 -0
  214. package/src/Model/Repository/ext.ts +96 -10
  215. package/src/Model/Repository/internal/internal.ts +218 -149
  216. package/src/Model/Repository/makeRepo.ts +12 -10
  217. package/src/Model/Repository/service.ts +31 -22
  218. package/src/Model/Repository/validation.ts +4 -4
  219. package/src/Model/Repository.ts +1 -0
  220. package/src/Model/dsl.ts +3 -3
  221. package/src/Model/filter/types/path/eager.ts +1 -2
  222. package/src/Model/query/dsl.ts +348 -18
  223. package/src/Model/query/new-kid-interpreter.ts +206 -6
  224. package/src/Model.ts +1 -0
  225. package/src/QueueMaker/SQLQueue.ts +144 -152
  226. package/src/QueueMaker/memQueue.ts +104 -103
  227. package/src/QueueMaker/sbqueue.ts +70 -86
  228. package/src/RequestContext.ts +14 -16
  229. package/src/RequestFiberSet.ts +2 -2
  230. package/src/Store/ContextMapContainer.ts +41 -2
  231. package/src/Store/Cosmos/query.ts +140 -43
  232. package/src/Store/Cosmos.ts +482 -349
  233. package/src/Store/Disk.ts +102 -65
  234. package/src/Store/Memory.ts +275 -87
  235. package/src/Store/SQL/Pg.ts +361 -0
  236. package/src/Store/SQL/query.ts +539 -0
  237. package/src/Store/SQL.ts +731 -0
  238. package/src/Store/codeFilter.ts +3 -1
  239. package/src/Store/index.ts +17 -2
  240. package/src/Store/service.ts +41 -10
  241. package/src/Store/utils.ts +23 -22
  242. package/src/adapters/SQL/Model.ts +41 -40
  243. package/src/adapters/ServiceBus.ts +125 -121
  244. package/src/adapters/cosmos-client.ts +2 -2
  245. package/src/adapters/index.ts +7 -0
  246. package/src/adapters/memQueue.ts +2 -2
  247. package/src/adapters/mongo-client.ts +2 -2
  248. package/src/adapters/redis-client.ts +2 -2
  249. package/src/api/ContextProvider.ts +12 -13
  250. package/src/api/internal/RequestContextMiddleware.ts +15 -5
  251. package/src/api/internal/auth.ts +246 -44
  252. package/src/api/internal/events.ts +13 -9
  253. package/src/api/layerUtils.ts +8 -8
  254. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  255. package/src/api/routing/middleware/middleware.ts +55 -14
  256. package/src/api/routing/middleware.ts +0 -2
  257. package/src/api/routing.ts +296 -131
  258. package/src/api/setupRequest.ts +28 -8
  259. package/src/arbs.ts +4 -2
  260. package/src/errorReporter.ts +62 -74
  261. package/src/logger/shared.ts +1 -1
  262. package/src/otel.ts +152 -0
  263. package/src/rateLimit.ts +30 -22
  264. package/src/test.ts +1 -1
  265. package/test/auth.test.ts +101 -0
  266. package/test/contextProvider.test.ts +11 -11
  267. package/test/controller.test.ts +21 -30
  268. package/test/dist/auth.test.d.ts.map +1 -0
  269. package/test/dist/contextProvider.test.d.ts.map +1 -1
  270. package/test/dist/controller.test.d.ts.map +1 -1
  271. package/test/dist/date-query.test.d.ts.map +1 -0
  272. package/test/dist/fixtures.d.ts +26 -12
  273. package/test/dist/fixtures.d.ts.map +1 -1
  274. package/test/dist/fixtures.js +12 -10
  275. package/test/dist/query.test.d.ts.map +1 -1
  276. package/test/dist/rawQuery.test.d.ts.map +1 -1
  277. package/test/dist/repository-ext.test.d.ts.map +1 -0
  278. package/test/dist/requires.test.d.ts.map +1 -1
  279. package/test/dist/router-generator.test.d.ts.map +1 -0
  280. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  281. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  282. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  283. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  284. package/test/dist/sql-store.test.d.ts.map +1 -0
  285. package/test/fixtures.ts +11 -9
  286. package/test/query.test.ts +813 -38
  287. package/test/rawQuery.test.ts +301 -20
  288. package/test/repository-ext.test.ts +60 -0
  289. package/test/requires.test.ts +6 -6
  290. package/test/router-generator.test.ts +183 -0
  291. package/test/routing-interruptibility.test.ts +63 -0
  292. package/test/rpc-e2e-invalidation.test.ts +251 -0
  293. package/test/rpc-multi-middleware.test.ts +78 -9
  294. package/test/rpc-stream-fullstack.test.ts +300 -0
  295. package/test/sql-store.test.ts +1592 -0
  296. package/test/validateSample.test.ts +15 -12
  297. package/tsconfig.examples.json +1 -1
  298. package/tsconfig.json +0 -1
  299. package/tsconfig.json.bak +2 -2
  300. package/tsconfig.src.json +35 -35
  301. package/tsconfig.test.json +2 -2
  302. package/dist/Operations.d.ts +0 -55
  303. package/dist/Operations.d.ts.map +0 -1
  304. package/dist/Operations.js +0 -102
  305. package/dist/OperationsRepo.d.ts +0 -41
  306. package/dist/OperationsRepo.d.ts.map +0 -1
  307. package/dist/OperationsRepo.js +0 -14
  308. package/eslint.config.mjs +0 -24
  309. package/src/Operations.ts +0 -235
  310. package/src/OperationsRepo.ts +0 -16
@@ -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"
@@ -8,6 +8,82 @@ import type { FieldValues } from "../filter/types.js"
8
8
  import type { FieldPath } from "../filter/types/path/eager.js"
9
9
  import { make, type Q, type QAll } from "../query/dsl.js"
10
10
 
11
+ export type ComputedProjectionMathIrExpression =
12
+ | {
13
+ readonly _tag: "field"
14
+ readonly field: string
15
+ }
16
+ | {
17
+ readonly _tag: "mul"
18
+ readonly left: ComputedProjectionMathIrExpression
19
+ readonly right: ComputedProjectionMathIrExpression
20
+ }
21
+
22
+ export type ComputedProjectionIrExpression =
23
+ | {
24
+ readonly _tag: "relation-count"
25
+ readonly path: string
26
+ readonly filter: readonly FilterResult[]
27
+ }
28
+ | {
29
+ readonly _tag: "relation-any"
30
+ readonly path: string
31
+ readonly filter: readonly FilterResult[]
32
+ }
33
+ | {
34
+ readonly _tag: "relation-every"
35
+ readonly path: string
36
+ readonly filter: readonly FilterResult[]
37
+ }
38
+ | {
39
+ readonly _tag: "relation-distinct-count"
40
+ readonly path: string
41
+ readonly field: string
42
+ readonly filter: readonly FilterResult[]
43
+ }
44
+ | {
45
+ readonly _tag: "relation-sum"
46
+ readonly path: string
47
+ readonly field: string
48
+ readonly filter: readonly FilterResult[]
49
+ }
50
+ | {
51
+ readonly _tag: "relation-sum-expr"
52
+ readonly path: string
53
+ readonly expression: ComputedProjectionMathIrExpression
54
+ readonly filter: readonly FilterResult[]
55
+ }
56
+ | {
57
+ readonly _tag: "relation-sum-expr-by"
58
+ readonly path: string
59
+ readonly expression: ComputedProjectionMathIrExpression
60
+ readonly unit: string
61
+ readonly filter: readonly FilterResult[]
62
+ }
63
+ | {
64
+ readonly _tag: "relation-sum-expr-normalized"
65
+ readonly path: string
66
+ readonly expression: ComputedProjectionMathIrExpression
67
+ readonly unit: string
68
+ readonly toBase: string
69
+ readonly factors: Readonly<Record<string, number>>
70
+ readonly filter: readonly FilterResult[]
71
+ }
72
+ | {
73
+ readonly _tag: "relation-collect"
74
+ readonly path: string
75
+ readonly field: string
76
+ readonly distinct: boolean
77
+ readonly filter: readonly FilterResult[]
78
+ }
79
+ | {
80
+ readonly _tag: "relation-collect-fields"
81
+ readonly path: string
82
+ readonly fields: readonly string[]
83
+ readonly distinct: boolean
84
+ readonly filter: readonly FilterResult[]
85
+ }
86
+
11
87
  type Result<TFieldValues extends FieldValues, A = TFieldValues, R = never> = {
12
88
  filter: FilterResult[]
13
89
  schema: S.Codec<A, TFieldValues, R> | undefined
@@ -16,6 +92,7 @@ type Result<TFieldValues extends FieldValues, A = TFieldValues, R = never> = {
16
92
  order: { key: FieldPath<TFieldValues>; direction: "ASC" | "DESC" }[]
17
93
  ttype: "one" | "many" | "count" | undefined
18
94
  mode: "collect" | "project" | "transform" | undefined
95
+ computed: Record<string, ComputedProjectionIrExpression> | undefined
19
96
  }
20
97
 
21
98
  const interpret = <
@@ -33,7 +110,8 @@ const interpret = <
33
110
  skip: undefined,
34
111
  order: [],
35
112
  ttype: undefined,
36
- mode: undefined
113
+ mode: undefined,
114
+ computed: undefined
37
115
  }
38
116
 
39
117
  const upd = (
@@ -46,6 +124,7 @@ const interpret = <
46
124
  if (v.ttype !== undefined) data.ttype = v.ttype
47
125
  if (v.schema !== undefined) data.schema = v.schema
48
126
  if (v.mode !== undefined) data.mode = v.mode
127
+ if (v.computed !== undefined) data.computed = v.computed
49
128
  }
50
129
 
51
130
  const applyPath = (path: string) => (_: FilterResult): FilterResult =>
@@ -135,8 +214,84 @@ const interpret = <
135
214
  },
136
215
  project: (v) => {
137
216
  upd(interpret(v.current))
217
+ if (v.computed && v.mode === "transform") {
218
+ throw new Error("Computed projections require mode 'project' or 'collect', not 'transform'")
219
+ }
138
220
  data.schema = v.schema
139
- data.mode = v.mode
221
+ data.mode = v.computed
222
+ ? v.mode === "collect" ? "collect" : "project"
223
+ : v.mode
224
+ data.computed = v.computed
225
+ ? Object.fromEntries(
226
+ Object.entries(v.computed).map(([key, expression]) => {
227
+ const e = expression
228
+ const filter = e.operation ? interpret(e.operation(make())).filter.map(applyPath(e.path)) : []
229
+ switch (e._tag) {
230
+ case "relation-count":
231
+ case "relation-any":
232
+ case "relation-every":
233
+ return [key, { _tag: e._tag, path: e.path, filter } as ComputedProjectionIrExpression]
234
+ case "relation-distinct-count":
235
+ case "relation-sum":
236
+ return [
237
+ key,
238
+ { _tag: e._tag, path: e.path, field: e.field, filter } as ComputedProjectionIrExpression
239
+ ]
240
+ case "relation-sum-expr":
241
+ return [
242
+ key,
243
+ { _tag: e._tag, path: e.path, expression: e.expression, filter } as ComputedProjectionIrExpression
244
+ ]
245
+ case "relation-sum-expr-by":
246
+ return [
247
+ key,
248
+ {
249
+ _tag: e._tag,
250
+ path: e.path,
251
+ expression: e.expression,
252
+ unit: e.unit,
253
+ filter
254
+ } as ComputedProjectionIrExpression
255
+ ]
256
+ case "relation-sum-expr-normalized":
257
+ return [
258
+ key,
259
+ {
260
+ _tag: e._tag,
261
+ path: e.path,
262
+ expression: e.expression,
263
+ unit: e.unit,
264
+ toBase: e.toBase,
265
+ factors: e.factors,
266
+ filter
267
+ } as ComputedProjectionIrExpression
268
+ ]
269
+ case "relation-collect":
270
+ return [
271
+ key,
272
+ {
273
+ _tag: e._tag,
274
+ path: e.path,
275
+ field: e.field,
276
+ distinct: e.distinct,
277
+ filter
278
+ } as ComputedProjectionIrExpression
279
+ ]
280
+ case "relation-collect-fields":
281
+ return [
282
+ key,
283
+ {
284
+ _tag: e._tag,
285
+ path: e.path,
286
+ fields: e.fields,
287
+ distinct: e.distinct,
288
+ filter
289
+ } as ComputedProjectionIrExpression
290
+ ]
291
+ }
292
+ })
293
+ )
294
+ : undefined
140
295
  }
141
296
  })
142
297
  )
@@ -157,15 +312,19 @@ export const toFilter = <
157
312
  R,
158
313
  TFieldValuesRefined extends TFieldValues = TFieldValues
159
314
  >(
160
- q: QAll<TFieldValues, TFieldValuesRefined, A, R>
315
+ q: QAll<TFieldValues, TFieldValuesRefined, A, R>,
316
+ baseSchema?: S.Schema<unknown>
161
317
  ) => {
162
318
  // TODO: Native interpreter for each db adapter, instead of the intermediate "new-kid" format
163
319
  const a = interpret(q)
164
320
  const schema = a.schema
165
- let select: (keyof TFieldValues | { key: string; subKeys: string[] })[] = []
321
+ let select: (keyof TFieldValues | { key: string; subKeys: string[] } | {
322
+ key: string
323
+ computed: ComputedProjectionIrExpression
324
+ })[] = []
166
325
  // TODO: support more complex (nested) schemas?
167
326
  if (schema) {
168
- const t = walkTransformation(schema.ast)
327
+ const t = walkTransformation(SchemaAST.toEncoded(schema.ast))
169
328
  if (S.AST.isObjects(t)) {
170
329
  select = t.propertySignatures.map((_) => _.name as string)
171
330
  for (const prop of t.propertySignatures) {
@@ -194,12 +353,53 @@ export const toFilter = <
194
353
  }
195
354
  }
196
355
  }
356
+ const computed = a.computed
357
+ const getSelectKey = (_: (typeof select)[number]) => {
358
+ if (typeof _ === "string") {
359
+ return _
360
+ }
361
+ if (typeof _ === "object" && _ !== null && "key" in _) {
362
+ return _.key
363
+ }
364
+ return String(_)
365
+ }
366
+ const schemaKeys = select.map(getSelectKey)
367
+ const nonEncodedSchemaKeys = (() => {
368
+ if (!baseSchema) {
369
+ return [] as string[]
370
+ }
371
+ const encoded = walkTransformation(SchemaAST.toEncoded(baseSchema.ast))
372
+ if (!S.AST.isObjects(encoded)) {
373
+ return [] as string[]
374
+ }
375
+ const encodedKeys = encoded.propertySignatures.map((_) => _.name as string)
376
+ return schemaKeys.filter((key) => !encodedKeys.includes(key))
377
+ })()
378
+ const missingComputedKeys = nonEncodedSchemaKeys.filter((key) => !(computed && key in computed))
379
+
380
+ if (Array.isArrayNonEmpty(missingComputedKeys)) {
381
+ throw new Error(`Missing computed projections for schema keys: ${missingComputedKeys.join(", ")}`)
382
+ }
383
+
384
+ if (computed) {
385
+ const computedKeys = Object.keys(computed)
386
+ const extraComputedKeys = computedKeys.filter((key) => !schemaKeys.includes(key))
387
+ if (Array.isArrayNonEmpty(extraComputedKeys)) {
388
+ throw new Error(`Computed projection keys must exist in projection schema: ${extraComputedKeys.join(", ")}`)
389
+ }
390
+ select = select.filter((_) => {
391
+ const key = getSelectKey(_)
392
+ return !(key in computed)
393
+ })
394
+ select.push(...Object.entries(computed).map(([key, expression]) => ({ key, computed: expression })))
395
+ }
197
396
  return dropUndefinedT({
198
397
  t: null as unknown as TFieldValues,
199
398
  limit: a.limit,
200
399
  skip: a.skip,
201
400
  select: Option.getOrUndefined(toNonEmptyArray(select)),
202
401
  schema,
402
+ computed,
203
403
  order: Option.getOrUndefined(toNonEmptyArray(a.order)),
204
404
  ttype: a.ttype,
205
405
  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"
@@ -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
+ })