@effect-app/infra 4.0.0-beta.20 → 4.0.0-beta.201

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 (305) hide show
  1. package/CHANGELOG.md +1410 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +15 -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 +43 -32
  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 +142 -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 +16 -16
  53. package/dist/Model/query/dsl.d.ts.map +1 -1
  54. package/dist/Model/query/new-kid-interpreter.d.ts +6 -6
  55. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  56. package/dist/Model/query/new-kid-interpreter.js +3 -3
  57. package/dist/Model/query.d.ts +1 -1
  58. package/dist/Model.d.ts +2 -1
  59. package/dist/Model.d.ts.map +1 -1
  60. package/dist/Model.js +2 -1
  61. package/dist/QueueMaker/SQLQueue.d.ts +5 -7
  62. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  63. package/dist/QueueMaker/SQLQueue.js +105 -114
  64. package/dist/QueueMaker/errors.d.ts +2 -2
  65. package/dist/QueueMaker/errors.d.ts.map +1 -1
  66. package/dist/QueueMaker/memQueue.d.ts +7 -4
  67. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  68. package/dist/QueueMaker/memQueue.js +51 -62
  69. package/dist/QueueMaker/sbqueue.d.ts +6 -3
  70. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  71. package/dist/QueueMaker/sbqueue.js +37 -53
  72. package/dist/QueueMaker/service.d.ts +1 -1
  73. package/dist/RequestContext.d.ts +112 -26
  74. package/dist/RequestContext.d.ts.map +1 -1
  75. package/dist/RequestContext.js +7 -8
  76. package/dist/RequestFiberSet.d.ts +7 -7
  77. package/dist/RequestFiberSet.d.ts.map +1 -1
  78. package/dist/RequestFiberSet.js +5 -5
  79. package/dist/Store/ContextMapContainer.d.ts +19 -3
  80. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  81. package/dist/Store/ContextMapContainer.js +13 -3
  82. package/dist/Store/Cosmos/query.d.ts +1 -1
  83. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  84. package/dist/Store/Cosmos/query.js +10 -12
  85. package/dist/Store/Cosmos.d.ts +1 -1
  86. package/dist/Store/Cosmos.d.ts.map +1 -1
  87. package/dist/Store/Cosmos.js +318 -240
  88. package/dist/Store/Disk.d.ts +2 -2
  89. package/dist/Store/Disk.d.ts.map +1 -1
  90. package/dist/Store/Disk.js +25 -22
  91. package/dist/Store/Memory.d.ts +4 -4
  92. package/dist/Store/Memory.d.ts.map +1 -1
  93. package/dist/Store/Memory.js +27 -22
  94. package/dist/Store/SQL/Pg.d.ts +4 -0
  95. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  96. package/dist/Store/SQL/Pg.js +189 -0
  97. package/dist/Store/SQL/query.d.ts +38 -0
  98. package/dist/Store/SQL/query.d.ts.map +1 -0
  99. package/dist/Store/SQL/query.js +367 -0
  100. package/dist/Store/SQL.d.ts +20 -0
  101. package/dist/Store/SQL.d.ts.map +1 -0
  102. package/dist/Store/SQL.js +381 -0
  103. package/dist/Store/codeFilter.d.ts +1 -1
  104. package/dist/Store/codeFilter.d.ts.map +1 -1
  105. package/dist/Store/codeFilter.js +2 -1
  106. package/dist/Store/index.d.ts +5 -2
  107. package/dist/Store/index.d.ts.map +1 -1
  108. package/dist/Store/index.js +15 -3
  109. package/dist/Store/service.d.ts +18 -7
  110. package/dist/Store/service.d.ts.map +1 -1
  111. package/dist/Store/service.js +24 -6
  112. package/dist/Store/utils.d.ts +1 -1
  113. package/dist/Store/utils.d.ts.map +1 -1
  114. package/dist/Store/utils.js +3 -4
  115. package/dist/Store.d.ts +1 -1
  116. package/dist/adapters/SQL/Model.d.ts +28 -42
  117. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  118. package/dist/adapters/SQL/Model.js +2 -2
  119. package/dist/adapters/SQL.d.ts +1 -1
  120. package/dist/adapters/ServiceBus.d.ts +11 -11
  121. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  122. package/dist/adapters/ServiceBus.js +13 -15
  123. package/dist/adapters/cosmos-client.d.ts +3 -3
  124. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  125. package/dist/adapters/cosmos-client.js +3 -3
  126. package/dist/adapters/index.d.ts +8 -2
  127. package/dist/adapters/index.d.ts.map +1 -1
  128. package/dist/adapters/index.js +8 -2
  129. package/dist/adapters/logger.d.ts +2 -2
  130. package/dist/adapters/logger.d.ts.map +1 -1
  131. package/dist/adapters/memQueue.d.ts +3 -3
  132. package/dist/adapters/memQueue.d.ts.map +1 -1
  133. package/dist/adapters/memQueue.js +3 -3
  134. package/dist/adapters/mongo-client.d.ts +3 -3
  135. package/dist/adapters/mongo-client.d.ts.map +1 -1
  136. package/dist/adapters/mongo-client.js +3 -3
  137. package/dist/adapters/redis-client.d.ts +3 -3
  138. package/dist/adapters/redis-client.d.ts.map +1 -1
  139. package/dist/adapters/redis-client.js +3 -3
  140. package/dist/api/ContextProvider.d.ts +8 -8
  141. package/dist/api/ContextProvider.d.ts.map +1 -1
  142. package/dist/api/ContextProvider.js +6 -6
  143. package/dist/api/codec.d.ts +1 -1
  144. package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
  145. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  146. package/dist/api/internal/RequestContextMiddleware.js +2 -2
  147. package/dist/api/internal/auth.d.ts +44 -6
  148. package/dist/api/internal/auth.d.ts.map +1 -1
  149. package/dist/api/internal/auth.js +160 -29
  150. package/dist/api/internal/events.d.ts +3 -3
  151. package/dist/api/internal/events.d.ts.map +1 -1
  152. package/dist/api/internal/events.js +9 -7
  153. package/dist/api/internal/health.d.ts +1 -1
  154. package/dist/api/layerUtils.d.ts +6 -6
  155. package/dist/api/layerUtils.d.ts.map +1 -1
  156. package/dist/api/layerUtils.js +5 -5
  157. package/dist/api/middlewares.d.ts +1 -1
  158. package/dist/api/reportError.d.ts +1 -1
  159. package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
  160. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  161. package/dist/api/routing/middleware/middleware.d.ts +39 -3
  162. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  163. package/dist/api/routing/middleware/middleware.js +46 -14
  164. package/dist/api/routing/middleware.d.ts +1 -2
  165. package/dist/api/routing/middleware.d.ts.map +1 -1
  166. package/dist/api/routing/middleware.js +1 -2
  167. package/dist/api/routing/schema/jwt.d.ts +1 -1
  168. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  169. package/dist/api/routing/tsort.d.ts +1 -1
  170. package/dist/api/routing/tsort.d.ts.map +1 -1
  171. package/dist/api/routing/utils.d.ts +3 -3
  172. package/dist/api/routing/utils.d.ts.map +1 -1
  173. package/dist/api/routing.d.ts +25 -26
  174. package/dist/api/routing.d.ts.map +1 -1
  175. package/dist/api/routing.js +99 -35
  176. package/dist/api/setupRequest.d.ts +8 -5
  177. package/dist/api/setupRequest.d.ts.map +1 -1
  178. package/dist/api/setupRequest.js +12 -7
  179. package/dist/api/util.d.ts +1 -1
  180. package/dist/arbs.d.ts +1 -1
  181. package/dist/arbs.d.ts.map +1 -1
  182. package/dist/arbs.js +5 -3
  183. package/dist/errorReporter.d.ts +4 -4
  184. package/dist/errorReporter.d.ts.map +1 -1
  185. package/dist/errorReporter.js +20 -25
  186. package/dist/errors.d.ts +1 -1
  187. package/dist/fileUtil.d.ts +1 -1
  188. package/dist/fileUtil.d.ts.map +1 -1
  189. package/dist/index.d.ts +1 -1
  190. package/dist/logger/jsonLogger.d.ts +1 -1
  191. package/dist/logger/logFmtLogger.d.ts +1 -1
  192. package/dist/logger/shared.d.ts +1 -1
  193. package/dist/logger/shared.js +2 -2
  194. package/dist/logger.d.ts +1 -1
  195. package/dist/logger.d.ts.map +1 -1
  196. package/dist/rateLimit.d.ts +9 -3
  197. package/dist/rateLimit.d.ts.map +1 -1
  198. package/dist/rateLimit.js +5 -11
  199. package/dist/test.d.ts +2 -2
  200. package/dist/test.d.ts.map +1 -1
  201. package/dist/test.js +1 -1
  202. package/dist/vitest.d.ts +1 -1
  203. package/examples/query.ts +39 -35
  204. package/package.json +41 -37
  205. package/src/CUPS.ts +9 -11
  206. package/src/Emailer/Sendgrid.ts +17 -14
  207. package/src/Emailer/service.ts +9 -3
  208. package/src/MainFiberSet.ts +5 -6
  209. package/src/Model/Repository/Registry.ts +33 -0
  210. package/src/Model/Repository/ext.ts +96 -10
  211. package/src/Model/Repository/internal/internal.ts +97 -88
  212. package/src/Model/Repository/makeRepo.ts +12 -10
  213. package/src/Model/Repository/service.ts +31 -22
  214. package/src/Model/Repository/validation.ts +4 -4
  215. package/src/Model/Repository.ts +1 -0
  216. package/src/Model/dsl.ts +3 -3
  217. package/src/Model/filter/types/path/eager.ts +1 -2
  218. package/src/Model/query/dsl.ts +18 -18
  219. package/src/Model/query/new-kid-interpreter.ts +2 -2
  220. package/src/Model.ts +1 -0
  221. package/src/QueueMaker/SQLQueue.ts +121 -151
  222. package/src/QueueMaker/memQueue.ts +82 -103
  223. package/src/QueueMaker/sbqueue.ts +56 -86
  224. package/src/RequestContext.ts +8 -10
  225. package/src/RequestFiberSet.ts +4 -4
  226. package/src/Store/ContextMapContainer.ts +41 -2
  227. package/src/Store/Cosmos/query.ts +16 -20
  228. package/src/Store/Cosmos.ts +452 -342
  229. package/src/Store/Disk.ts +52 -49
  230. package/src/Store/Memory.ts +54 -48
  231. package/src/Store/SQL/Pg.ts +318 -0
  232. package/src/Store/SQL/query.ts +409 -0
  233. package/src/Store/SQL.ts +668 -0
  234. package/src/Store/codeFilter.ts +1 -0
  235. package/src/Store/index.ts +17 -2
  236. package/src/Store/service.ts +32 -8
  237. package/src/Store/utils.ts +23 -22
  238. package/src/adapters/SQL/Model.ts +10 -4
  239. package/src/adapters/ServiceBus.ts +112 -116
  240. package/src/adapters/cosmos-client.ts +2 -2
  241. package/src/adapters/index.ts +7 -0
  242. package/src/adapters/memQueue.ts +2 -2
  243. package/src/adapters/mongo-client.ts +2 -2
  244. package/src/adapters/redis-client.ts +2 -2
  245. package/src/api/ContextProvider.ts +12 -13
  246. package/src/api/internal/RequestContextMiddleware.ts +1 -1
  247. package/src/api/internal/auth.ts +246 -44
  248. package/src/api/internal/events.ts +12 -8
  249. package/src/api/layerUtils.ts +8 -8
  250. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  251. package/src/api/routing/middleware/middleware.ts +53 -12
  252. package/src/api/routing/middleware.ts +0 -2
  253. package/src/api/routing.ts +173 -63
  254. package/src/api/setupRequest.ts +28 -8
  255. package/src/arbs.ts +4 -2
  256. package/src/errorReporter.ts +62 -74
  257. package/src/logger/shared.ts +1 -1
  258. package/src/rateLimit.ts +30 -22
  259. package/src/test.ts +1 -1
  260. package/test/auth.test.ts +101 -0
  261. package/test/contextProvider.test.ts +11 -11
  262. package/test/controller.test.ts +19 -17
  263. package/test/dist/auth.test.d.ts.map +1 -0
  264. package/test/dist/contextProvider.test.d.ts.map +1 -1
  265. package/test/dist/controller.test.d.ts.map +1 -1
  266. package/test/dist/date-query.test.d.ts.map +1 -0
  267. package/test/dist/fixtures.d.ts +26 -12
  268. package/test/dist/fixtures.d.ts.map +1 -1
  269. package/test/dist/fixtures.js +12 -10
  270. package/test/dist/query.test.d.ts.map +1 -1
  271. package/test/dist/rawQuery.test.d.ts.map +1 -1
  272. package/test/dist/repository-ext.test.d.ts.map +1 -0
  273. package/test/dist/requires.test.d.ts.map +1 -1
  274. package/test/dist/router-generator.test.d.ts.map +1 -0
  275. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  276. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  277. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  278. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  279. package/test/dist/sql-store.test.d.ts.map +1 -0
  280. package/test/fixtures.ts +11 -9
  281. package/test/query.test.ts +216 -34
  282. package/test/rawQuery.test.ts +23 -19
  283. package/test/repository-ext.test.ts +60 -0
  284. package/test/requires.test.ts +6 -6
  285. package/test/router-generator.test.ts +183 -0
  286. package/test/routing-interruptibility.test.ts +63 -0
  287. package/test/rpc-e2e-invalidation.test.ts +249 -0
  288. package/test/rpc-multi-middleware.test.ts +78 -9
  289. package/test/rpc-stream-fullstack.test.ts +325 -0
  290. package/test/sql-store.test.ts +1064 -0
  291. package/test/validateSample.test.ts +15 -12
  292. package/tsconfig.examples.json +1 -1
  293. package/tsconfig.json +0 -1
  294. package/tsconfig.json.bak +2 -2
  295. package/tsconfig.src.json +35 -35
  296. package/tsconfig.test.json +2 -2
  297. package/dist/Operations.d.ts +0 -55
  298. package/dist/Operations.d.ts.map +0 -1
  299. package/dist/Operations.js +0 -102
  300. package/dist/OperationsRepo.d.ts +0 -41
  301. package/dist/OperationsRepo.d.ts.map +0 -1
  302. package/dist/OperationsRepo.js +0 -14
  303. package/eslint.config.mjs +0 -24
  304. package/src/Operations.ts +0 -235
  305. package/src/OperationsRepo.ts +0 -16
