@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,14 +1,174 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
 
3
- import { Array, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, ServiceMap, Struct } from "effect-app"
3
+ import * as Array from "effect-app/Array"
4
+ import type { NonEmptyReadonlyArray } from "effect-app/Array"
5
+ import * as Context from "effect-app/Context"
6
+ import * as Effect from "effect-app/Effect"
7
+ import * as Option from "effect-app/Option"
4
8
  import { NonEmptyString255 } from "effect-app/Schema"
5
- import { get } from "effect-app/utils"
9
+ import { assertUnreachable } from "effect-app/utils"
10
+ import { flow, pipe } from "effect/Function"
11
+ import * as Order from "effect/Order"
12
+ import * as Ref from "effect/Ref"
13
+ import * as Result from "effect/Result"
14
+ import * as Semaphore from "effect/Semaphore"
15
+ import * as Struct from "effect/Struct"
6
16
  import { InfraLogger } from "../logger.js"
17
+ import type { FilterResult } from "../Model/filter/filterApi.js"
7
18
  import type { FieldValues } from "../Model/filter/types.js"
19
+ import type { ComputedProjectionIrExpression, ComputedProjectionMathIrExpression } from "../Model/query.js"
20
+ import { annotateDb } from "../otel.js"
8
21
  import { codeFilter, codeFilter3_ } from "./codeFilter.js"
9
22
  import { type FilterArgs, type PersistenceModelType, type Store, type StoreConfig, StoreMaker } from "./service.js"
10
23
  import { makeUpdateETag } from "./utils.js"
11
24
 
