@effect-app/infra 2.3.5 → 2.4.0

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 (45) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/_cjs/services/OperationsRepo.cjs +10 -1
  3. package/_cjs/services/OperationsRepo.cjs.map +1 -1
  4. package/_cjs/services/Repository/dsl.cjs.map +1 -1
  5. package/_cjs/services/Repository/ext.cjs +58 -104
  6. package/_cjs/services/Repository/ext.cjs.map +1 -1
  7. package/_cjs/services/Repository/legacy.cjs +108 -0
  8. package/_cjs/services/Repository/legacy.cjs.map +1 -0
  9. package/_cjs/services/Repository.cjs +11 -0
  10. package/_cjs/services/Repository.cjs.map +1 -1
  11. package/_cjs/services/RepositoryBase.cjs +4 -222
  12. package/_cjs/services/RepositoryBase.cjs.map +1 -1
  13. package/dist/services/Operations.d.ts +3 -3
  14. package/dist/services/OperationsRepo.d.ts +4 -1
  15. package/dist/services/OperationsRepo.d.ts.map +1 -1
  16. package/dist/services/OperationsRepo.js +12 -3
  17. package/dist/services/QueueMaker/errors.d.ts +1 -1
  18. package/dist/services/QueueMaker/errors.d.ts.map +1 -1
  19. package/dist/services/Repository/dsl.d.ts.map +1 -1
  20. package/dist/services/Repository/dsl.js +1 -1
  21. package/dist/services/Repository/ext.d.ts +43 -104
  22. package/dist/services/Repository/ext.d.ts.map +1 -1
  23. package/dist/services/Repository/ext.js +59 -93
  24. package/dist/services/Repository/legacy.d.ts +139 -0
  25. package/dist/services/Repository/legacy.d.ts.map +1 -0
  26. package/dist/services/Repository/legacy.js +99 -0
  27. package/dist/services/Repository/service.d.ts +21 -2
  28. package/dist/services/Repository/service.d.ts.map +1 -1
  29. package/dist/services/Repository.d.ts +1 -0
  30. package/dist/services/Repository.d.ts.map +1 -1
  31. package/dist/services/Repository.js +2 -1
  32. package/dist/services/RepositoryBase.d.ts +7 -237
  33. package/dist/services/RepositoryBase.d.ts.map +1 -1
  34. package/dist/services/RepositoryBase.js +5 -200
  35. package/dist/services/query/new-kid-interpreter.d.ts +1 -1
  36. package/examples/query.ts +3 -2
  37. package/package.json +11 -1
  38. package/src/services/OperationsRepo.ts +13 -4
  39. package/src/services/Repository/dsl.ts +1 -0
  40. package/src/services/Repository/ext.ts +269 -250
  41. package/src/services/Repository/legacy.ts +351 -0
  42. package/src/services/Repository/service.ts +44 -2
  43. package/src/services/Repository.ts +1 -0
  44. package/src/services/RepositoryBase.ts +12 -931
  45. package/test/query.test.ts +10 -8
@@ -1,272 +1,291 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- // Update = Must return updated items
3
- // Modify = Must `set` updated items, and can return anything.
4
- import { Array, Effect } from "effect-app"
5
- import type { NonEmptyArray } from "effect-app"
6
- import { type FixEnv, runTerm } from "effect-app/Pure"
7
- import { NotFoundError } from "../../errors.js"
8
- import type { RepositoryBaseC } from "../RepositoryBase.js"
2
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
3
+ import type { NonEmptyArray, NonEmptyReadonlyArray } from "effect-app"
4
+ import { Array, Effect, Exit, Option, Request, RequestResolver } from "effect-app"
5
+ import type { InvalidStateError, OptimisticConcurrencyException } from "effect-app/client"
6
+ import { NotFoundError } from "effect-app/client"
7
+ import type { FixEnv, PureEnv } from "effect-app/Pure"
8
+ import { runTerm } from "effect-app/Pure"
9
+ import type { Query, QueryEnd, QueryWhere } from "../query.js"
10
+ import * as Q from "../query.js"
9
11
  import { AnyPureDSL } from "./dsl.js"
10
12
  import type { Repository } from "./service.js"
11
13
 