@@ -9,6 +9,22 @@ import type { Query, QueryEnd, QueryWhere } from "../query.js"
9
9
  import * as Q from "../query.js"
10
10
  import type { Repository } from "./service.js"
11
11
 
12
+ interface BatchOptions {
13
+ readonly batch?: true | number
14
+ }
15
+
16
+ const asReadonlyArray = <T>(itemOrItems: T | ReadonlyArray<T>): ReadonlyArray<T> =>
17
+ globalThis.Array.isArray(itemOrItems)
18
+ ? itemOrItems as ReadonlyArray<T>
19
+ : [itemOrItems as T]
20
+
21
+ const getBatchSize = (batch?: true | number) =>
22
+ batch === true
23
+ ? 100
24
+ : typeof batch === "number" && Number.isFinite(batch) && batch > 0
25
+ ? Math.floor(batch)
26
+ : undefined
27
+
12
28
  export const extendRepo = <
13
29
  T,
14
30
  Encoded extends FieldValues,
@@ -16,9 +32,10 @@ export const extendRepo = <
16
32
  ItemType extends string,
17
33
  IdKey extends keyof T & keyof Encoded,
18
34
  RSchema,
19
- RPublish
35
+ RPublish,
36
+ RProvided = never
20
37
  >(
21
- repo: Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish>
38
+ repo: Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided>
22
39
  ) => {
23
40
  const get = (id: T[IdKey]) =>
24
41
  repo.find(id).pipe(
@@ -109,7 +126,7 @@ export const extendRepo = <
109
126
  ) =>
110
127
  | Query<Encoded>
111
128
  | QueryWhere<Encoded>
112
- | QueryEnd<Encoded, "many">,
129
+ | QueryEnd<Encoded>,
113
130
  pure: Effect.Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>
114
131
  ): Effect.Effect<
