@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,11 +1,25 @@
1
+ import { SqliteClient } from "@effect/sql-sqlite-node"
1
2
  import { describe, expect, it } from "@effect/vitest"
2
- import { Array, Config, Effect, flow, Layer, ManagedRuntime, Redacted, References, Result, S, ServiceMap } from "effect-app"
3
+ import * as Array from "effect-app/Array"
4
+ import * as Config from "effect-app/Config"
5
+ import * as Context from "effect-app/Context"
6
+ import * as Effect from "effect-app/Effect"
7
+ import * as Layer from "effect-app/Layer"
8
+ import * as S from "effect-app/Schema"
3
9
  import { LogLevels } from "effect-app/utils"
10
+ import { flow } from "effect/Function"
11
+ import * as ManagedRuntime from "effect/ManagedRuntime"
12
+ import * as Redacted from "effect/Redacted"
13
+ import * as References from "effect/References"
14
+ import * as Result from "effect/Result"
15
+ import * as Struct from "effect/Struct"
4
16
  import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
5
- import { and, or, project, where, whereEvery, whereSome } from "../src/Model/query.js"
17
+ import { and, computed, or, project, projectComputed, relation, where, whereEvery, whereSome } from "../src/Model/query.js"
6
18
  import { makeRepo } from "../src/Model/Repository/makeRepo.js"
19
+ import { RepositoryRegistryLive } from "../src/Model/Repository/Registry.js"
7
20
  import { CosmosStoreLayer } from "../src/Store/Cosmos.js"
8
21
  import { MemoryStoreLive } from "../src/Store/Memory.js"
22
+ import { SQLiteStoreLayer } from "../src/Store/SQL.js"
9
23
 
10
24
  export const rt = ManagedRuntime.make(Layer.mergeAll(
11
25
  Layer.effect(
@@ -24,7 +38,7 @@ class Something extends S.Class<Something>("Something")({
24
38
  id: S.String,
25
39
  name: S.String,
26
40
  description: S.String,
27
- items: S.Array(S.Struct({ id: S.String, value: S.Number, description: S.String }))
41
+ items: S.Array(S.Struct({ id: S.String, value: S.Finite, description: S.String }))
28
42
  }) {}
29
43
 
30
44
  const items = [
@@ -49,7 +63,7 @@ const items = [
49
63
  ]
50
64
 
51
65
  // @effect-diagnostics-next-line missingEffectServiceDependency:off
52
- class SomethingRepo extends ServiceMap.Service<SomethingRepo>()(
66
+ class SomethingRepo extends Context.Service<SomethingRepo>()(
53
67
  "SomethingRepo",
54
68
  {
55
69
  make: Effect.gen(function*() {
@@ -76,28 +90,31 @@ class SomethingRepo extends ServiceMap.Service<SomethingRepo>()(
76
90
  static readonly Test = this
77
91
  .layer
78
92
  .pipe(
79
- Layer.provide(MemoryStoreLive)
93
+ Layer.provide(Layer.merge(MemoryStoreLive, RepositoryRegistryLive))
80
94
  )
81
95
 
82
96
  static readonly TestCosmos = this
83
97
  .layer
84
98
  .pipe(
85
99
  Layer.provide(
86
- Effect.gen(function*() {
87
- const url = yield* Config.redacted("STORAGE_URL").pipe(
88
- Config.withDefault(
89
- Redacted.make(
90
- // the emulator doesn't implement array projections :/ so you need an actual cloud instance!
91
- "AccountEndpoint=http://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
100
+ Effect
101
+ .gen(function*() {
102
+ const url = yield* Config.redacted("STORAGE_URL").pipe(
103
+ Config.withDefault(
104
+ Redacted.make(
105
+ // the emulator doesn't implement array projections :/ so you need an actual cloud instance!
106
+ "AccountEndpoint=http://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
107
+ )
92
108
  )
93
109
  )
94
- )
95
- return CosmosStoreLayer({
96
- dbName: "test",
97
- prefix: "",
98
- url
110
+ return CosmosStoreLayer({
111
+ dbName: "test",
112
+ prefix: "",
113
+ url
114
+ })
115
+ .pipe(Layer.merge(RepositoryRegistryLive))
99
116
  })
100
- }).pipe(Layer.unwrap)
117
+ .pipe(Layer.unwrap)
101
118
  )
102
119
  )
103
120
  }
@@ -107,7 +124,7 @@ describe("select first-level array fields", () => {
107
124
  .gen(function*() {
108
125
  const repo = yield* SomethingRepo
109
126
 
110
- const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Number })) })
127
+ const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Finite })) })
111
128
 
112
129
  // ok crazy lol, "value" is a reserved word in CosmosDB, so we have to use t["value"] as a field name instead of t.value
113
130
  const items = yield* repo.queryRaw(projected, {
@@ -159,7 +176,7 @@ describe("select first-level array fields", () => {
159
176
  .pipe(Effect.provide(SomethingRepo.Test), rt.runPromise))
160
177
  })
161
178
 
162
- const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Number })) })
179
+ const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Finite })) })
163
180
 
