@effect-app/infra 0.170.5 → 0.171.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 (90) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/_cjs/api/routing/base.cjs.map +1 -1
  3. package/_cjs/api/routing/makeRequestHandler.cjs.map +1 -1
  4. package/_cjs/services/QueueMaker/errors.cjs.map +1 -1
  5. package/_cjs/services/Repository/dsl.cjs +70 -0
  6. package/_cjs/services/Repository/dsl.cjs.map +1 -0
  7. package/_cjs/services/Repository/ext.cjs +202 -0
  8. package/_cjs/services/Repository/ext.cjs.map +1 -0
  9. package/_cjs/services/Repository/service.cjs +6 -0
  10. package/_cjs/services/Repository/service.cjs.map +1 -0
  11. package/_cjs/services/Repository.cjs +32 -252
  12. package/_cjs/services/Repository.cjs.map +1 -1
  13. package/_cjs/services/RepositoryBase.cjs +173 -48
  14. package/_cjs/services/RepositoryBase.cjs.map +1 -1
  15. package/_cjs/services/RequestContextContainer.cjs +6 -7
  16. package/_cjs/services/RequestContextContainer.cjs.map +1 -1
  17. package/_cjs/services/Store/ContextMapContainer.cjs +6 -7
  18. package/_cjs/services/Store/ContextMapContainer.cjs.map +1 -1
  19. package/_cjs/services/Store/Memory.cjs.map +1 -1
  20. package/_cjs/services/Store/utils.cjs.map +1 -1
  21. package/_src/api/routing/base.ts +5 -3
  22. package/_src/api/routing/makeRequestHandler.ts +20 -12
  23. package/_src/rateLimit.ts +2 -2
  24. package/_src/services/Emailer/Sendgrid.ts +2 -2
  25. package/_src/services/QueueMaker/errors.ts +6 -3
  26. package/_src/services/QueueMaker/memQueue.ts +2 -2
  27. package/_src/services/QueueMaker/sbqueue.ts +2 -2
  28. package/_src/services/Repository/dsl.ts +137 -0
  29. package/_src/services/Repository/ext.ts +669 -0
  30. package/_src/services/Repository/service.ts +12 -0
  31. package/_src/services/Repository.ts +3 -791
  32. package/_src/services/RepositoryBase.ts +640 -13
  33. package/_src/services/RequestContextContainer.ts +14 -16
  34. package/_src/services/Store/ContextMapContainer.ts +6 -9
  35. package/_src/services/Store/Cosmos.ts +2 -2
  36. package/_src/services/Store/Disk.ts +2 -2
  37. package/_src/services/Store/Memory.ts +2 -1
  38. package/_src/services/Store/Redis.ts +2 -2
  39. package/_src/services/Store/utils.ts +4 -5
  40. package/_src/services/simpledb/cosmosdb.ts +1 -1
  41. package/_src/services/simpledb/diskdb.ts +1 -1
  42. package/_src/services/simpledb/memdb.ts +1 -1
  43. package/_src/services/simpledb/mongodb.ts +1 -1
  44. package/_src/services/simpledb/redisdb.ts +2 -2
  45. package/dist/api/routing/base.d.ts +1 -2
  46. package/dist/api/routing/base.d.ts.map +1 -1
  47. package/dist/api/routing/base.js +1 -1
  48. package/dist/api/routing/makeRequestHandler.d.ts.map +1 -1
  49. package/dist/api/routing/makeRequestHandler.js +15 -11
  50. package/dist/rateLimit.js +1 -1
  51. package/dist/services/Emailer/Sendgrid.js +1 -1
  52. package/dist/services/QueueMaker/errors.d.ts.map +1 -1
  53. package/dist/services/QueueMaker/errors.js +5 -2
  54. package/dist/services/QueueMaker/memQueue.js +1 -1
  55. package/dist/services/QueueMaker/sbqueue.js +1 -1
  56. package/dist/services/QueueMaker/service.d.ts +2 -2
  57. package/dist/services/Repository/dsl.d.ts +51 -0
  58. package/dist/services/Repository/dsl.d.ts.map +1 -0
  59. package/dist/services/Repository/dsl.js +55 -0
  60. package/dist/services/Repository/ext.d.ts +310 -0
  61. package/dist/services/Repository/ext.d.ts.map +1 -0
  62. package/dist/services/Repository/ext.js +166 -0
  63. package/dist/services/Repository/service.d.ts +10 -0
  64. package/dist/services/Repository/service.d.ts.map +1 -0
  65. package/dist/services/Repository/service.js +2 -0
  66. package/dist/services/Repository.d.ts +3 -363
  67. package/dist/services/Repository.d.ts.map +1 -1
  68. package/dist/services/Repository.js +4 -217
  69. package/dist/services/RepositoryBase.d.ts +241 -6
  70. package/dist/services/RepositoryBase.d.ts.map +1 -1
  71. package/dist/services/RepositoryBase.js +176 -52
  72. package/dist/services/RequestContextContainer.d.ts +3 -5
  73. package/dist/services/RequestContextContainer.d.ts.map +1 -1
  74. package/dist/services/RequestContextContainer.js +17 -16
  75. package/dist/services/Store/ContextMapContainer.d.ts +2 -5
  76. package/dist/services/Store/ContextMapContainer.d.ts.map +1 -1
  77. package/dist/services/Store/ContextMapContainer.js +7 -8
  78. package/dist/services/Store/Cosmos.js +1 -1
  79. package/dist/services/Store/Disk.js +1 -1
  80. package/dist/services/Store/Memory.d.ts.map +1 -1
  81. package/dist/services/Store/Memory.js +1 -1
  82. package/dist/services/Store/Redis.js +1 -1
  83. package/dist/services/Store/utils.d.ts.map +1 -1
  84. package/dist/services/Store/utils.js +1 -1
  85. package/dist/services/simpledb/cosmosdb.js +1 -1
  86. package/dist/services/simpledb/diskdb.js +1 -1
  87. package/dist/services/simpledb/memdb.js +1 -1
  88. package/dist/services/simpledb/mongodb.js +1 -1
  89. package/dist/services/simpledb/redisdb.js +1 -1
  90. package/package.json +37 -7