115
132
  A,
@@ -126,7 +143,7 @@ export const extendRepo = <
126
143
  ) =>
127
144
  | Query<Encoded>
128
145
  | QueryWhere<Encoded>
129
- | QueryEnd<Encoded, "many">,
146
+ | QueryEnd<Encoded>,
130
147
  pure: Effect.Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>,
131
148
  batch: "batched" | number
132
149
  ): Effect.Effect<
@@ -214,8 +231,7 @@ export const extendRepo = <
214
231
  _key: unknown
215
232
  ) =>
216
233
  (repo.query(Q.where(repo.idKey as any, "in" as any, entries.map((_) => _.request.id)) as any) as Effect.Effect<
217
- readonly T[],
218
- never
234
+ readonly T[]
219
235
  >)
220
236
  // TODO
221
237
  .pipe(
@@ -244,8 +260,77 @@ export const extendRepo = <
244
260
  request: (id: T[IdKey]) => Effect.request(_request({ id }), requestResolver),
245
261
  get,
246
262
  log: (evt: Evt) => AnyPureDSL.log(evt),
247
- save: (...items: NonEmptyArray<T>) => repo.saveAndPublish(items),
263
+ /**
264
+ * Enables chunked writes for large batches via `options.batch`.
265
+ * Note: batching breaks transactional properties because chunks are saved independently.
266
+ */
267
+ save: ((itemOrItems: T | ReadonlyArray<T>, options?: BatchOptions) => {
268
+ const items = asReadonlyArray(itemOrItems)
269
+ if (!Array.isReadonlyArrayNonEmpty(items)) {
270
+ return Effect.void
271
+ }
272
+ const batchSize = getBatchSize(options?.batch)
273
+ if (batchSize === undefined) {
274
+ return repo.saveAndPublish(items)
275
+ }
276
+ return Effect.forEach(
277
+ Array.chunksOf(items, batchSize),
278
+ (batch) => repo.saveAndPublish(batch),
279
+ { discard: true }
280
+ )
281
+ }) as (
282
+ itemOrItems: T | ReadonlyArray<T>,
283
+ options?: BatchOptions
284
+ ) => Effect.Effect<
285
+ void,
286
+ InvalidStateError | OptimisticConcurrencyException,
287
+ RSchema | RPublish
288
+ >,
248
289
  saveWithEvents: (events: Iterable<Evt>) => (...items: NonEmptyArray<T>) => repo.saveAndPublish(items, events),
290
+ /**
291
+ * Enables chunked deletes for large batches via `options.batch`.
292
+ * Note: batching breaks transactional properties because chunks are removed independently.
293
+ */
294
+ remove: ((itemOrItems: T | ReadonlyArray<T>, options?: BatchOptions) => {
295
+ const items = asReadonlyArray(itemOrItems)
296
+ if (!Array.isReadonlyArrayNonEmpty(items)) {
297
+ return Effect.void
298
+ }
299
+ const batchSize = getBatchSize(options?.batch)
300
+ if (batchSize === undefined) {
301
+ return repo.removeAndPublish(items)
302
+ }
303
+ return Effect.forEach(
304
+ Array.chunksOf(items, batchSize),
305
+ (batch) => repo.removeAndPublish(batch),
306
+ { discard: true }
307
+ )
308
+ }) as (
309
+ itemOrItems: T | ReadonlyArray<T>,
310
+ options?: BatchOptions
311
+ ) => Effect.Effect<void, never, RSchema | RPublish>,
312
+ /**
313
+ * Enables chunked deletes for large batches via `options.batch`.
314
+ * Note: batching breaks transactional properties because chunks are removed independently.
315
+ */
316
+ removeById: ((idOrIds: T[IdKey] | ReadonlyArray<T[IdKey]>, options?: BatchOptions) => {
317
+ const ids = asReadonlyArray(idOrIds)
318
+ if (!Array.isReadonlyArrayNonEmpty(ids)) {
319
+ return Effect.void
320
+ }
321
+ const batchSize = getBatchSize(options?.batch)
322
+ if (batchSize === undefined) {
323
+ return repo.removeById(ids)
324
+ }
325
+ return Effect.forEach(
326
+ Array.chunksOf(ids, batchSize),
327
+ (batch) => repo.removeById(batch),
328
+ { discard: true }
329
+ )
330
+ }) as (
331
+ idOrIds: T[IdKey] | ReadonlyArray<T[IdKey]>,
332
+ options?: BatchOptions
333
+ ) => Effect.Effect<void, never, RSchema>,
249
334
  queryAndSavePure,
250
335
  saveManyWithPure,
251
336
  byIdAndSaveWithPure,
@@ -268,7 +353,7 @@ export const extendRepo = <
268
353
  return {
269
354
  ...repo,
270
355
  ...exts
271
- } as Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish> & typeof exts
356
+ } as Repository<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided> & typeof exts
272
357
  }