164
181
  const expected = [
165
182
  {
@@ -352,6 +369,41 @@ describe("multi-level", () => {
352
369
  .pipe(Effect.provide(SomethingRepo.Test), rt.runPromise))
353
370
  })
354
371
 
372
+ describe("computed projections", () => {
373
+ const test = Effect
374
+ .gen(function*() {
375
+ const repo = yield* SomethingRepo
376
+ const output = S.Struct({
377
+ id: S.String,
378
+ pickedCount: S.NonNegativeInt,
379
+ hasPicked: S.Boolean
380
+ })
381
+ const pickedFilter = where("value", "gt", 20)
382
+ const items = yield* repo.query(
383
+ projectComputed(
384
+ output,
385
+ computed({
386
+ pickedCount: relation<S.Codec.Encoded<typeof Something>>("items").count(pickedFilter),
387
+ hasPicked: relation<S.Codec.Encoded<typeof Something>>("items").any(pickedFilter)
388
+ })
389
+ )
390
+ )
391
+ expect(items).toStrictEqual([
392
+ { id: "1", pickedCount: 0, hasPicked: false },
393
+ { id: "2", pickedCount: 2, hasPicked: true }
394
+ ])
395
+ })
396
+ .pipe(setupRequestContextFromCurrent())
397
+
398
+ it.skipIf(!process.env["STORAGE_URL"])("works well in CosmosDB", () =>
399
+ test
400
+ .pipe(Effect.provide(SomethingRepo.TestCosmos), rt.runPromise))
401
+
402
+ it("works well in Memory", () =>
403
+ test
404
+ .pipe(Effect.provide(SomethingRepo.Test), rt.runPromise))
405
+ })
406
+
355
407
  // FUTURE: we need something like this instead:
356
408
  /*
357
409
  const subQuery = <T extends FieldValues>() => <TKey extends keyof T>(key: TKey, type: "some" | "every" = "some") => make<T[TKey][number]>() // todo: mark that this is sub query on field "items"
@@ -369,6 +421,246 @@ describe("multi-level", () => {
369
421
  ))
370
422
  */
371
423
 
424
+ // Mimic scanner MultiPick/EasyLife AllPickList shape:
425
+ // - parent has tagged-state with `at` timestamp
426
+ // - items is NonEmptyArray with state._tag + articleId/articleGTIN
427
+ // - controller filters `state.at gte X` and `state._tag neq closed`
428
+ // then projectComputed with: count, any(initial/picking/packed),
429
+ // every(picked/packed), collectDistinct(articleId)
430
+ const itemStateSchema = S.Union([
431
+ S.TaggedStruct("initial", { at: S.String }),
432
+ S.TaggedStruct("picking", { at: S.String }),
433
+ S.TaggedStruct("picked", { at: S.String }),
434
+ S.TaggedStruct("packed", { at: S.String })
435
+ ])
436
+
437
+ class ArticleLineItem extends S.Class<ArticleLineItem>("ArticleLineItem")({
438
+ articleId: S.String,
439
+ articleGTIN: S.String,
440
+ state: itemStateSchema
441
+ }) {}
442
+
443
+ const stOrderState = S.Union([
444
+ S.TaggedStruct("initial", { at: S.String }),
445
+ S.TaggedStruct("packed", { at: S.String }),
446
+ S.TaggedStruct("closed", { at: S.String })
447
+ ])
448
+
449
+ class Order extends S.Class<Order>("Order")({
450
+ id: S.String,
451
+ state: stOrderState,
452
+ items: S.NonEmptyArray(ArticleLineItem)
453
+ }) {}
454
+
455
+ const orderItems = [
456
+ new Order({
457
+ id: "o-open-1",
458
+ state: { _tag: "initial", at: "2026-05-08T08:00:00Z" },
459
+ items: [
460
+ new ArticleLineItem({
461
+ articleId: "A1",
462
+ articleGTIN: "G1",
463
+ state: { _tag: "picking", at: "2026-05-08T08:01:00Z" }
464
+ }),
465
+ new ArticleLineItem({
466
+ articleId: "A1",
467
+ articleGTIN: "G1",
468
+ state: { _tag: "picked", at: "2026-05-08T08:02:00Z" }
469
+ }),
470
+ new ArticleLineItem({
471
+ articleId: "A2",
472
+ articleGTIN: "G2",
473
+ state: { _tag: "initial", at: "2026-05-08T08:00:00Z" }
474
+ })
475
+ ]
476
+ }),
477
+ new Order({
478
+ id: "o-allpicked-2",
479
+ state: { _tag: "packed", at: "2026-05-07T10:00:00Z" },
480
+ items: [
481
+ new ArticleLineItem({
482
+ articleId: "B1",
483
+ articleGTIN: "GB1",
484
+ state: { _tag: "picked", at: "2026-05-07T09:50:00Z" }
485
+ }),
486
+ new ArticleLineItem({
487
+ articleId: "B2",
488
+ articleGTIN: "GB2",
489
+ state: { _tag: "picked", at: "2026-05-07T09:55:00Z" }
490
+ })
491
+ ]
492
+ }),
493
+ new Order({
494
+ id: "o-closed-3",
495
+ state: { _tag: "closed", at: "2026-05-04T10:00:00Z" },
496
+ items: [
497
+ new ArticleLineItem({
498
+ articleId: "C1",
499
+ articleGTIN: "GC1",
500
+ state: { _tag: "packed", at: "2026-05-04T09:00:00Z" }
501
+ })
502
+ ]
503
+ })
504
+ ]
505
+
506
+ // @effect-diagnostics-next-line missingEffectServiceDependency:off
507
+ class OrderRepo extends Context.Service<OrderRepo>()(
508
+ "OrderRepo",
509
+ {
510
+ make: Effect.gen(function*() {
511
+ const partitionKey = "orders-" + new Date().getTime()
512
+ return yield* makeRepo("Order", Order, { config: { partitionValue: () => partitionKey } })
513
+ })
514
+ }
515
+ ) {
516
+ static readonly layer = Layer
517
+ .effect(
518
+ OrderRepo,
519
+ Effect.gen(function*() {
520
+ const partitionKey = "orders-" + new Date().getTime()
521
+ const repo = OrderRepo.of(
522
+ yield* makeRepo("Order", Order, {
523
+ config: { partitionValue: () => partitionKey }
524
+ })
525
+ )
526
+ yield* repo.saveAndPublish(orderItems).pipe(setupRequestContextFromCurrent("init"))
527
+ return repo
528
+ })
529
+ )
530
+ static readonly Test = this
531
+ .layer
532
+ .pipe(Layer.provide(Layer.merge(MemoryStoreLive, RepositoryRegistryLive)))
533
+
534
+ static readonly TestSqlite = this
535
+ .layer
536
+ .pipe(
537
+ Layer.provide(
538
+ Layer.merge(
539
+ SQLiteStoreLayer({
540
+ url: Redacted.make("sqlite://"),
541
+ prefix: "test_",
542
+ dbName: "test"
543
+ }),
544
+ RepositoryRegistryLive
545
+ )
546
+ ),
547
+ Layer.provide(SqliteClient.layer({ filename: ":memory:" }))
548
+ )
549
+ }
550
+
551
+ describe("scanner-style AllPickList computed projections", () => {
552
+ const test = Effect
553
+ .gen(function*() {
554
+ const repo = yield* OrderRepo
555
+ type OrderEnc = S.Codec.Encoded<typeof Order>
556
+
557
+ const projection = S.Struct({
558
+ id: S.String,
559
+ state: stOrderState,
560
+ articleCount: S.NonNegativeInt,
561
+ hasInitialItem: S.Boolean,
562
+ hasPickingItem: S.Boolean,
563
+ hasPackedItem: S.Boolean,
564
+ allItemsPicked: S.Boolean,
565
+ allItemsPacked: S.Boolean,
566
+ articleIds: S.Array(S.String)
567
+ })
568
+
569
+ const result = yield* repo.query(
570
+ where("state.at", "gte", "2026-05-05T00:00:00Z"),
571
+ and("state._tag", "neq", "closed"),
572
+ projectComputed(
573
+ projection,
574
+ computed({
575
+ articleCount: relation<OrderEnc>("items").count(),
576
+ hasInitialItem: relation<OrderEnc>("items").any(where("state._tag", "initial")),
577
+ hasPickingItem: relation<OrderEnc>("items").any(where("state._tag", "picking")),
578
+ hasPackedItem: relation<OrderEnc>("items").any(where("state._tag", "packed")),
579
+ allItemsPicked: relation<OrderEnc>("items").every(where("state._tag", "picked")),
580
+ allItemsPacked: relation<OrderEnc>("items").every(where("state._tag", "packed")),
581
+ articleIds: relation<OrderEnc>("items").collectDistinct("articleId")
582
+ })
583
+ )
584
+ )
585
+
586
+ const byId = Object.fromEntries(result.map((r) => [r.id, r]))
587
+
588
+ expect(Object.keys(byId).sort()).toEqual(["o-allpicked-2", "o-open-1"])
589
+
590
+ const open = byId["o-open-1"]!
591
+ expect(open.articleCount).toBe(3)
592
+ expect(open.hasInitialItem).toBe(true)
593
+ expect(open.hasPickingItem).toBe(true)
594
+ expect(open.hasPackedItem).toBe(false)
595
+ expect(open.allItemsPicked).toBe(false)
596
+ expect(open.allItemsPacked).toBe(false)
597
+ expect([...open.articleIds].sort()).toEqual(["A1", "A2"])
598
+
599
+ const allp = byId["o-allpicked-2"]!
600
+ expect(allp.articleCount).toBe(2)
601
+ expect(allp.hasInitialItem).toBe(false)
602
+ expect(allp.hasPickingItem).toBe(false)
603
+ expect(allp.hasPackedItem).toBe(false)
604
+ expect(allp.allItemsPicked).toBe(true)
605
+ expect(allp.allItemsPacked).toBe(false)
606
+ expect([...allp.articleIds].sort()).toEqual(["B1", "B2"])
607
+ })
608
+ .pipe(setupRequestContextFromCurrent())
609
+
610
+ it("works well in Memory", () => test.pipe(Effect.provide(OrderRepo.Test), rt.runPromise))
611
+
612
+ it("works well in SQLite", () => test.pipe(Effect.provide(OrderRepo.TestSqlite), rt.runPromise))
613
+ })
614
+
615
+ // Same but mimics the FULL controller projection: includes `items` array
616
+ // (NonEmptyArray) alongside the computed scalars. This tests the
617
+ // memory-side select pipeline that combines subKeys (items) with
618
+ // computedKeys in one Project node.
619
+ describe("scanner-style AllPickList — items + computed combined", () => {
620
+ const test = Effect
621
+ .gen(function*() {
622
+ const repo = yield* OrderRepo
623
+ type OrderEnc = S.Codec.Encoded<typeof Order>
624
+
625
+ const projection = S.Struct({
626
+ id: S.String,
627
+ items: S.NonEmptyArray(ArticleLineItem.mapFields(Struct.pick(["articleId", "articleGTIN"]))),
628
+ articleCount: S.NonNegativeInt,
629
+ allItemsPicked: S.Boolean,
630
+ articleIds: S.Array(S.String)
631
+ })
632
+
633
+ const result = yield* repo.query(
634
+ where("state.at", "gte", "2026-05-05T00:00:00Z"),
635
+ and("state._tag", "neq", "closed"),
636
+ projectComputed(
637
+ projection,
638
+ computed({
639
+ articleCount: relation<OrderEnc>("items").count(),
640
+ allItemsPicked: relation<OrderEnc>("items").every(where("state._tag", "picked")),
641
+ articleIds: relation<OrderEnc>("items").collectDistinct("articleId")
642
+ })
643
+ )
644
+ )
645
+
646
+ expect(result.length).toBe(2)
647
+ const byId = Object.fromEntries(result.map((r) => [r.id, r]))
648
+ const open = byId["o-open-1"]!
649
+ expect(open.items.length).toBe(3)
650
+ expect(open.items[0]).toHaveProperty("articleId")
651
+ expect(open.items[0]).toHaveProperty("articleGTIN")
652
+ expect(open.allItemsPicked).toBe(false)
653
+ const allp = byId["o-allpicked-2"]!
654
+ expect(allp.items.length).toBe(2)
655
+ expect(allp.allItemsPicked).toBe(true)
656
+ })
657
+ .pipe(setupRequestContextFromCurrent())
658
+
659
+ it("works well in Memory", () => test.pipe(Effect.provide(OrderRepo.Test), rt.runPromise))
660
+
661
+ it("works well in SQLite", () => test.pipe(Effect.provide(OrderRepo.TestSqlite), rt.runPromise))
662
+ })
663
+
372
664
  describe("removeByIds", () => {
373
665
  const test = Effect
374
666
  .gen(function*() {
@@ -405,7 +697,7 @@ describe("removeByIds", () => {
405
697
 
406
698
  yield* repo.saveAndPublish(items)
407
699
  const itemsAfterSave = yield* repo.all
408
- yield* repo.removeById(...items.slice(0, 2).map((_) => _.id))
700
+ yield* repo.removeById([items[0]!.id, items[1]!.id])
409
701
 
410
702
  const items2 = yield* repo.all
411
703
 
@@ -0,0 +1,62 @@
1
+ import { describe, expect, it } from "@effect/vitest"
2
+ import * as Effect from "effect-app/Effect"
3
+ import * as Layer from "effect-app/Layer"
4
+ import * as S from "effect-app/Schema"
5
+ import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
6
+ import { makeRepo } from "../src/Model/Repository.js"
7
+ import { RepositoryRegistryLive } from "../src/Model/Repository/Registry.js"
8
+ import { MemoryStoreLive } from "../src/Store/Memory.js"
9
+
10
+ class BatchItem extends S.Class<BatchItem>("BatchItem")({
11
+ id: S.String,
12
+ label: S.String
13
+ }) {}
14
+
15
+ const TestStoreLive = Layer.merge(MemoryStoreLive, RepositoryRegistryLive)
16
+
17
+ describe("repository ext save/remove batching", () => {
18
+ it.effect("supports save batching overload", () =>
19
+ Effect
20
+ .gen(function*() {
21
+ const repo = yield* makeRepo("BatchItem", BatchItem, {})
22
+ const items = [
23
+ new BatchItem({ id: "1", label: "one" }),
24
+ new BatchItem({ id: "2", label: "two" }),
25
+ new BatchItem({ id: "3", label: "three" }),
26
+ new BatchItem({ id: "4", label: "four" })
27
+ ] as const
28
+
29
+ yield* repo.save(items, { batch: 2 })
30
+
31
+ const all = yield* repo.all
32
+ expect(all).toHaveLength(4)
33
+ expect(all.map((_) => _.id).toSorted()).toEqual(["1", "2", "3", "4"])
34
+ })
35
+ .pipe(
36
+ setupRequestContextFromCurrent(),
37
+ Effect.provide(TestStoreLive)
38
+ ))
39
+
40
+ it.effect("supports remove batching overload", () =>
41
+ Effect
42
+ .gen(function*() {
43
+ const repo = yield* makeRepo("BatchItem", BatchItem, {})
44
+ const items = [
45
+ new BatchItem({ id: "1", label: "one" }),
46
+ new BatchItem({ id: "2", label: "two" }),
47
+ new BatchItem({ id: "3", label: "three" }),
48
+ new BatchItem({ id: "4", label: "four" })
49
+ ] as const
50
+
51
+ yield* repo.save(items)
52
+ yield* repo.remove([items[0], items[1], items[2]], { batch: true })
53
+
54
+ const all = yield* repo.all
55
+ expect(all).toHaveLength(1)
56
+ expect(all[0]?.id).toBe("4")
57
+ })
58
+ .pipe(
59
+ setupRequestContextFromCurrent(),
60
+ Effect.provide(TestStoreLive)
61
+ ))
62
+ })
@@ -1,10 +1,14 @@
1
1
  import { describe, expect, expectTypeOf, it } from "@effect/vitest"
2
- import { Effect, Layer, Result, S, ServiceMap } from "effect-app"
3
2
  import { NotLoggedInError, UnauthorizedError } from "effect-app/client"
3
+ import * as Context from "effect-app/Context"
4
+ import * as Effect from "effect-app/Effect"
4
5
  import { HttpHeaders } from "effect-app/http"
6
+ import * as Layer from "effect-app/Layer"
5
7
  import * as RpcX from "effect-app/rpc"
6
8
  import { MiddlewareMaker } from "effect-app/rpc"
9
+ import * as S from "effect-app/Schema"
7
10
  import type { unhandled } from "effect-app/Types"
11
+ import * as Result from "effect/Result"
8
12
  import { Rpc } from "effect/unstable/rpc"
9
13
  import { type SuccessValue } from "effect/unstable/rpc/RpcMiddleware"
10
14
  import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive } from "./fixtures.js"
@@ -63,11 +67,12 @@ const testSuite = (_mw: typeof middleware3) =>
63
67
  "works",
64
68
  Effect.fn(function*() {
65
69
  const defaultOpts = {
70
+ client: null as any, // TODO?
66
71
  headers: HttpHeaders.fromRecordUnsafe({}),
67
72
  payload: { _tag: "Test" },
68
73
  clientId: 0,
69
74
  requestId: "test-id" as any,
70
- rpc: { ...TestRpc, annotations: ServiceMap.make(_mw.requestContext, {}) }
75
+ rpc: { ...TestRpc, annotations: Context.make(_mw.requestContext, {}) }
71
76
  }
72
77
  const next = Effect.void as unknown as Effect.Effect<SuccessValue, unhandled, never>
73
78
  const layer = _mw.layer.pipe(
@@ -89,7 +94,7 @@ const testSuite = (_mw: typeof middleware3) =>
89
94
  headers: HttpHeaders.fromRecordUnsafe({ "x-user": "test-user", "x-is-manager": "true" }),
90
95
  rpc: {
91
96
  ...defaultOpts.rpc,
92
- annotations: ServiceMap.make(_mw.requestContext, { requireRoles: ["manager"] })
97
+ annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
93
98
  }
94
99
  })
95
100
  )
@@ -127,7 +132,7 @@ const testSuite = (_mw: typeof middleware3) =>
127
132
  Object.assign({ ...defaultOpts }, {
128
133
  rpc: {
129
134
  ...defaultOpts.rpc,
130
- annotations: ServiceMap.make(_mw.requestContext, { requireRoles: ["manager"] })
135
+ annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
131
136
  }
132
137
  })
133
138
  )
@@ -153,7 +158,7 @@ const testSuite = (_mw: typeof middleware3) =>
153
158
  {
154
159
  rpc: {
155
160
  ...defaultOpts.rpc,
156
- annotations: ServiceMap.make(_mw.requestContext, { requireRoles: ["manager"] })
161
+ annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
157
162
  }
158
163
  }
159
164
  )