12
- /**
13
- * @tsplus fluent Repository get
14
- */
15
- export function get<
16
- T,
17
- Encoded extends { id: string },
18
- Evt,
19
- ItemType extends string,
20
- IdKey extends keyof T
21
- >(
22
- self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>,
23
- id: T[IdKey]
24
- ) {
25
- return self
26
- .find(id)
27
- .pipe(Effect.flatMap((_) => Effect.mapError(_, () => new NotFoundError<ItemType>({ type: self.itemType, id }))))
28
- }
14
+ export const extendRepo = <T, Encoded extends { id: string }, Evt, ItemType extends string, IdKey extends keyof T>(
15
+ repo: Repository<T, Encoded, Evt, ItemType, IdKey>
16
+ ) => {
17
+ const get = (id: T[IdKey]) =>
18
+ Effect.flatMap(
19
+ repo.find(id),
20
+ (_) => Effect.mapError(_, () => new NotFoundError<ItemType>({ type: repo.itemType, id }))
21
+ )
22
+ function saveManyWithPure_<
23
+ R,
24
+ T,
25
+ Encoded extends { id: string },
26
+ A,
27
+ E,
28
+ Evt,
29
+ S1 extends T,
30
+ S2 extends T,
31
+ ItemType extends string,
32
+ IdKey extends keyof T
33
+ >(
34
+ self: Repository<T, Encoded, Evt, ItemType, IdKey>,
35
+ items: Iterable<S1>,
36
+ pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>
37
+ ) {
38
+ return saveAllWithEffectInt(
39
+ self,
40
+ runTerm(pure, [...items])
41
+ )
42
+ }
29
43
 
30
- /**
31
- * @tsplus getter Repository log
32
- */
33
- export function log<
34
- T,
35
- Encoded extends { id: string },
36
- Evt,
37
- ItemType extends string,
38
- IdKey extends keyof T
39
- >(_: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>) {
40
- return (evt: Evt) => AnyPureDSL.log(evt)
41
- }
44
+ function saveWithPure_<
45
+ R,
46
+ T,
47
+ Encoded extends { id: string },
48
+ A,
49
+ E,
50
+ Evt,
51
+ S1 extends T,
52
+ S2 extends T,
53
+ ItemType extends string,
54
+ IdKey extends keyof T
55
+ >(
56
+ self: Repository<T, Encoded, Evt, ItemType, IdKey>,
57
+ item: S1,
58
+ pure: Effect<A, E, FixEnv<R, Evt, S1, S2>>
59
+ ) {
60
+ return saveAllWithEffectInt(
61
+ self,
62
+ runTerm(pure, item)
63
+ .pipe(Effect
64
+ .map(([item, events, a]) => [[item], events, a]))
65
+ )
66
+ }
42
67
 
43
- /**
44
- * @tsplus fluent Repository byIdAndSaveWithPure
45
- */
46
- export function byIdAndSaveWithPure<
47
- T,
48
- Encoded extends { id: string },
49
- Evt,
50
- ItemType extends string,
51
- IdKey extends keyof T
52
- >(self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>, id: T[IdKey]) {
53
- return <R, A, E, S2 extends T>(pure: Effect<A, E, FixEnv<R, Evt, T, S2>>) =>
54
- get(self, id).pipe(Effect.flatMap((item) => saveWithPure_(self, item, pure)))
55
- }
68
+ function saveAllWithEffectInt<
69
+ T,
70
+ Encoded extends { id: string },
71
+ P extends T,
72
+ Evt,
73
+ ItemType extends string,
74
+ IdKey extends keyof T,
75
+ R,
76
+ E,
77
+ A
78
+ >(
79
+ self: Repository<T, Encoded, Evt, ItemType, IdKey>,
80
+ gen: Effect<readonly [Iterable<P>, Iterable<Evt>, A], E, R>
81
+ ) {
82
+ return Effect.flatMap(gen, ([items, events, a]) => self.saveAndPublish(items, events).pipe(Effect.map(() => a)))
83
+ }
56
84
 
57
- /**
58
- * NOTE: it's not as composable, only useful when the request is simple, and only this part needs request args.
59
- * @tsplus getter Repository handleByIdAndSaveWithPure
60
- */
61
- export function handleByIdAndSaveWithPure<
62
- T,
63
- Encoded extends { id: string },
64
- Evt,
65
- ItemType extends string,
66
- IdKey extends keyof T
67
- >(self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>) {
68
- return <Req extends { id: T[IdKey] }, Context, R, A, E, S2 extends T>(
69
- pure: (req: Req, ctx: Context) => Effect<A, E, FixEnv<R, Evt, T, S2>>
70
- ) =>
71
- (req: Req, ctx: Context) => byIdAndSaveWithPure(self, req.id)(pure(req, ctx))
72
- }
85
+ function saveManyWithPureBatched_<
86
+ R,
87
+ T,
88
+ Encoded extends { id: string },
89
+ A,
90
+ E,
91
+ Evt,
92
+ S1 extends T,
93
+ S2 extends T,
94
+ ItemType extends string,
95
+ IdKey extends keyof T
96
+ >(
97
+ self: Repository<T, Encoded, Evt, ItemType, IdKey>,
98
+ items: Iterable<S1>,
99
+ pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>,
100
+ batchSize = 100
101
+ ) {
102
+ return Effect.forEach(
103
+ Array.chunk_(items, batchSize),
104
+ (batch) =>
105
+ saveAllWithEffectInt(
106
+ self,
107
+ runTerm(pure, batch)
108
+ )
109
+ )
110
+ }
73
111
 
74
- /**
75
- * @tsplus fluent Repository saveManyWithPure_
76
- */
77
- export function saveManyWithPure_<
78
- R,
79
- T,
80
- Encoded extends { id: string },
81
- A,
82
- E,
83
- Evt,
84
- S1 extends T,
85
- S2 extends T,
86
- ItemType extends string,
87
- IdKey extends keyof T
88
- >(
89
- self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>,
90
- items: Iterable<S1>,
91
- pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>
92
- ) {
93
- return saveAllWithEffectInt(
94
- self,
95
- runTerm(pure, [...items])
96
- )
97
- }
112
+ const queryAndSavePure: {
113
+ <A, E2, R2, T2 extends T>(
114
+ q: (
115
+ q: Query<Encoded>
116
+ ) => QueryEnd<Encoded, "one">,
117
+ pure: Effect<A, E2, FixEnv<R2, Evt, T, T2>>
118
+ ): Effect.Effect<
119
+ A,
120
+ InvalidStateError | OptimisticConcurrencyException | NotFoundError<ItemType> | E2,
121
+ Exclude<R2, {
122
+ env: PureEnv<Evt, T, T2>
123
+ }>
124
+ >
125
+ <A, E2, R2, T2 extends T>(
126
+ q: (
127
+ q: Query<Encoded>
128
+ ) =>
129
+ | Query<Encoded>
130
+ | QueryWhere<Encoded>
131
+ | QueryEnd<Encoded, "many">,
132
+ pure: Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>
133
+ ): Effect.Effect<
134
+ A,
135
+ InvalidStateError | OptimisticConcurrencyException | E2,
136
+ Exclude<R2, {
137
+ env: PureEnv<Evt, readonly T[], readonly T2[]>
138
+ }>
139
+ >
140
+ <A, E2, R2, T2 extends T>(
141
+ q: (
142
+ q: Query<Encoded>
143
+ ) =>
144
+ | Query<Encoded>
145
+ | QueryWhere<Encoded>
146
+ | QueryEnd<Encoded, "many">,
147
+ pure: Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>,
148
+ batch: "batched" | number
149
+ ): Effect.Effect<
150
+ A[],
151
+ InvalidStateError | OptimisticConcurrencyException | E2,
152
+ Exclude<R2, {
153
+ env: PureEnv<Evt, readonly T[], readonly T2[]>
154
+ }>
155
+ >
156
+ } = (q, pure, batch?: "batched" | number) =>
157
+ repo.query(q).pipe(
158
+ Effect.andThen((_) =>
159
+ Array.isArray(_)
160
+ ? batch === undefined
161
+ ? saveManyWithPure_(repo, _ as any, pure as any)
162
+ : saveManyWithPureBatched_(repo, _ as any, pure as any, batch === "batched" ? 100 : batch)
163
+ : saveWithPure_(repo, _ as any, pure as any)
164
+ )
165
+ ) as any
98
166
 
99
- /**
100
- * @tsplus fluent Repository saveWithPure_
101
- */
102
- export function saveWithPure_<
103
- R,
104
- T,
105
- Encoded extends { id: string },
106
- A,
107
- E,
108
- Evt,
109
- S1 extends T,
110
- S2 extends T,
111
- ItemType extends string,
112
- IdKey extends keyof T
113
- >(
114
- self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>,
115
- item: S1,
116
- pure: Effect<A, E, FixEnv<R, Evt, S1, S2>>
117
- ) {
118
- return saveAllWithEffectInt(
119
- self,
120
- runTerm(pure, item)
121
- .pipe(Effect
122
- .map(([item, events, a]) => [[item], events, a]))
123
- )
124
- }
167
+ const saveManyWithPure: {
168
+ <R, A, E, S1 extends T, S2 extends T>(
169
+ items: Iterable<S1>,
170
+ pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>
171
+ ): Effect.Effect<
172
+ A,
173
+ InvalidStateError | OptimisticConcurrencyException | E,
174
+ Exclude<R, {
175
+ env: PureEnv<Evt, readonly S1[], readonly S2[]>
176
+ }>
177
+ >
178
+ <R, A, E, S1 extends T, S2 extends T>(
179
+ items: Iterable<S1>,
180
+ pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>,
181
+ batch: "batched" | number
182
+ ): Effect.Effect<
183
+ A[],
184
+ InvalidStateError | OptimisticConcurrencyException | E,
185
+ Exclude<R, {
186
+ env: PureEnv<Evt, readonly S1[], readonly S2[]>
187
+ }>
188
+ >
189
+ } = (items, pure, batch?: "batched" | number) =>
190
+ batch
191
+ ? Effect.forEach(
192
+ Array.chunk_(items, batch === "batched" ? 100 : batch),
193
+ (batch) =>
194
+ saveAllWithEffectInt(
195
+ repo,
196
+ runTerm(pure, batch)
197
+ )
198
+ )
199
+ : saveAllWithEffectInt(
200
+ repo,
201
+ runTerm(pure, [...items])
202
+ )
125
203
 
126
- export function saveAllWithEffectInt<
127
- T,
128
- Encoded extends { id: string },
129
- P extends T,
130
- Evt,
131
- ItemType extends string,
132
- IdKey extends keyof T,
133
- R,
134
- E,
135
- A
136
- >(
137
- self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>,
138
- gen: Effect<readonly [Iterable<P>, Iterable<Evt>, A], E, R>
139
- ) {
140
- return Effect.flatMap(gen, ([items, events, a]) => self.saveAndPublish(items, events).pipe(Effect.map(() => a)))
141
- }
204
+ const byIdAndSaveWithPure: {
205
+ <R, A, E, S2 extends T>(
206
+ id: T[IdKey],
207
+ pure: Effect<A, E, FixEnv<R, Evt, T, S2>>
208
+ ): Effect<
209
+ A,
210
+ InvalidStateError | OptimisticConcurrencyException | NotFoundError<ItemType> | E,
211
+ Exclude<R, {
212
+ env: PureEnv<Evt, T, S2>
213
+ }>
214
+ >
215
+ } = (id, pure): any => get(id).pipe(Effect.flatMap((item) => saveWithPure_(repo, item, pure)))
142
216
 
143
- /**
144
- * @tsplus fluent Repository saveManyWithPureBatched
145
- */
146
- export function saveManyWithPureBatched<
147
- T,
148
- Encoded extends { id: string },
149
- Evt,
150
- ItemType extends string,
151
- IdKey extends keyof T
152
- >(self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>, batchSize = 100) {
153
- return <R, A, E, S1 extends T, S2 extends T>(pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>) =>
154
- (items: Iterable<S1>) => saveManyWithPureBatched_(self, items, pure, batchSize)
155
- }
217
+ type Req =
218
+ & Request.Request<T, NotFoundError<ItemType>>
219
+ & { _tag: `Get${ItemType}`; id: T[IdKey] }
220
+ const _request = Request.tagged<Req>(`Get${repo.itemType}`)
156
221
 
157
- /**
158
- * @tsplus fluent Repository saveManyWithPureBatched_
159
- */
160
- export function saveManyWithPureBatched_<
161
- R,
162
- T,
163
- Encoded extends { id: string },
164
- A,
165
- E,
166
- Evt,
167
- S1 extends T,
168
- S2 extends T,
169
- ItemType extends string,
170
- IdKey extends keyof T
171
- >(
172
- self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>,
173
- items: Iterable<S1>,
174
- pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>,
175
- batchSize = 100
176
- ) {
177
- return Effect.forEach(
178
- Array.chunk_(items, batchSize),
179
- (batch) =>
222
+ const requestResolver = RequestResolver
223
+ .makeBatched((
224
+ requests: NonEmptyReadonlyArray<Req>
225
+ ) =>
226
+ (repo.query(Q.where("id", "in", requests.map((_) => _.id)) as any) as Effect<readonly T[], never>)
227
+ // TODO
228
+ .pipe(
229
+ Effect.andThen((items) =>
230
+ Effect.forEach(requests, (r) =>
231
+ Request.complete(
232
+ r,
233
+ Array
234
+ .findFirst(items, (_) => _[repo.idKey] === r.id)
235
+ .pipe(Option.match({
236
+ onNone: () => Exit.fail(new NotFoundError({ type: repo.itemType, id: r.id })),
237
+ onSome: Exit.succeed
238
+ }))
239
+ ), { discard: true })
240
+ ),
241
+ Effect
242
+ .catchAllCause((cause) =>
243
+ Effect.forEach(requests, Request.complete(Exit.failCause(cause)), { discard: true })
244
+ )
245
+ )
246
+ )
247
+ .pipe(
248
+ RequestResolver.batchN(20)
249
+ )
250
+
251
+ const exts = {
252
+ request: (id: T[IdKey]) => Effect.request(_request({ id }), requestResolver),
253
+ get,
254
+ log: (evt: Evt) => AnyPureDSL.log(evt),
255
+ removeById: (id: T[IdKey]) => Effect.andThen(get(id), (_) => repo.removeAndPublish([_])),
256
+ save: (...items: NonEmptyArray<T>) => repo.saveAndPublish(items),
257
+ saveWithEvents: (events: Iterable<Evt>) => (...items: NonEmptyArray<T>) => repo.saveAndPublish(items, events),
258
+ queryAndSavePure,
259
+ saveManyWithPure,
260
+ byIdAndSaveWithPure,
261
+ saveWithPure: <
262
+ R,
263
+ A,
264
+ E,
265
+ S1 extends T,
266
+ S2 extends T
267
+ >(
268
+ item: S1,
269
+ pure: Effect<A, E, FixEnv<R, Evt, S1, S2>>
270
+ ) =>
180
271
  saveAllWithEffectInt(
181
- self,
182
- runTerm(pure, batch)
272
+ repo,
273
+ runTerm(pure, item)
274
+ .pipe(Effect.map(([item, events, a]) => [[item], events, a]))
183
275
  )
184
- )
185
- }
276
+ }
186
277
 
187
- /**
188
- * @tsplus getter Repository save
189
- */
190
- export function save<
191
- T,
192
- Encoded extends { id: string },
193
- Evt,
194
- ItemType extends string,
195
- IdKey extends keyof T
196
- >(
197
- self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>
198
- ) {
199
- return (...items: NonEmptyArray<T>) => self.saveAndPublish(items)
278
+ return {
279
+ ...repo,
280
+ ...exts
281
+ } as Repository<T, Encoded, Evt, ItemType, IdKey> & typeof exts
200
282
  }
201
283
 
202
- /**
203
- * @tsplus getter Repository saveWithEvents
204
- */
205
- export function saveWithEvents<
284
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
285
+ export interface ExtendedRepository<
206
286
  T,
207
287
  Encoded extends { id: string },
208
288
  Evt,
209
289
  ItemType extends string,
210
290
  IdKey extends keyof T
211
- >(
212
- self: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>
213
- ) {
214
- return (events: Iterable<Evt>) => (...items: NonEmptyArray<T>) => self.saveAndPublish(items, events)
215
- }
216
-
217
- /**
218
- * @tsplus fluent Repository updateWithEffect
219
- */
220
- export function itemUpdateWithEffect<
221
- R,
222
- E,
223
- T extends { id: string },
224
- Encoded extends { id: string },
225
- Evt,
226
- ItemType extends string,
227
- IdKey extends keyof T
228
- >(
229
- repo: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>,
230
- id: T[IdKey],
231
- mod: (item: T) => Effect<T, E, R>
232
- ) {
233
- return get(repo, id).pipe(Effect.andThen(mod), Effect.andThen(save(repo)))
234
- }
235
-
236
- /**
237
- * @tsplus fluent Repository update
238
- */
239
- export function itemUpdate<
240
- T extends { id: string },
241
- Encoded extends { id: string },
242
- Evt,
243
- ItemType extends string,
244
- IdKey extends keyof T
245
- >(
246
- repo: RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey>,
247
- id: T[IdKey],
248
- mod: (item: T) => T
249
- ) {
250
- return itemUpdateWithEffect(
251
- repo,
252
- id,
253
- (item) => Effect.sync(() => mod(item))
254
- )
255
- }
256
-
257
- /**
258
- * only use this as a shortcut if you don't have the item already
259
- * @tsplus fluent Repository removeById
260
- */
261
- export function removeById<
262
- T,
263
- Encoded extends { id: string },
264
- Evt,
265
- ItemType extends string,
266
- IdKey extends keyof T
267
- >(
268
- self: Repository<T, Encoded, Evt, ItemType, IdKey>,
269
- id: T[IdKey]
270
- ) {
271
- return get(self, id).pipe(Effect.flatMap((_) => self.removeAndPublish([_])))
272
- }
291
+ > extends ReturnType<typeof extendRepo<T, Encoded, Evt, ItemType, IdKey>> {}