273
358
 
274
359
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -279,5 +364,6 @@ export interface ExtendedRepository<
279
364
  ItemType extends string,
280
365
  IdKey extends keyof T & keyof Encoded,
281
366
  RSchema,
282
- RPublish
283
- > extends ReturnType<typeof extendRepo<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish>> {}
367
+ RPublish,
368
+ RProvided = never
369
+ > extends ReturnType<typeof extendRepo<T, Encoded, Evt, ItemType, IdKey, RSchema, RPublish, RProvided>> {}
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type {} from "effect/Equal"
3
- import type {} from "effect/Hash"
4
- import { Array, Chunk, Effect, Equivalence, flow, type NonEmptyReadonlyArray, Option, pipe, Pipeable, PubSub, Result, S, SchemaAST, ServiceMap, Unify } from "effect-app"
2
+
3
+ import { Array, Chunk, Context, Effect, Equivalence, flow, type NonEmptyReadonlyArray, Option, pipe, Pipeable, PubSub, Result, S, SchemaAST, Unify } from "effect-app"
5
4
  import { toNonEmptyArray } from "effect-app/Array"
6
5
  import { NotFoundError } from "effect-app/client/errors"
7
6
  import { flatMapOption } from "effect-app/Effect"
@@ -55,14 +54,14 @@ export function makeRepoInternal<
55
54
 
