@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
@@ -4,6 +4,7 @@ import { Array, Effect, type NonEmptyReadonlyArray } from "effect-app"
4
4
  import { assertUnreachable } from "effect-app/utils"
5
5
  import { InfraLogger } from "../../logger.js"
6
6
  import type { FilterR, FilterResult, Ops } from "../../Model/filter/filterApi.js"
7
+ import type { ComputedProjectionIrExpression, ComputedProjectionMathIrExpression } from "../../Model/query.js"
7
8
  import { isRelationCheck } from "../codeFilter.js"
8
9
  import type { SupportedValues } from "../service.js"
9
10
 
@@ -40,12 +41,20 @@ export function buildWhereCosmosQuery3(
40
41
  filter: readonly FilterResult[],
41
42
  name: string,
42
43
  defaultValues: Record<string, unknown>,
43
- select?: NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }>,
44
+ select?: NonEmptyReadonlyArray<
45
+ string | {
46
+ key: string
47
+ subKeys: readonly string[]
48
+ } | {
49
+ key: string
50
+ computed: ComputedProjectionIrExpression
51
+ }
52
+ >,
44
53
  order?: NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }>,
45
54
  skip?: number,
46
55
  limit?: number
47
56
  ) {
48
- const statement = (x: FilterR, i: number, values: any[]) => {
57
+ const statement = (x: FilterR, i: number) => {
49
58
  if (x.path === idKey) {
50
59
  x = { ...x, path: "id" }
51
60
  }
@@ -60,8 +69,6 @@ export function buildWhereCosmosQuery3(
60
69
 
61
70
  const v = "@v" + i
62
71
 
63
- const realValue = values[i]
64
-
65
72
  switch (x.op) {
66
73
  case "in":
67
74
  return `ARRAY_CONTAINS(${v}, ${k})`
@@ -74,14 +81,22 @@ export function buildWhereCosmosQuery3(
74
81
  return `(NOT ARRAY_CONTAINS(${k}, ${v}))`
75
82
 
76
83
  case "includes-any":
77
- return `ARRAY_CONTAINS_ANY(${k}, ${(realValue as any[]).map((_, i) => `${v}__${i}`).join(", ")})`
84
+ return `ARRAY_CONTAINS_ANY(${k}, ${
85
+ (x.value as unknown as readonly unknown[]).map((_, i) => `${v}__${i}`).join(", ")
86
+ })`
78
87
  case "notIncludes-any":
79
- return `(NOT ARRAY_CONTAINS_ANY(${k}, ${(realValue as any[]).map((_, i) => `${v}__${i}`).join(", ")}))`
88
+ return `(NOT ARRAY_CONTAINS_ANY(${k}, ${
89
+ (x.value as unknown as readonly unknown[]).map((_, i) => `${v}__${i}`).join(", ")
90
+ }))`
80
91
 
81
92
  case "includes-all":
82
- return `ARRAY_CONTAINS_ALL(${k}, ${(realValue as any[]).map((_, i) => `${v}__${i}`).join(", ")})`
93
+ return `ARRAY_CONTAINS_ALL(${k}, ${
94
+ (x.value as unknown as readonly unknown[]).map((_, i) => `${v}__${i}`).join(", ")
95
+ })`
83
96
  case "notIncludes-all":
84
- return `(NOT ARRAY_CONTAINS_ALL(${k}, ${(realValue as any[]).map((_, i) => `${v}__${i}`).join(", ")}))`
97
+ return `(NOT ARRAY_CONTAINS_ALL(${k}, ${
98
+ (x.value as unknown as readonly unknown[]).map((_, i) => `${v}__${i}`).join(", ")
99
+ }))`
85
100
 
86
101
  case "contains":
87
102
  return `CONTAINS(${k}, ${v}, true)`
@@ -165,7 +180,7 @@ export function buildWhereCosmosQuery3(
165
180
  : _
166
181
  : _
167
182
 
168
- const print = (state: readonly FilterResult[], values: any[], isRelation: string | null, every: boolean) => {
183
+ const print = (state: readonly FilterResult[], isRelation: string | null, every: boolean) => {
169
184
  let s = ""
170
185
  let l = 0
171
186
  const printN = (n: number) => {
@@ -174,13 +189,13 @@ export function buildWhereCosmosQuery3(
174
189
  for (const e of state) {
175
190
  switch (e.t) {
176
191
  case "where":
177
- s += statement(e, i++, values)
192
+ s += statement(e, i++)
178
193
  break
179
194
  case "or":
180
- s += ` OR ${statement(e, i++, values)}`
195
+ s += ` OR ${statement(e, i++)}`
181
196
  break
182
197
  case "and":
183
- s += ` AND ${statement(e, i++, values)}`
198
+ s += ` AND ${statement(e, i++)}`
184
199
  break
185
200
  case "or-scope": {
186
201
  ++l
@@ -189,7 +204,7 @@ export function buildWhereCosmosQuery3(
189
204
  if (rel) {
190
205
  const rel = (e.result[0]! as { path: string }).path.split(".-1.")[0]
191
206
  s += isRelation
192
- ? ` OR (\n${printN(l + 1)}${print(e.result, values, rel, every)}\n${printN(l)})`
207
+ ? ` OR (\n${printN(l + 1)}${print(e.result, rel, every)}\n${printN(l)})`
193
208
  : ` OR (\n${printN(l + 1)}${
194
209
  every ? "NOT " : ""
195
210
  }EXISTS(SELECT VALUE ${rel} FROM ${rel} IN f.${rel} WHERE ${
@@ -197,13 +212,12 @@ export function buildWhereCosmosQuery3(
197
212
  e
198
213
  .result
199
214
  .map(flip(every)),
200
- values,
201
215
  rel,
202
216
  every
203
217
  )
204
218
  }))`
205
219
  } else {
206
- s += ` OR (\n${printN(l + 1)}${print(e.result, values, null, every)}\n${printN(l)})`
220
+ s += ` OR (\n${printN(l + 1)}${print(e.result, null, every)}\n${printN(l)})`
207
221
  }
208
222
  --l
209
223
  break
@@ -215,14 +229,14 @@ export function buildWhereCosmosQuery3(
215
229
  if (rel) {
216
230
  const rel = (e.result[0]! as { path: string }).path.split(".-1.")[0]
217
231
  s += isRelation
218
- ? ` AND (\n${printN(l + 1)}${print(e.result, values, rel, every)}\n${printN(l)})`
232
+ ? ` AND (\n${printN(l + 1)}${print(e.result, rel, every)}\n${printN(l)})`
219
233
  : ` AND (\n${printN(l + 1)}${
220
234
  every ? "NOT " : ""
221
235
  }EXISTS(SELECT VALUE ${rel} FROM ${rel} IN f.${rel} WHERE ${
222
- print(e.result.map(flip(every)), values, rel, every)
236
+ print(e.result.map(flip(every)), rel, every)
223
237
  }))`
224
238
  } else {
225
- s += ` AND (\n${printN(l + 1)}${print(e.result, values, null, every)}\n${printN(l)})`
239
+ s += ` AND (\n${printN(l + 1)}${print(e.result, null, every)}\n${printN(l)})`
226
240
  }
227
241
  --l
228
242
  break
@@ -234,12 +248,12 @@ export function buildWhereCosmosQuery3(
234
248
  if (rel) {
235
249
  const rel = (e.result[0]! as { path: string }).path.split(".-1.")[0]
236
250
  s += isRelation
237
- ? `(\n${printN(l + 1)}${print(e.result, values, rel, every)}\n${printN(l)})`
251
+ ? `(\n${printN(l + 1)}${print(e.result, rel, every)}\n${printN(l)})`
238
252
  : `(\n${printN(l + 1)}${every ? "NOT " : ""}EXISTS(SELECT VALUE ${rel} FROM ${rel} IN f.${rel} WHERE ${
239
- print(e.result.map(flip(every)), values, rel, every)
253
+ print(e.result.map(flip(every)), rel, every)
240
254
  }))`
241
255
  } else {
242
- s += `(\n${printN(l + 1)}${print(e.result, values, null, every)}\n${printN(l)})`
256
+ s += `(\n${printN(l + 1)}${print(e.result, null, every)}\n${printN(l)})`
243
257
  }
244
258
  // ;--l
245
259
  break
@@ -272,40 +286,123 @@ export function buildWhereCosmosQuery3(
272
286
  ? getValues(_.result)
273
287
  : [_]
274
288
  )
275
- const values = getValues(filter)
289
+ const computedFilters = select
290
+ ? select.flatMap((_) => typeof _ === "object" && "computed" in _ ? getValues(_.computed.filter) : [])
291
+ : []
292
+ const values = [...computedFilters, ...getValues(filter)]
293
+
294
+ const computedSelectExpr = (key: string, computed: ComputedProjectionIrExpression) => {
295
+ const relationPath = computed.path
296
+ const relationAlias = relationPath
297
+ const relationSource = dottedToAccess(`f.${relationPath}`)
298
+ const compileExpr = (expression: ComputedProjectionMathIrExpression): string => {
299
+ switch (expression._tag) {
300
+ case "field":
301
+ return dottedToAccess(`${relationAlias}.${expression.field}`)
302
+ case "mul":
303
+ return `(${compileExpr(expression.left)} * ${compileExpr(expression.right)})`
304
+ default:
305
+ return assertUnreachable(expression)
306
+ }
307
+ }
308
+ const factorExpr = (unitExpr: string, toBase: string, factors: Readonly<Record<string, number>>) => {
309
+ const entries = Object.entries(factors).filter(([, factor]) => Number.isFinite(factor))
310
+ return entries.reduceRight<string>(
311
+ (acc, [unit, factor]) => `IIF(${unitExpr} = ${JSON.stringify(unit)}, ${factor}, ${acc})`,
312
+ `IIF(${unitExpr} = ${JSON.stringify(toBase)}, 1, 0)`
313
+ )
314
+ }
315
+ const where = computed.filter.length > 0
316
+ ? ` WHERE ${print(computed.filter, relationPath, false)}`
317
+ : ""
318
+ switch (computed._tag) {
319
+ case "relation-count":
320
+ return `(SELECT VALUE COUNT(1) FROM ${relationAlias} IN ${relationSource}${where}) AS ${key}`
321
+ case "relation-any":
322
+ return `EXISTS(SELECT VALUE ${relationAlias} FROM ${relationAlias} IN ${relationSource}${where}) AS ${key}`
323
+ case "relation-every": {
324
+ // ∀x.P(x) ≡ ¬∃x.¬P(x). Cosmos has no NOT(...) on EXISTS subqueries directly,
325
+ // but we can flip via NOT EXISTS(... WHERE NOT (filter)).
326
+ if (computed.filter.length === 0) return `true AS ${key}`
327
+ return `NOT EXISTS(SELECT VALUE ${relationAlias} FROM ${relationAlias} IN ${relationSource} WHERE NOT (${
328
+ print(computed.filter, relationPath, false)
329
+ })) AS ${key}`
330
+ }
331
+ case "relation-distinct-count": {
332
+ const fieldRef = dottedToAccess(`${relationAlias}.${computed.field}`)
333
+ return `(SELECT VALUE COUNT(1) FROM (SELECT DISTINCT VALUE ${fieldRef} FROM ${relationAlias} IN ${relationSource}${where})) AS ${key}`
334
+ }
335
+ case "relation-sum": {
336
+ const fieldRef = dottedToAccess(`${relationAlias}.${computed.field}`)
337
+ return `(SELECT VALUE SUM(${fieldRef}) FROM ${relationAlias} IN ${relationSource}${where}) AS ${key}`
338
+ }
339
+ case "relation-sum-expr": {
340
+ const expression = compileExpr(computed.expression)
341
+ return `(SELECT VALUE SUM(${expression}) FROM ${relationAlias} IN ${relationSource}${where}) AS ${key}`
342
+ }
343
+ case "relation-sum-expr-by": {
344
+ const unitRef = dottedToAccess(`${relationAlias}.${computed.unit}`)
345
+ const expression = compileExpr(computed.expression)
346
+ return `ARRAY(SELECT VALUE { "unit": ${unitRef}, "total": SUM(${expression}) } FROM ${relationAlias} IN ${relationSource}${where} GROUP BY ${unitRef}) AS ${key}`
347
+ }
348
+ case "relation-sum-expr-normalized": {
349
+ const unitRef = dottedToAccess(`${relationAlias}.${computed.unit}`)
350
+ const expression = compileExpr(computed.expression)
351
+ const factor = factorExpr(unitRef, computed.toBase, computed.factors)
352
+ return `(SELECT VALUE SUM((${expression}) * (${factor})) FROM ${relationAlias} IN ${relationSource}${where}) AS ${key}`
353
+ }
354
+ case "relation-collect": {
355
+ const fieldRef = dottedToAccess(`${relationAlias}.${computed.field}`)
356
+ if (computed.distinct) {
357
+ return `ARRAY(SELECT DISTINCT VALUE ${fieldRef} FROM ${relationAlias} IN ${relationSource}${where}) AS ${key}`
358
+ }
359
+ return `ARRAY(SELECT VALUE ${fieldRef} FROM ${relationAlias} IN ${relationSource}${where}) AS ${key}`
360
+ }
361
+ case "relation-collect-fields": {
362
+ const subqueries = computed.fields.map((field) => {
363
+ const fieldRef = dottedToAccess(`${relationAlias}.${field}`)
364
+ return computed.distinct
365
+ ? `ARRAY(SELECT DISTINCT VALUE ${fieldRef} FROM ${relationAlias} IN ${relationSource}${where})`
366
+ : `ARRAY(SELECT VALUE ${fieldRef} FROM ${relationAlias} IN ${relationSource}${where})`
367
+ })
368
+ const combined = computed.distinct
369
+ ? subqueries.reduce((acc, sq) => `SetUnion(${acc}, ${sq})`)
370
+ : subqueries.reduce((acc, sq) => `ARRAY_CONCAT(${acc}, ${sq})`)
371
+ return `${combined} AS ${key}`
372
+ }
373
+ }
374
+ }
276
375
  // with joins, you should use DISTINCT
277
376
  // or you can end up with duplicates
278
377
  return {
279
378
  query: `
280
379
  SELECT ${
281
380
  select
282
- ? `${
283
- select
284
- .map((s) =>
285
- typeof s === "string"
286
- ? dottedToAccess(s === idKey ? "f.id" : `f.${s}`) // x["y"} vs x.y, helps with reserved keywords like "value"
287
- : `ARRAY (SELECT ${s.subKeys.map((_) => dottedToAccess(`t.${_}`)).join(",")}
381
+ ? select
382
+ .map((s) =>
383
+ typeof s === "string"
384
+ ? dottedToAccess(s === idKey ? "f.id" : `f.${s}`) // x["y"} vs x.y, helps with reserved keywords like "value"
385
+ : "computed" in s
386
+ ? computedSelectExpr(s.key, s.computed)
387
+ : `ARRAY (SELECT ${s.subKeys.map((_) => dottedToAccess(`t.${_}`)).join(",")}
288
388
  FROM t in ${dottedToAccess(`f.${s.key}`)}) AS ${s.key}`
289
- )
290
- .join(", ")
291
- }`
389
+ )
390
+ .join(", ")
292
391
  : "f"
293
392
  }
294
393
  FROM ${name} f
295
394
 
296
- ${filter.length ? `WHERE (${print(filter, values.map((_) => _.value), null, false)})` : ""}
395
+ ${filter.length ? `WHERE (${print(filter, null, false)})` : ""}
297
396
  ${order ? `ORDER BY ${order.map((_) => `${dottedToAccess(`f.${_.key}`)} ${_.direction}`).join(", ")}` : ""}
298
397
  ${skip !== undefined || limit !== undefined ? `OFFSET ${skip ?? 0} LIMIT ${limit ?? 999999}` : ""}`,
299
- parameters: [
300
- ...values
301
- .flatMap((x, i) =>
302
- [{
303
- name: `@v${i}`,
304
- value: x.value as any
305
- }]
306
- // TODO: only for arrays that are used with _ANY or _ALL
307
- .concat(Array.isArray(x.value) ? x.value.map((_, i2) => ({ name: `@v${i}__${i2}`, value: _ as any })) : [])
308
- )
309
- ]
398
+ parameters: values
399
+ .flatMap((x, i) =>
400
+ [{
401
+ name: `@v${i}`,
402
+ value: x.value as any
403
+ }]
404
+ // TODO: only for arrays that are used with _ANY or _ALL
405
+ .concat(Array.isArray(x.value) ? x.value.map((_, i2) => ({ name: `@v${i}__${i2}`, value: _ as any })) : [])
406
+ )
310
407
  }
311
408
  }