25
+ /** Traverse an object by a dot-separated path string, e.g. `"a.b.c"`. */
26
+ export function get(obj: any, path: string): any {
27
+ return path.split(".").reduce((res: any, key: string) => (res != null ? res[key] : res), obj)
28
+ }
29
+
30
+ const stripRelationFilterPaths = (state: readonly FilterResult[], relationPath: string): readonly FilterResult[] => {
31
+ const prefix = `${relationPath}.-1.`
32
+ return state.map((entry) =>
33
+ "path" in entry
34
+ ? {
35
+ ...entry,
36
+ path: entry.path.startsWith(prefix) ? entry.path.slice(prefix.length) : entry.path
37
+ }
38
+ : {
39
+ ...entry,
40
+ result: stripRelationFilterPaths(entry.result, relationPath)
41
+ }
42
+ )
43
+ }
44
+
45
+ const emptyValueFor = (tag: ComputedProjectionIrExpression["_tag"]) => {
46
+ switch (tag) {
47
+ case "relation-count":
48
+ case "relation-distinct-count":
49
+ case "relation-sum":
50
+ case "relation-sum-expr":
51
+ case "relation-sum-expr-normalized":
52
+ return 0
53
+ case "relation-sum-expr-by":
54
+ return [] as unknown[]
55
+ case "relation-any":
56
+ return false
57
+ case "relation-every":
58
+ return true
59
+ case "relation-collect":
60
+ return [] as unknown[]
61
+ case "relation-collect-fields":
62
+ return [] as unknown[]
63
+ default:
64
+ return assertUnreachable(tag)
65
+ }
66
+ }
67
+
68
+ const computeProjectionValue = (
69
+ row: FieldValues,
70
+ computed: ComputedProjectionIrExpression
71
+ ) => {
72
+ const relation = get(row, computed.path)
73
+ if (!Array.isArray(relation)) {
74
+ return emptyValueFor(computed._tag)
75
+ }
76
+ const filter = stripRelationFilterPaths(computed.filter, computed.path)
77
+ // empty filter = unconditional match (codeFilter3_ uses eval on a built
78
+ // string and chokes on `( )`, so short-circuit before invoking it).
79
+ const matches = filter.length === 0
80
+ ? (_value: unknown) => true
81
+ : (value: unknown) => codeFilter3_(filter, value)
82
+ const evalExpr = (value: unknown, expression: ComputedProjectionMathIrExpression): number => {
83
+ switch (expression._tag) {
84
+ case "field": {
85
+ const v = get(value, expression.field)
86
+ return typeof v === "number" ? v : Number(v) || 0
87
+ }
88
+ case "mul":
89
+ return evalExpr(value, expression.left) * evalExpr(value, expression.right)
90
+ default:
91
+ return assertUnreachable(expression)
92
+ }
93
+ }
94
+ switch (computed._tag) {
95
+ case "relation-count":
96
+ return relation.reduce<number>((acc, value) => matches(value) ? acc + 1 : acc, 0)
97
+ case "relation-any":
98
+ return relation.some(matches)
99
+ case "relation-every":
100
+ return relation.every(matches)
101
+ case "relation-distinct-count": {
102
+ const seen = new Set<unknown>()
103
+ for (const value of relation) {
104
+ if (matches(value)) seen.add(get(value, computed.field))
105
+ }
106
+ return seen.size
107
+ }
108
+ case "relation-sum":
109
+ return relation.reduce<number>((acc, value) => {
110
+ if (!matches(value)) return acc
111
+ const v = get(value, computed.field)
112
+ return acc + (typeof v === "number" ? v : Number(v) || 0)
113
+ }, 0)
114
+ case "relation-sum-expr":
115
+ return relation.reduce<number>((acc, value) => {
116
+ if (!matches(value)) return acc
117
+ return acc + evalExpr(value, computed.expression)
118
+ }, 0)
119
+ case "relation-sum-expr-by": {
120
+ const totals = new Map<unknown, number>()
121
+ for (const value of relation) {
122
+ if (!matches(value)) continue
123
+ const unit = get(value, computed.unit)
124
+ const current = totals.get(unit) ?? 0
125
+ totals.set(unit, current + evalExpr(value, computed.expression))
126
+ }
127
+ return [...totals.entries()].map(([unit, total]) => ({ unit, total }))
128
+ }
129
+ case "relation-sum-expr-normalized":
130
+ return relation.reduce<number>((acc, value) => {
131
+ if (!matches(value)) return acc
132
+ const unit = get(value, computed.unit)
133
+ const factor = unit === computed.toBase ? 1 : computed.factors[String(unit)]
134
+ if (factor === undefined || !Number.isFinite(factor)) return acc
135
+ return acc + evalExpr(value, computed.expression) * factor
136
+ }, 0)
137
+ case "relation-collect": {
138
+ const out: unknown[] = []
139
+ const seen = computed.distinct ? new Set<unknown>() : undefined
140
+ for (const value of relation) {
141
+ if (!matches(value)) continue
142
+ const v = get(value, computed.field)
143
+ if (seen) {
144
+ if (seen.has(v)) continue
145
+ seen.add(v)
146
+ }
147
+ out.push(v)
148
+ }
149
+ return out
150
+ }
151
+ case "relation-collect-fields": {
152
+ const out: unknown[] = []
153
+ const seen = computed.distinct ? new Set<unknown>() : undefined
154
+ for (const value of relation) {
155
+ if (!matches(value)) continue
156
+ for (const field of computed.fields) {
157
+ const v = get(value, field)
158
+ if (seen) {
159
+ if (seen.has(v)) continue
160
+ seen.add(v)
161
+ }
162
+ out.push(v)
163
+ }
164
+ }
165
+ return out
166
+ }
167
+ default:
168
+ return assertUnreachable(computed)
169
+ }
170
+ }
171
+
12
172
  export function memFilter<T extends FieldValues, U extends keyof T = never>(f: FilterArgs<T, U>) {
13
173
  type M = U extends undefined ? T : Pick<T, U>
14
174
  return ((c: T[]): M[] => {
@@ -16,18 +176,26 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
16
176
  const sel = f.select
17
177
  if (!sel) return r as M[]
18
178
  return r.map((i) => {
19
- const [keys, subKeys] = pipe(
179
+ const [keys, entries] = pipe(
20
180
  sel,
21
- Array.partition((r) =>
22
- typeof r === "string" ? Result.fail(String(r)) : Result.succeed(r as { key: string; subKeys: string[] })
23
- )
181
+ Array.partition((entry) => typeof entry === "string" ? Result.fail(String(entry)) : Result.succeed(entry))
182
+ )
183
+ const subKeys = entries.filter((entry): entry is { key: string; subKeys: readonly string[] } =>
184
+ typeof entry === "object" && entry !== null && "subKeys" in entry
24
185
  )
186
+ const computedKeys = entries.filter((entry): entry is {
187
+ key: string
188
+ computed: ComputedProjectionIrExpression
189
+ } => typeof entry === "object" && entry !== null && "computed" in entry)
25
190
  const n = Struct.pick(i, keys)
26
191
  subKeys.forEach((subKey) => {
27
192
  n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys as never[]))
28
193
  })
194
+ computedKeys.forEach((entry) => {
195
+ ;(n as Record<string, unknown>)[entry.key] = computeProjectionValue(i, entry.computed)
196
+ })
29
197
  return n as M
30
- }) as any
198
+ })
31
199
  }