56
55
  function make<RInitial = never, E = never, RPublish = never, RCtx = never>(
57
56
  args: [Evt] extends [never] ? {
58
- schemaContext?: ServiceMap.ServiceMap<RCtx>
57
+ schemaContext?: Context.Context<RCtx>
59
58
  makeInitial?: Effect.Effect<readonly T[], E, RInitial> | undefined
60
59
  config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
61
60
  partitionValue?: (e?: Encoded) => string
62
61
  }
63
62
  }
64
63
  : {
65
- schemaContext?: ServiceMap.ServiceMap<RCtx>
64
+ schemaContext?: Context.Context<RCtx>
66
65
  publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect.Effect<void, never, RPublish>
67
66
  makeInitial?: Effect.Effect<readonly T[], E, RInitial> | undefined
68
67
  config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
@@ -72,16 +71,16 @@ export function makeRepoInternal<
72
71
  ) {
73
72
  return Effect
74
73
  .gen(function*() {
75
- const rctx: ServiceMap.ServiceMap<RCtx> = args.schemaContext ?? ServiceMap.empty() as any
74
+ const rctx: Context.Context<RCtx> = args.schemaContext ?? Context.empty() as any
76
75
  const provideRctx = Effect.provide(rctx)
77
76
  const encodeMany = flow(
78
77
  S.encodeEffect(S.Array(schema)),
79
78
  provideRctx,
80
- Effect.withSpan("encodeMany", {}, { captureStackTrace: false })
79
+ Effect.withSpan("encodeMany", { attributes: { itemType: name } }, { captureStackTrace: false })
81
80
  )
82
- const decode = flow(S.decodeEffect(schema), provideRctx)
81
+ const decode = flow(S.decodeEffectConcurrently(schema), provideRctx)
83
82
  const decodeMany = flow(
84
- S.decodeEffect(S.Array(schema)),
83
+ S.decodeEffectConcurrently(S.Array(schema)),
85
84
  provideRctx
86
85
  )
87
86
 
@@ -113,11 +112,14 @@ export function makeRepoInternal<
113
112
  let ast = _.ast
114
113
  if (ast._tag === "Declaration") ast = ast.typeParameters[0]!
115
114
 
116
- // In v4, to get the encoded (from) side of a schema, use SchemaAST.toEncoded
117
115
  const pickIdFromAst = (a: SchemaAST.AST) => {
118
- const encoded = SchemaAST.toEncoded(a)
119
- if (SchemaAST.isObjects(encoded)) {
120
- const field = encoded.propertySignatures.find((_) => _.name === idKey)
116
+ // Unwrap Declaration (e.g. TaggedClass) to get the underlying Objects AST
117
+ let inner = a
118
+ if (inner._tag === "Declaration") inner = inner.typeParameters[0]!
119
+ // Pick from the original AST to preserve the full encoding chain (e.g. decodeTo transformations).
120
+ // Using toEncoded would lose transformation info needed to encode Type -> Encoded.
121
+ if (SchemaAST.isObjects(inner)) {
122
+ const field = inner.propertySignatures.find((_) => _.name === idKey)
121
123
  if (field) {
122
124
  return S.Struct({ [idKey]: S.make(field.type) }) as unknown as Codec<T, Encoded>
123
125
  }
@@ -161,7 +163,7 @@ export function makeRepoInternal<
161
163
  )
162
164
  })
163
165
 
164
- const find = Effect.fn("find")(function*(id: T[IdKey]) {
166
+ const find = Effect.fn("find", { attributes: { itemType: name } })(function*(id: T[IdKey]) {
165
167
  yield* Effect.annotateCurrentSpan({ itemId: id })
166
168
 
167
169
  return yield* flatMapOption(findE(id), (_) => Effect.orDie(decode(_)))
@@ -188,73 +190,78 @@ export function makeRepoInternal<
188
190
  Effect.andThen(saveAllE)
189
191
  )
190
192
 
191
- const saveAndPublish = Effect.fn("saveAndPublish")(function*(items: Iterable<T>, events: Iterable<Evt> = []) {
192
- const it = Chunk.fromIterable(items)
193
- const evts = [...events]
194
- yield* Effect.annotateCurrentSpan({ itemIds: [...Chunk.map(it, (_) => _[idKey])], events: evts.length })
195
- return yield* saveAll(it)
196
- .pipe(
197
- Effect.andThen(Effect.sync(() => toNonEmptyArray(evts))),
198
- // TODO: for full consistency the events should be stored within the same database transaction, and then picked up.
199
- (_) => flatMapOption(_, pub),
200
- Effect.andThen(PubSub.publish(changeFeed, [Chunk.toArray(it), "save"] as [T[], "save" | "remove"])),
201
- Effect.asVoid
202
- )
203
- })
193
+ const saveAndPublish = Effect.fn("saveAndPublish", { attributes: { itemType: name } })(
194
+ function*(items: Iterable<T>, events: Iterable<Evt> = []) {
195
+ const it = Chunk.fromIterable(items)
196
+ const evts = [...events]
197
+ yield* Effect.annotateCurrentSpan({ itemIds: Chunk.map(it, (_) => _[idKey]), events: evts.length })
198
+ return yield* saveAll(it)
199
+ .pipe(
200
+ Effect.andThen(Effect.sync(() => toNonEmptyArray(evts))),
201
+ // TODO: for full consistency the events should be stored within the same database transaction, and then picked up.
202
+ (_) => flatMapOption(_, pub),
203
+ Effect.andThen(PubSub.publish(changeFeed, [Chunk.toArray(it), "save"] as [T[], "save" | "remove"])),
204
+ Effect.asVoid
205
+ )
206
+ }
207
+ )
204
208
 
205
- const removeAndPublish = Effect.fn("removeAndPublish")(function*(a: Iterable<T>, events: Iterable<Evt> = []) {
206
- const { set } = yield* cms
207
- const it = [...a]
208
- const evts = [...events]
209
- yield* Effect.annotateCurrentSpan({ itemIds: it.map((_) => _[idKey]), eventCount: evts.length })
210
- const items = yield* encodeMany(it).pipe(Effect.orDie)
211
- if (Array.isReadonlyArrayNonEmpty(items)) {
212
- yield* store.batchRemove(
213
- items.map((_) => (_[idKey])),
214
- args.config?.partitionValue?.(items[0])
215
- )
216
- for (const e of items) {
217
- set(e[idKey], undefined)
218
- }
219
- yield* Effect
220
- .sync(() => toNonEmptyArray(evts))
221
- // TODO: for full consistency the events should be stored within the same database transaction, and then picked up.
222
- .pipe((_) => flatMapOption(_, pub))
209
+ const removeAndPublish = Effect.fn("removeAndPublish", { attributes: { itemType: name } })(
210
+ function*(a: Iterable<T>, events: Iterable<Evt> = []) {
211
+ const { set } = yield* cms
212
+ const it = [...a]
213
+ const evts = [...events]
214
+ yield* Effect.annotateCurrentSpan({ itemIds: it.map((_) => _[idKey]), eventCount: evts.length })
215
+ const items = yield* encodeMany(it).pipe(Effect.orDie)
216
+ if (Array.isReadonlyArrayNonEmpty(items)) {
217
+ yield* store.batchRemove(
218
+ items.map((_) => (_[idKey])),
219
+ args.config?.partitionValue?.(items[0])
220
+ )
221
+ for (const e of items) {
222
+ set(e[idKey], undefined)
223
+ }
224
+ yield* Effect
225
+ .sync(() => toNonEmptyArray(evts))
226
+ // TODO: for full consistency the events should be stored within the same database transaction, and then picked up.
227
+ .pipe((_) => flatMapOption(_, pub))
223
228
 
224
- yield* PubSub.publish(changeFeed, [it, "remove"] as [T[], "save" | "remove"])
229
+ yield* PubSub.publish(changeFeed, [it, "remove"] as [T[], "save" | "remove"])
230
+ }
225
231
  }
226
- })
232
+ )
227
233
 
228
- const removeById = Effect.fn("removeById")(function*(...ids: readonly T[IdKey][]) {
229
- if (!Array.isReadonlyArrayNonEmpty(ids)) {
230
- return
231
- }
232
- const { set } = yield* cms
233
- const eids = yield* Effect.forEach(ids, (_) => encodeIdOnly(_ as any)).pipe(Effect.orDie)
234
- yield* Effect.annotateCurrentSpan({ itemIds: eids })
235
- yield* store.batchRemove(eids)
236
- for (const id of eids) {
237
- set(id, undefined)
234
+ const removeById = Effect.fn("removeById", { attributes: { itemType: name } })(
235
+ function*(idOrIds: T[IdKey] | ReadonlyArray<T[IdKey]>) {
236
+ const ids = globalThis.Array.isArray(idOrIds)
237
+ ? idOrIds as readonly T[IdKey][]
238
+ : [idOrIds as T[IdKey]]
239
+ if (!Array.isReadonlyArrayNonEmpty(ids)) {
240
+ return
241
+ }
242
+ const { set } = yield* cms
243
+ const eids = yield* Effect.forEach(ids, (_) => encodeIdOnly(_ as any)).pipe(Effect.orDie)
244
+ yield* Effect.annotateCurrentSpan({ itemIds: eids })
245
+ yield* store.batchRemove(eids)
246
+ for (const id of eids) {
247
+ set(id, undefined)
248
+ }
249
+ yield* PubSub.publish(changeFeed, [[], "remove"] as [T[], "save" | "remove"])
238
250
  }
239
- yield* PubSub.publish(changeFeed, [[], "remove"] as [T[], "save" | "remove"])
240
- })
251
+ )
241
252
 
242
- const parseMany = (items: readonly PM[]) =>
243
- Effect
244
- .flatMap(cms, (cm) =>
245
- decodeMany(items.map((_) => mapReverse(_, cm.set)))
246
- .pipe(Effect.orDie, Effect.withSpan("parseMany", {}, { captureStackTrace: false })))
247
- const parseMany2 = <A, R>(
248
- items: readonly PM[],
249
- schema: S.Codec<A, Encoded, R>
250
- ) =>
251
- Effect
252
- .flatMap(cms, (cm) =>
253
- S
254
- .decodeEffect(S.Array(schema))(
255
- items.map((_) => mapReverse(_, cm.set))
256
- )
257
- .pipe(Effect.orDie, Effect.withSpan("parseMany2", {}, { captureStackTrace: false })))
253
+ const parseMany = Effect.fn("parseMany", { attributes: { itemType: name } })(function*(items: readonly PM[]) {
254
+ const cm = yield* cms
255
+ return yield* decodeMany(items.map((_) => mapReverse(_, cm.set))).pipe(Effect.orDie)
256
+ })
257
+ const parseMany2 = Effect.fn("parseMany2", { attributes: { itemType: name } })(
258
+ function*<A, R>(items: readonly PM[], schema: S.Codec<A, Encoded, R>) {
259
+ const cm = yield* cms
260
+ return yield* S.decodeEffectConcurrently(S.Array(schema))(items.map((_) => mapReverse(_, cm.set))).pipe(
261
+ Effect.orDie
262
+ )
263
+ }
264
+ )
258
265
  const filter = <U extends keyof Encoded = keyof Encoded>(args: FilterArgs<Encoded, U>) =>
259
266
  store
260
267
  .filter(
@@ -276,24 +283,24 @@ export function makeRepoInternal<
276
283
  const query: {
277
284
  <A, R, From extends FieldValues>(
278
285
  q: Q.QueryProjection<Encoded extends From ? From : never, A, R>
279
- ): Effect.Effect<readonly A[], S.SchemaError, R>
286
+ ): Effect.Effect<readonly A[], S.SchemaError, Exclude<R, RCtx>>
280
287
  <A, R, EncodedRefined extends Encoded = Encoded>(
281
288
  q: Q.QAll<NoInfer<Encoded>, NoInfer<EncodedRefined>, A, R>
282
- ): Effect.Effect<readonly A[], never, R>
289
+ ): Effect.Effect<readonly A[], never, Exclude<R, RCtx>>
283
290
  } = (<A, R, EncodedRefined extends Encoded = Encoded>(q: Q.QAll<Encoded, EncodedRefined, A, R>) => {
284
291
  const a = Q.toFilter(q)
285
292
  const eff = a.mode === "project"
286
293
  ? filter(a)
287
294
  // TODO: mapFrom but need to support per field and dependencies
288
295
  .pipe(
289
- Effect.andThen(flow(S.decodeEffect(S.Array(a.schema ?? schema)), provideRctx))
296
+ Effect.andThen(flow(S.decodeEffectConcurrently(S.Array(a.schema ?? schema)), provideRctx))
290
297
  )
291
298
  : a.mode === "collect"
292
299
  ? filter(a)
293
300
  // TODO: mapFrom but need to support per field and dependencies
294
301
  .pipe(
295
302
  Effect.flatMap(flow(
296
- S.decodeEffect(S.Array(a.schema)),
303
+ S.decodeEffectConcurrently(S.Array(a.schema)),
297
304
  Effect.map(Array.getSomes),
298
305
  provideRctx
299
306
  ))
@@ -327,6 +334,7 @@ export function makeRepoInternal<
327
334
  : eff,
328
335
  Effect.withSpan("Repository.query [effect-app/infra]", {
329
336
  attributes: {
337
+ itemType: name,
330
338
  "repository.model_name": name,
331
339
  query: { ...a, schema: a.schema ? "__SCHEMA__" : a.schema, filter: a.filter }
332
340
  }
@@ -334,7 +342,7 @@ export function makeRepoInternal<
334
342
  )
335
343
  }) as any
336
344
 
337
- const validateSample = Effect.fn("validateSample")(function*(options?: {
345
+ const validateSample = Effect.fn("validateSample", { attributes: { itemType: name } })(function*(options?: {
338
346
  percentage?: number
339
347
  maxItems?: number
340
348
  }) {
@@ -367,14 +375,14 @@ export function makeRepoInternal<
367
375
  const rawData = rawResult.value as Encoded
368
376
  const jitMResult = mapFrom(rawData) // apply jitM
369
377
 
370
- const decodeResult = yield* S.decodeEffect(schema)(jitMResult).pipe(
378
+ const decodeResult = yield* S.decodeEffectConcurrently(schema)(jitMResult).pipe(
371
379
  Effect.result,
372
380
  provideRctx
373
381
  )
374
382
 
375
383
  if (Result.isFailure(decodeResult)) {
376
384
  errors.push(
377
- new ValidationError({
385
+ ValidationError.make({
378
386
  id,
379
387
  rawData,
380
388
  jitMResult,
@@ -384,7 +392,7 @@ export function makeRepoInternal<
384
392
  }
385
393
  }
386
394
 
387
- return new ValidationResult({
395
+ return ValidationResult.make({
388
396
  total: NonNegativeInt(allIds.length),
389
397
  sampled: NonNegativeInt(sample.length),
390
398
  valid: NonNegativeInt(sample.length - errors.length),
@@ -401,9 +409,10 @@ export function makeRepoInternal<
401
409
  saveAndPublish,
402
410
  removeAndPublish,
403
411
  removeById,
412
+ seedNamespace: (namespace: string) => store.seedNamespace(namespace),
404
413
  validateSample,
405
414
  queryRaw<A, Out, QR>(schema: S.Codec<A, Out, QR>, q: Q.RawQuery<Encoded, Out>) {
406
- const dec = S.decodeEffect(S.Array(schema))
415
+ const dec = S.decodeEffectConcurrently(S.Array(schema))
407
416
  return store.queryRaw(q).pipe(Effect.flatMap(dec))
408
417
  },
409
418
  query(q: any) {
@@ -414,9 +423,9 @@ export function makeRepoInternal<
414
423
  * @internal
415
424
  */
416
425
  mapped: <A, R>(schema: S.Codec<A, any, R>) => {
417
- const dec = S.decodeEffect(schema)
426
+ const dec = S.decodeEffectConcurrently(schema)
418
427
  const encMany = S.encodeEffect(S.Array(schema))
419
- const decMany = S.decodeEffect(S.Array(schema))
428
+ const decMany = S.decodeEffectConcurrently(S.Array(schema))
420
429
  return {
421
430
  all: allE.pipe(
422
431
  Effect.flatMap(decMany),
@@ -441,12 +450,12 @@ export function makeRepoInternal<
441
450
  // },
442
451
  save: (...xes: any[]) =>
443
452
  Effect.flatMap(encMany(xes), (_) => saveAllE(_)).pipe(
444
- Effect.withSpan("mapped.save", {}, { captureStackTrace: false })
453
+ Effect.withSpan("mapped.save", { attributes: { itemType: name } }, { captureStackTrace: false })
445
454
  )
446
455
  }
447
456
  }
448
457
  }
449
- return r as Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<R, RCtx>, RPublish>
458
+ return r as Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<R, RCtx>, RPublish, RCtx>
450
459
  })
451
460
  .pipe(Effect
452
461
  // .withSpan("Repository.make [effect-app/infra]", { attributes: { "repository.model_name": name } })
@@ -5,13 +5,13 @@
5
5
  /* eslint-disable @typescript-eslint/no-explicit-any */
6
6
 
7
7
  // import type { ParserEnv } from "effect-app/Schema/custom/Parser"
8
- import type {} from "effect/Equal"
9
- import type {} from "effect/Hash"
10
- import { Effect, type NonEmptyReadonlyArray, type S, type ServiceMap } from "effect-app"
8
+
9
+ import { type Context, Effect, type NonEmptyReadonlyArray, type S } from "effect-app"
11
10
  import type { StoreConfig, StoreMaker } from "../../Store.js"
12
11
  import type { FieldValues } from "../filter/types.js"
13
12
  import { type ExtendedRepository, extendRepo } from "./ext.js"
14
13
  import { makeRepoInternal } from "./internal/internal.js"
14
+ import { RepositoryRegistry } from "./Registry.js"
15
15
  import type { Repository } from "./service.js"
16
16
 
17
17
  export interface RepositoryOptions<
@@ -52,11 +52,11 @@ export interface RepositoryOptions<
52
52
  * Optional context to be provided to Schema decode/encode.
53
53
  * Useful for effectful transformations like XWithItems, where items is a transformation retrieving elements from another database table or other source.
54
54
  */
55
- schemaContext?: ServiceMap.ServiceMap<RCtx>
55
+ schemaContext?: Context.Context<RCtx>
56
56
 
57
57
  overrides?: (
58
- repo: Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<RSchema, RCtx>, RPublish>
59
- ) => Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<RSchema, RCtx>, RPublish>
58
+ repo: Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<RSchema, RCtx>, RPublish, RCtx>
59
+ ) => Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<RSchema, RCtx>, RPublish, RCtx>
60
60
  }
61
61
 
62
62
  /**
@@ -83,9 +83,9 @@ export const makeRepo: {
83
83
  schema: S.Codec<T, Encoded, RSchema>,
84
84
  options: RepositoryOptions<IdKey, Encoded, T, ItemType, Evt, RPublish, E, RInitial, RCtx, RSchema>
85
85
  ): Effect.Effect<
86
- ExtendedRepository<T, Encoded, Evt, ItemType, IdKey, Exclude<RSchema, RCtx>, RPublish>,
86
+ ExtendedRepository<T, Encoded, Evt, ItemType, IdKey, Exclude<RSchema, RCtx>, RPublish, RCtx>,
87
87
  E,
88
- RInitial | StoreMaker
88
+ RInitial | StoreMaker | RepositoryRegistry
89
89
  >
90
90
  <
91
91
  ItemType extends string,
@@ -102,9 +102,9 @@ export const makeRepo: {
102
102
  schema: S.Codec<T, Encoded, RSchema>,
103
103
  options: Omit<RepositoryOptions<"id", Encoded, T, ItemType, Evt, RPublish, E, RInitial, RCtx, RSchema>, "idKey">
104
104
  ): Effect.Effect<
105
- ExtendedRepository<T, Encoded, Evt, ItemType, "id", Exclude<RSchema, RCtx>, RPublish>,
105
+ ExtendedRepository<T, Encoded, Evt, ItemType, "id", Exclude<RSchema, RCtx>, RPublish, RCtx>,
106
106
  E,
107
- RInitial | StoreMaker
107
+ RInitial | StoreMaker | RepositoryRegistry
108
108
  >
109
109
  } = <
110
110
  ItemType extends string,
@@ -135,5 +135,7 @@ export const makeRepo: {
135
135
  let r = yield* mkRepo.make<RInitial, E, RPublish, RCtx>(options as any)
136
136
  if (options.overrides) r = options.overrides(r)
137
137
  const repo = extendRepo(r)
138
+ const registry = yield* RepositoryRegistry
139
+ registry.register(itemType, repo)
138
140
  return repo
139
141
  })