@@ -1,7 +1,19 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  /* eslint-disable @typescript-eslint/ban-types */
3
3
  // import type { ParserEnv } from "@effect-app/schema/custom/Parser"
4
- import type { Repository } from "./Repository.js"
4
+ import {
5
+ AnyPureDSL,
6
+ byIdAndSaveWithPure,
7
+ get,
8
+ queryAndSaveOnePureEffect,
9
+ queryEffect,
10
+ queryOneEffect,
11
+ type Repository,
12
+ saveAllWithEffectInt,
13
+ saveManyWithPure_,
14
+ saveManyWithPureBatched_,
15
+ saveWithPure_
16
+ } from "./Repository.js"
5
17
  import { StoreMaker } from "./Store.js"
6
18
  import type { Filter, FilterArgs, FilterFunc, PersistenceModelType, StoreConfig, Where } from "./Store.js"
7
19
  import type {} from "effect/Equal"
@@ -9,7 +21,8 @@ import type {} from "effect/Hash"
9
21
  import type { Opt } from "@effect-app/core/Option"
10
22
  import { makeFilters } from "@effect-app/infra/filter"
11
23
  import { S } from "@effect-app/prelude"
12
- import type { InvalidStateError, OptimisticConcurrencyException } from "../errors.js"
24
+ import type { FixEnv } from "@effect-app/prelude/Pure"
25
+ import { type InvalidStateError, NotFoundError, type OptimisticConcurrencyException } from "../errors.js"
13
26
  import { ContextMapContainer } from "./Store/ContextMapContainer.js"
14
27
  import { QueryBuilder } from "./Store/filterApi/query.js"
15
28
 
@@ -76,6 +89,610 @@ export class RepositoryBaseC2<
76
89
  override changeFeed = this.impl.changeFeed
77
90
  }
78
91
 