32
200
  const skip = f?.skip
33
201
  const limit = f?.limit
@@ -72,7 +240,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
72
240
  }
73
241
 
74
242
  const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
75
- export class storeId extends ServiceMap.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
243
+ export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
76
244
 
77
245
  function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
78
246
  return InfraLogger
@@ -151,43 +319,55 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
151
319
  withPermit
152
320
  )
153
321
  const s: Store<IdKey, Encoded> = {
322
+ seedNamespace: () => Effect.void,
323
+
154
324
  queryRaw: (query) =>
155
325
  all
156
326
  .pipe(
157
327
  // Effect.tap(() => logQuery(query, defaultValues)),
158
328
  Effect.map(query.memory),
159
- Effect.withSpan("Memory.queryRaw [effect-app/infra/Store]", {
160
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
161
- }, { captureStackTrace: false })
329
+ annotateDb({
330
+ operation: "queryRaw",
331
+ system: "memory",
332
+ collection: modelName,
333
+ namespace,
334
+ entity: modelName
335
+ })
162
336
  ),
163
337
 
164
- all: all.pipe(Effect.withSpan("Memory.all [effect-app/infra/Store]", {
165
- attributes: {
166
- modelName,
167
- namespace
168
- }
169
- }, { captureStackTrace: false })),
338
+ all: all.pipe(annotateDb({
339
+ operation: "all",
340
+ system: "memory",
341
+ collection: modelName,
342
+ namespace,
343
+ entity: modelName
344
+ })),
170
345
  find: (id) =>
171
346
  Ref
172
347
  .get(store)
173
348
  .pipe(
174
349
  Effect.map((_) => Option.fromNullishOr(_.get(id))),
175
- Effect
176
- .withSpan("Memory.find [effect-app/infra/Store]", {
177
- attributes: {
178
- modelName,
179
- namespace
180
- }
181
- }, { captureStackTrace: false })
350
+ annotateDb({
351
+ operation: "find",
352
+ system: "memory",
353
+ collection: modelName,
354
+ namespace,
355
+ entity: modelName,
356
+ extra: { "app.entity.id": id as unknown }
357
+ })
182
358
  ),
183
359
  filter: (f) =>
184
360
  all
185
361
  .pipe(
186
362
  Effect.tap(() => logQuery(f, defaultValues)),
187
363
  Effect.map(memFilter(f)),
188
- Effect.withSpan("Memory.filter [effect-app/infra/Store]", {
189
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
190
- }, { captureStackTrace: false })
364
+ annotateDb({
365
+ operation: "filter",
366
+ system: "memory",
367
+ collection: modelName,
368
+ namespace,
369
+ entity: modelName
370
+ })
191
371
  ),
192
372
  set: (e) =>
193
373
  s
@@ -202,10 +382,14 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
202
382
  )
203
383
  ),
204
384
  withPermit,
205
- Effect
206
- .withSpan("Memory.set [effect-app/infra/Store]", {
207
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
208
- }, { captureStackTrace: false })
385
+ annotateDb({
386
+ operation: "set",
387
+ system: "memory",
388
+ collection: modelName,
389
+ namespace,
390
+ entity: modelName,
391
+ extra: { "app.entity.id": e[idKey] as unknown }
392
+ })
209
393
  ),
210
394
  batchRemove: (items: NonEmptyReadonlyArray<Encoded[IdKey]>) =>
211
395
  pipe(
@@ -216,10 +400,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
216
400
  Effect.filterOrFail((_) => _.length <= 100, () => "BatchRemove: a batch may not exceed 100 items"),
217
401
  Effect.orDie,
218
402
  Effect.andThen(batchRemove),
219
- Effect
220
- .withSpan("Memory.batchRemove [effect-app/infra/Store]", {
221
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
222
- }, { captureStackTrace: false })
403
+ annotateDb({
404
+ operation: "batchRemove",
405
+ system: "memory",
406
+ collection: modelName,
407
+ namespace,
408
+ entity: modelName
409
+ })
223
410
  )
224
411
  ),
225
412
  batchSet: (items: readonly [PM, ...PM[]]) =>
@@ -231,18 +418,25 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
231
418
  Effect.filterOrFail((_) => _.length <= 100, () => "BatchSet: a batch may not exceed 100 items"),
232
419
  Effect.orDie,
233
420
  Effect.andThen(batchSet),