92
+ export class RepositoryBaseC3<
93
+ T extends { id: unknown },
94
+ PM extends PersistenceModelType<string>,
95
+ Evt,
96
+ ItemType extends string
97
+ > extends RepositoryBaseC2<T, PM, Evt, ItemType> {
98
+ get<
99
+ T extends { id: unknown },
100
+ PM extends PersistenceModelType<string>,
101
+ Evt,
102
+ ItemType extends string
103
+ >(
104
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
105
+ id: T["id"]
106
+ ) {
107
+ return this
108
+ .find(id)
109
+ .flatMap((_) => _.encaseInEffect(() => new NotFoundError<ItemType>({ type: this.itemType, id })))
110
+ }
111
+
112
+ readonly log = (evt: Evt) => AnyPureDSL.log(evt)
113
+
114
+ // TODO: project inside the db.
115
+ projectEffect<
116
+ T extends { id: unknown },
117
+ PM extends PersistenceModelType<string>,
118
+ Evt,
119
+ ItemType extends string,
120
+ R,
121
+ E,
122
+ S = PM
123
+ >(
124
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
125
+ map: Effect<
126
+ {
127
+ filter?: Filter<PM> | undefined
128
+ collect?: ((t: PM) => Option<S>) | undefined
129
+ limit?: number | undefined
130
+ skip?: number | undefined
131
+ },
132
+ E,
133
+ R
134
+ >
135
+ ): Effect<S[], E, R>
136
+ projectEffect<
137
+ T extends { id: unknown },
138
+ PM extends PersistenceModelType<string>,
139
+ Evt,
140
+ ItemType extends string,
141
+ R,
142
+ E,
143
+ U extends keyof PM,
144
+ S = PM
145
+ >(
146
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
147
+ map: Effect<
148
+ {
149
+ filter?: QueryBuilder<PM> | undefined
150
+ collect?: ((t: PM) => Option<S>) | undefined
151
+ limit?: number | undefined
152
+ skip?: number | undefined
153
+ },
154
+ E,
155
+ R
156
+ >
157
+ ): Effect<Pick<PM, U>[], E, R>
158
+ projectEffect<
159
+ T extends { id: unknown },
160
+ PM extends PersistenceModelType<string>,
161
+ Evt,
162
+ ItemType extends string,
163
+ R,
164
+ E,
165
+ U extends keyof PM,
166
+ S = Pick<PM, U>
167
+ >(
168
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
169
+ map: Effect<
170
+ {
171
+ filter?: Filter<PM> | undefined
172
+ select?: NonEmptyReadonlyArray<U> | undefined
173
+ collect?: ((t: PM) => Option<S>) | undefined
174
+ limit?: number | undefined
175
+ skip?: number | undefined
176
+ },
177
+ E,
178
+ R
179
+ >
180
+ ): Effect<S[], E, R> {
181
+ // TODO: a projection that gets sent to the db instead.
182
+ return map.flatMap((f) =>
183
+ this
184
+ .utils
185
+ .filter(f)
186
+ .map((_) => f.collect ? _.filterMap(f.collect) : _ as unknown as S[])
187
+ )
188
+ }
189
+
190
+ /**
191
+ * TODO: project inside the db.
192
+ * @tsplus fluent Repository project
193
+ */
194
+ project<
195
+ T extends { id: unknown },
196
+ PM extends PersistenceModelType<string>,
197
+ Evt,
198
+ ItemType extends string,
199
+ S = PM
200
+ >(
201
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
202
+ map: {
203
+ filter?: Filter<PM>
204
+ collect?: (t: PM) => Option<S>
205
+ limit?: number
206
+ skip?: number
207
+ }
208
+ ): Effect<S[]>
209
+ project<
210
+ T extends { id: unknown },
211
+ PM extends PersistenceModelType<string>,
212
+ Evt,
213
+ ItemType extends string,
214
+ U extends keyof PM
215
+ >(
216
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
217
+ map: {
218
+ filter?: QueryBuilder<PM>
219
+ select: NonEmptyReadonlyArray<U>
220
+ limit?: number
221
+ skip?: number
222
+ }
223
+ ): Effect<Pick<PM, U>[]>
224
+ project<
225
+ T extends { id: unknown },
226
+ PM extends PersistenceModelType<string>,
227
+ Evt,
228
+ ItemType extends string,
229
+ U extends keyof PM,
230
+ S = Pick<PM, U>
231
+ >(
232
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
233
+ map: {
234
+ filter?: Filter<PM>
235
+ select?: NonEmptyReadonlyArray<U>
236
+ collect?: (t: Pick<PM, U>) => Option<S>
237
+ limit?: number
238
+ skip?: number
239
+ }
240
+ ): Effect<S[]> {
241
+ return this.projectEffect(Effect.sync(() => map))
242
+ }
243
+
244
+ /**
245
+ * TODO: count inside the db.
246
+ * @tsplus fluent Repository project
247
+ */
248
+ count<
249
+ T extends { id: unknown },
250
+ PM extends PersistenceModelType<string>,
251
+ Evt,
252
+ ItemType extends string
253
+ >(
254
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
255
+ filter?: Filter<PM>
256
+ ) {
257
+ return this
258
+ .projectEffect(Effect.sync(() => ({ filter })))
259
+ .map((_) => NonNegativeInt(_.length))
260
+ }
261
+
262
+ /**
263
+ * @tsplus fluent Repository queryEffect
264
+ */
265
+ queryEffect<
266
+ T extends { id: unknown },
267
+ PM extends PersistenceModelType<string>,
268
+ Evt,
269
+ ItemType extends string,
270
+ R,
271
+ E,
272
+ S = T
273
+ >(
274
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
275
+ // TODO: think about collectPM, collectE, and collect(Parsed)
276
+ map: Effect<{ filter?: Filter<PM>; collect?: (t: T) => Option<S>; limit?: number; skip?: number }, E, R>
277
+ ) {
278
+ return map.flatMap((f) =>
279
+ (f.filter ? this.utils.filter(f) : this.utils.all)
280
+ .flatMap((_) => this.utils.parseMany(_))
281
+ .map((_) => f.collect ? _.filterMap(f.collect) : _ as any as S[])
282
+ )
283
+ }
284
+
285
+ /**
286
+ * @tsplus fluent Repository queryOneEffect
287
+ */
288
+ queryOneEffect<
289
+ T extends { id: unknown },
290
+ PM extends PersistenceModelType<string>,
291
+ Evt,
292
+ ItemType extends string,
293
+ R,
294
+ E
295
+ >(
296
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
297
+ // TODO: think about collectPM, collectE, and collect(Parsed)
298
+ map: Effect<{ filter?: Filter<PM> }, E, R>
299
+ ): Effect<T, E | NotFoundError<ItemType>, R>
300
+ queryOneEffect<
301
+ T extends { id: unknown },
302
+ PM extends PersistenceModelType<string>,
303
+ Evt,
304
+ ItemType extends string,
305
+ R,
306
+ E,
307
+ S = T
308
+ >(
309
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
310
+ // TODO: think about collectPM, collectE, and collect(Parsed)
311
+ map: Effect<{ filter?: Filter<PM>; collect: (t: T) => Option<S> }, E, R>
312
+ ): Effect<S, E | NotFoundError<ItemType>, R>
313
+ queryOneEffect<
314
+ T extends { id: unknown },
315
+ PM extends PersistenceModelType<string>,
316
+ Evt,
317
+ ItemType extends string,
318
+ R,
319
+ E,
320
+ S = T
321
+ >(
322
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
323
+ // TODO: think about collectPM, collectE, and collect(Parsed)
324
+ map: Effect<{ filter?: Filter<PM>; collect?: (t: T) => Option<S> }, E, R>
325
+ ): Effect<S, E | NotFoundError<ItemType>, R> {
326
+ return map.flatMap((f) =>
327
+ (f.filter ? this.utils.filter({ filter: f.filter, limit: 1 }) : this.utils.all)
328
+ .flatMap((_) => this.utils.parseMany(_))
329
+ .flatMap((_) =>
330
+ (f.collect ? _.filterMap(f.collect) : _ as any as S[])
331
+ .toNonEmpty
332
+ .encaseInEffect(() => new NotFoundError<ItemType>({ type: this.itemType, id: f.filter }))
333
+ .map((_) => _[0])
334
+ )
335
+ )
336
+ }
337
+
338
+ /**
339
+ * @tsplus fluent Repository query
340
+ */
341
+ query<
342
+ T extends { id: unknown },
343
+ PM extends PersistenceModelType<string>,
344
+ Evt,
345
+ ItemType extends string,
346
+ S = T
347
+ >(
348
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
349
+ // TODO: think about collectPM, collectE, and collect(Parsed)
350
+ map: { filter?: Filter<PM>; collect?: (t: T) => Option<S>; limit?: number; skip?: number }
351
+ ) {
352
+ return this.queryEffect(Effect.sync(() => map))
353
+ }
354
+
355
+ /**
356
+ * @tsplus fluent Repository queryOne
357
+ */
358
+ queryOne<
359
+ T extends { id: unknown },
360
+ PM extends PersistenceModelType<string>,
361
+ Evt,
362
+ ItemType extends string,
363
+ S = T
364
+ >(
365
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
366
+ map: { filter?: Filter<PM>; collect: (t: T) => Option<S> }
367
+ ): Effect<S, NotFoundError<ItemType>>
368
+ queryOne<
369
+ T extends { id: unknown },
370
+ PM extends PersistenceModelType<string>,
371
+ Evt,
372
+ ItemType extends string
373
+ >(
374
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
375
+ map: { filter?: Filter<PM> }
376
+ ): Effect<T, NotFoundError<ItemType>>
377
+ queryOne<
378
+ T extends { id: unknown },
379
+ PM extends PersistenceModelType<string>,
380
+ Evt,
381
+ ItemType extends string,
382
+ S = T
383
+ >(
384
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
385
+ map: { filter?: Filter<PM>; collect?: (t: T) => Option<S> }
386
+ ) {
387
+ return this.queryOneEffect(Effect.sync(() => map))
388
+ }
389
+
390
+ /**
391
+ * @tsplus fluent Repository queryAndSaveOnePureEffect
392
+ */
393
+ queryAndSaveOnePureEffect<
394
+ T extends { id: unknown },
395
+ PM extends PersistenceModelType<string>,
396
+ Evt,
397
+ ItemType extends string,
398
+ R,
399
+ E,
400
+ S extends T = T
401
+ >(
402
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
403
+ map: Effect<{ filter: Filter<PM>; collect: (t: T) => Option<S>; limit?: number; skip?: number }, E, R>
404
+ ): <R2, A, E2, S2 extends T>(
405
+ pure: Effect<A, E2, FixEnv<R2, Evt, S, S2>>
406
+ ) => Effect<
407
+ A,
408
+ InvalidStateError | OptimisticConcurrencyException | E | E2 | NotFoundError<ItemType>,
409
+ | R
410
+ | Exclude<R2, {
411
+ env: PureEnv<Evt, S, S2>
412
+ }>
413
+ >
414
+ queryAndSaveOnePureEffect<
415
+ T extends { id: unknown },
416
+ PM extends PersistenceModelType<string>,
417
+ Evt,
418
+ ItemType extends string,
419
+ R,
420
+ E
421
+ >(
422
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
423
+ map: Effect<{ filter: Filter<PM> }, E, R>
424
+ ): <R2, A, E2, T2 extends T>(
425
+ pure: Effect<A, E2, FixEnv<R2, Evt, T, T2>>
426
+ ) => Effect<
427
+ A,
428
+ InvalidStateError | OptimisticConcurrencyException | E | E2 | NotFoundError<ItemType>,
429
+ | R
430
+ | Exclude<R2, {
431
+ env: PureEnv<Evt, T, T2>
432
+ }>
433
+ >
434
+ queryAndSaveOnePureEffect(
435
+ this: any,
436
+ map: any
437
+ ) {
438
+ return (pure: any) =>
439
+ queryOneEffect(this, map)
440
+ .flatMap((_) => saveWithPure_(this, _, pure))
441
+ }
442
+
443
+ /**
444
+ * @tsplus fluent Repository queryAndSaveOnePure
445
+ */
446
+ queryAndSaveOnePure<
447
+ T extends { id: unknown },
448
+ PM extends PersistenceModelType<string>,
449
+ Evt,
450
+ ItemType extends string,
451
+ S extends T = T
452
+ >(
453
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
454
+ map: { filter: Filter<PM>; collect: (t: T) => Option<S>; limit?: number; skip?: number }
455
+ ): <R2, A, E2, S2>(pure: Effect<A, E2, FixEnv<R2, Evt, S, S2>>) => Effect<
456
+ A,
457
+ InvalidStateError | OptimisticConcurrencyException | E2 | NotFoundError<ItemType>,
458
+ Exclude<R2, {
459
+ env: PureEnv<Evt, S, S2>
460
+ }>
461
+ >
462
+ queryAndSaveOnePure<
463
+ T extends { id: unknown },
464
+ PM extends PersistenceModelType<string>,
465
+ Evt,
466
+ ItemType extends string
467
+ >(
468
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
469
+ map: { filter: Filter<PM> }
470
+ ): <R2, A, E2, T2>(pure: Effect<A, E2, FixEnv<R2, Evt, T, T2>>) => Effect<
471
+ A,
472
+ InvalidStateError | OptimisticConcurrencyException | E2 | NotFoundError<ItemType>,
473
+ Exclude<R2, {
474
+ env: PureEnv<Evt, T, T2>
475
+ }>
476
+ >
477
+ queryAndSaveOnePure(
478
+ this: any,
479
+ map: { filter: Filter<any>; collect?: any }
480
+ ) {
481
+ return queryAndSaveOnePureEffect(this, Effect.sync(() => map))
482
+ }
483
+
484
+ /**
485
+ * @tsplus fluent Repository queryAndSavePureEffect
486
+ */
487
+ queryAndSavePureEffect<
488
+ T extends { id: unknown },
489
+ PM extends PersistenceModelType<string>,
490
+ Evt,
491
+ ItemType extends string,
492
+ R,
493
+ E,
494
+ S extends T = T
495
+ >(
496
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
497
+ map: Effect<{ filter: Filter<PM>; collect?: (t: T) => Option<S>; limit?: number; skip?: number }, E, R>
498
+ ) {
499
+ return <R2, A, E2, S2 extends T>(pure: Effect<A, E2, FixEnv<R2, Evt, S[], S2[]>>) =>
500
+ queryEffect(this, map)
501
+ .flatMap((_) => this.saveManyWithPure_(_, pure))
502
+ }
503
+
504
+ /**
505
+ * @tsplus fluent Repository queryAndSavePure
506
+ */
507
+ queryAndSavePure<
508
+ T extends { id: unknown },
509
+ PM extends PersistenceModelType<string>,
510
+ Evt,
511
+ ItemType extends string,
512
+ S extends T = T
513
+ >(
514
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
515
+ map: { filter: Filter<PM>; collect?: (t: T) => Option<S>; limit?: number; skip?: number }
516
+ ) {
517
+ return this.queryAndSavePureEffect(Effect.sync(() => map))
518
+ }
519
+
520
+ saveManyWithPure<
521
+ T extends { id: unknown },
522
+ PM extends PersistenceModelType<string>,
523
+ Evt,
524
+ ItemType extends string
525
+ >(this: RepositoryBaseC<T, PM, Evt, ItemType>) {
526
+ return <R, A, E, S1 extends T, S2 extends T>(pure: Effect<A, E, FixEnv<R, Evt, S1[], S2[]>>) =>
527
+ (items: Iterable<S1>) => saveManyWithPure_(this, items, pure)
528
+ }
529
+
530
+ byIdAndSaveWithPure<
531
+ T extends { id: unknown },
532
+ PM extends PersistenceModelType<string>,
533
+ Evt,
534
+ ItemType extends string
535
+ >(this: RepositoryBaseC<T, PM, Evt, ItemType>, id: T["id"]) {
536
+ return <R, A, E, S2 extends T>(pure: Effect<A, E, FixEnv<R, Evt, T, S2>>) =>
537
+ get(this, id).flatMap((item) => saveWithPure_(this, item, pure))
538
+ }
539
+
540
+ /**
541
+ * NOTE: it's not as composable, only useful when the request is simple, and only this part needs request args.
542
+ */
543
+ handleByIdAndSaveWithPure<
544
+ T extends { id: unknown },
545
+ PM extends PersistenceModelType<string>,
546
+ Evt,
547
+ ItemType extends string
548
+ >(this: RepositoryBaseC<T, PM, Evt, ItemType>) {
549
+ return <Req extends { id: T["id"] }, Context, R, A, E, S2 extends T>(
550
+ pure: (req: Req, ctx: Context) => Effect<A, E, FixEnv<R, Evt, T, S2>>
551
+ ) =>
552
+ (req: Req, ctx: Context) => byIdAndSaveWithPure(this, req.id)(pure(req, ctx))
553
+ }
554
+
555
+ saveManyWithPure_<
556
+ R,
557
+ T extends { id: unknown },
558
+ PM extends PersistenceModelType<string>,
559
+ A,
560
+ E,
561
+ Evt,
562
+ S1 extends T,
563
+ S2 extends T,
564
+ ItemType extends string
565
+ >(
566
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
567
+ items: Iterable<S1>,
568
+ pure: Effect<A, E, FixEnv<R, Evt, S1[], S2[]>>
569
+ ) {
570
+ return saveAllWithEffectInt(
571
+ this,
572
+ pure.runTerm([...items])
573
+ )
574
+ }
575
+
576
+ saveWithPure_<
577
+ R,
578
+ T extends { id: unknown },
579
+ PM extends PersistenceModelType<string>,
580
+ A,
581
+ E,
582
+ Evt,
583
+ S1 extends T,
584
+ S2 extends T,
585
+ ItemType extends string
586
+ >(
587
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
588
+ item: S1,
589
+ pure: Effect<A, E, FixEnv<R, Evt, S1, S2>>
590
+ ) {
591
+ return saveAllWithEffectInt(
592
+ this,
593
+ pure
594
+ .runTerm(item)
595
+ .map(([item, events, a]) => [[item], events, a])
596
+ )
597
+ }
598
+
599
+ saveAllWithEffectInt<
600
+ T extends { id: unknown },
601
+ PM extends PersistenceModelType<string>,
602
+ P extends T,
603
+ Evt,
604
+ ItemType extends string,
605
+ R,
606
+ E,
607
+ A
608
+ >(
609
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
610
+ gen: Effect<readonly [Iterable<P>, Iterable<Evt>, A], E, R>
611
+ ) {
612
+ return gen
613
+ .flatMap(
614
+ ([items, events, a]) => this.saveAndPublish(items, events).map(() => a)
615
+ )
616
+ }
617
+
618
+ queryAndSavePureEffectBatched<
619
+ T extends { id: unknown },
620
+ PM extends PersistenceModelType<string>,
621
+ Evt,
622
+ ItemType extends string,
623
+ R,
624
+ E,
625
+ S extends T = T
626
+ >(
627
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
628
+ // TODO: think about collectPM, collectE, and collect(Parsed)
629
+ map: Effect<{ filter: Filter<PM>; collect?: (t: T) => Option<S>; limit?: number; skip?: number }, E, R>,
630
+ batchSize = 100
631
+ ) {
632
+ return <R2, A, E2, S2 extends T>(pure: Effect<A, E2, FixEnv<R2, Evt, S[], S2[]>>) =>
633
+ queryEffect(this, map)
634
+ .flatMap((_) => this.saveManyWithPureBatched_(_, pure, batchSize))
635
+ }
636
+
637
+ queryAndSavePureBatched<
638
+ T extends { id: unknown },
639
+ PM extends PersistenceModelType<string>,
640
+ Evt,
641
+ ItemType extends string,
642
+ S extends T = T
643
+ >(
644
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
645
+ // TODO: think about collectPM, collectE, and collect(Parsed)
646
+ map: { filter: Filter<PM>; collect?: (t: T) => Option<S>; limit?: number; skip?: number },
647
+ batchSize = 100
648
+ ) {
649
+ return this.queryAndSavePureEffectBatched(Effect.sync(() => map), batchSize)
650
+ }
651
+
652
+ saveManyWithPureBatched<
653
+ T extends { id: unknown },
654
+ PM extends PersistenceModelType<string>,
655
+ Evt,
656
+ ItemType extends string
657
+ >(this: RepositoryBaseC<T, PM, Evt, ItemType>, batchSize = 100) {
658
+ return <R, A, E, S1 extends T, S2 extends T>(pure: Effect<A, E, FixEnv<R, Evt, S1[], S2[]>>) =>
659
+ (items: Iterable<S1>) => saveManyWithPureBatched_(this, items, pure, batchSize)
660
+ }
661
+
662
+ saveManyWithPureBatched_<
663
+ R,
664
+ T extends { id: unknown },
665
+ PM extends PersistenceModelType<string>,
666
+ A,
667
+ E,
668
+ Evt,
669
+ S1 extends T,
670
+ S2 extends T,
671
+ ItemType extends string
672
+ >(
673
+ this: RepositoryBaseC<T, PM, Evt, ItemType>,
674
+ items: Iterable<S1>,
675
+ pure: Effect<A, E, FixEnv<R, Evt, S1[], S2[]>>,
676
+ batchSize = 100
677
+ ) {
678
+ return items
679
+ .chunk(batchSize)
680
+ .forEachEffect((batch) =>
681
+ saveAllWithEffectInt(
682
+ this,
683
+ pure.runTerm(batch)
684
+ )
685
+ )
686
+ }
687
+
688
+ readonly save = (...items: NonEmptyArray<T>) => this.saveAndPublish(items)
689
+
690
+ readonly saveWithEvents = (
691
+ events: Iterable<Evt>
692
+ ) =>
693
+ (...items: NonEmptyArray<T>) => this.saveAndPublish(items, events)
694
+ }
695
+
79
696
  type Exact<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false
80
697
  /**
81
698
  * A base implementation to create a repository.
@@ -164,10 +781,20 @@ export function makeRepo<
164
781
  (S.make(_._tag === "Transform" ? _.from : _) as unknown as Schema<T, From>)
165
782
  .pipe(S.pick("id"))
166
783
  ))
167
- : _.ast._tag === "Transform"
168
- ? (S.make(_.ast.from) as unknown as Schema<T, From>)
169
- .pipe(S.pick("id"))
170
- : _.pipe(S.pick("id"))
784
+ : _
785
+ .ast
786
+ ._tag === "Transform"
787
+ ? (S
788
+ .make(
789
+ _
790
+ .ast
791
+ .from
792
+ ) as unknown as Schema<T, From>)
793
+ .pipe(S
794
+ .pick("id"))
795
+ : _
796
+ .pipe(S
797
+ .pick("id"))
171
798
  )
172
799
  const encodeId = flow(i.encode, Effect.provide(rctx))
173
800
  function findE(_id: T["id"]) {
@@ -269,7 +896,7 @@ export function makeRepo<
269
896
  return r
270
897
  })
271
898
  // .withSpan("Repository.make [effect-app/infra]", { attributes: { "repository.model_name": name } })
272
- .withLogSpan("Repository.make: " + name);
899
+ .withLogSpan("Repository.make: " + name)
273
900
  }
274
901
 
275
902
  return {
@@ -277,7 +904,7 @@ export function makeRepo<
277
904
  where,
278
905
  query: QueryBuilder.make<PM>()
279
906
  }
280
- };
907
+ }
281
908
  }
282
909
 
283
910
  /**
@@ -388,7 +1015,7 @@ export function makeStore<
388
1015
  }
389
1016
 
390
1017
  return makeStore
391
- };
1018
+ }
392
1019
  }
393
1020
 
394
1021
  export interface Repos<
@@ -476,7 +1103,7 @@ export const RepositoryBaseImpl = <Service>() => {
476
1103
  static readonly type: Repository<T, PM, Evt, ItemType> = undefined as any
477
1104
  }
478
1105
  return assignTag<Service>()(Cls) as any
479
- };
1106
+ }
480
1107
  }
481
1108
 
482
1109
  export const RepositoryDefaultImpl = <Service>() => {
@@ -491,7 +1118,7 @@ export const RepositoryDefaultImpl = <Service>() => {
491
1118
  ): Exact<PM, From & { _etag: string | undefined }> extends true ?
492
1119
  & (abstract new(
493
1120
  impl: Repository<T, PM, Evt, ItemType>
494
- ) => RepositoryBaseC2<T, PM, Evt, ItemType>)
1121
+ ) => RepositoryBaseC3<T, PM, Evt, ItemType>)
495
1122
  & Tag<Service, Service>
496
1123
  & Repos<
497
1124
  T,
@@ -508,7 +1135,7 @@ export const RepositoryDefaultImpl = <Service>() => {
508
1135
  jitM ? (pm) => jitM(pm as unknown as From) : (pm) => pm as any,
509
1136
  (e, _etag) => ({ ...e, _etag })
510
1137
  )
511
- abstract class Cls extends RepositoryBaseC2<T, PM, Evt, ItemType> {
1138
+ abstract class Cls extends RepositoryBaseC3<T, PM, Evt, ItemType> {
512
1139
  constructor(
513
1140
  impl: Repository<T, PM, Evt, ItemType>
514
1141
  ) {
@@ -523,5 +1150,5 @@ export const RepositoryDefaultImpl = <Service>() => {
523
1150
  static readonly type: Repository<T, PM, Evt, ItemType> = undefined as any
524
1151
  }
525
1152
  return assignTag<Service>()(Cls) as any // impl is missing, but its marked protected
526
- };
1153
+ }
527
1154
  }