234
- Effect
235
- .withSpan("Memory.batchSet [effect-app/infra/Store]", {
236
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
237
- }, { captureStackTrace: false })
421
+ annotateDb({
422
+ operation: "batchSet",
423
+ system: "memory",
424
+ collection: modelName,
425
+ namespace,
426
+ entity: modelName
427
+ })
238
428
  )
239
429
  ),
240
430
  bulkSet: flow(
241
431
  batchSet,
242
432
  (_) =>
243
- _.pipe(Effect.withSpan("Memory.bulkSet [effect-app/infra/Store]", {
244
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
245
- }, { captureStackTrace: false }))
433
+ _.pipe(annotateDb({
434
+ operation: "bulkSet",
435
+ system: "memory",
436
+ collection: modelName,
437
+ namespace,
438
+ entity: modelName
439
+ }))
246
440
  )
247
441
  }
248
442
  return s
@@ -250,56 +444,60 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
250
444
  }
251
445
 
252
446
  export const makeMemoryStore = () => ({
253
- make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
447
+ make: Effect.fnUntraced(function*<IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
254
448
  modelName: string,
255
449
  idKey: IdKey,
256
450
  seed?: Effect.Effect<Iterable<Encoded>, E, R>,
257
451
  config?: StoreConfig<Encoded>
258
- ) =>
259
- Effect.gen(function*() {
260
- const storesSem = Semaphore.makeUnsafe(1)
261
- const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
262
- modelName,
263
- idKey,
264
- "primary",
265
- seed,
266
- config?.defaultValues
267
- )
268
- const ctx = yield* Effect.services<R>()
269
- const stores = new Map([["primary", primary]])
270
- const getStore = !config?.allowNamespace
271
- ? Effect.succeed(primary)
272
- : storeId.asEffect().pipe(Effect.flatMap((namespace) => {
273
- const store = stores.get(namespace)
274
- if (store) {
275
- return Effect.succeed(store)
276
- }
277
- if (!config.allowNamespace!(namespace)) {
278
- throw new Error(`Namespace ${namespace} not allowed!`)
279
- }
280
- return storesSem.withPermits(1)(Effect.suspend(() => {
281
- const store = stores.get(namespace)
282
- if (store) return Effect.sync(() => store)
283
- return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
284
- .pipe(
285
- Effect.orDie,
286
- Effect.provide(ctx),
287
- Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
288
- )
289
- }))
290
- }))
291
- const s: Store<IdKey, Encoded> = {
292
- all: Effect.flatMap(getStore, (_) => _.all),
293
- queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
294
- find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
295
- filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
296
- set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
297
- batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
298
- bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
299
- batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args))
452
+ ) {
453
+ const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
454
+ modelName,
455
+ idKey,
456
+ "primary",
457
+ seed,
458
+ config?.defaultValues
459
+ )
460
+ const ctx = yield* Effect.context<R>()
461
+ const stores = new Map([["primary", primary]])
462
+ const semaphores = new Map<string, Semaphore.Semaphore>()
463
+ const getSem = (ns: string) => {
464
+ let sem = semaphores.get(ns)
465
+ if (!sem) {
466
+ sem = Semaphore.makeUnsafe(1)
467
+ semaphores.set(ns, sem)
300
468
  }
301
- return s
302
- })
469
+ return sem
470
+ }
471
+ const ensureStore = (namespace: string) =>
472
+ getSem(namespace).withPermits(1)(Effect.suspend(() => {
473
+ const store = stores.get(namespace)
474
+ if (store) return Effect.succeed(store)
475
+ if (config?.allowNamespace && !config.allowNamespace(namespace)) {
476
+ throw new Error(`Namespace ${namespace} not allowed!`)
477
+ }
478
+ return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
479
+ .pipe(
480
+ Effect.orDie,
481
+ Effect.provide(ctx),
482
+ Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
483
+ )
484
+ }))
485
+ const getStore = !config?.allowNamespace
486
+ ? Effect.succeed(primary)
487
+ : storeId.asEffect().pipe(Effect.flatMap((namespace) => ensureStore(namespace)))
488
+ const s: Store<IdKey, Encoded> = {
489
+ seedNamespace: (namespace) => ensureStore(namespace).pipe(Effect.asVoid),
490
+ all: Effect.flatMap(getStore, (_) => _.all),
491
+ queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
492
+ find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
493
+ filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
494
+ set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
495
+ batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
496
+ bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
497
+ batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args))
498
+ }
499
+ return s
500
+ })
303
501
  })
304
502
 
305
503
  export const MemoryStoreLive = StoreMaker.toLayer(Effect.sync(() => makeMemoryStore()))