@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
@@ -5,325 +5,27 @@
5
5
  /* eslint-disable @typescript-eslint/no-explicit-any */
6
6
 
7
7
  // import type { ParserEnv } from "effect-app/Schema/custom/Parser"
8
- import {
9
- AnyPureDSL,
10
- byIdAndSaveWithPure,
11
- get,
12
- type Repository,
13
- saveAllWithEffectInt,
14
- saveManyWithPure_,
15
- saveManyWithPureBatched_,
16
- saveWithPure_
17
- } from "./Repository.js"
8
+ import { type Repository } from "./Repository.js"
18
9
  import { StoreMaker } from "./Store.js"
19
10
  import type { FilterArgs, PersistenceModelType, StoreConfig } from "./Store.js"
20
11
  import type {} from "effect/Equal"
21
12
  import type {} from "effect/Hash"
22
- import type { NonEmptyArray, NonEmptyReadonlyArray } from "effect-app"
23
- import {
24
- Array,
25
- Chunk,
26
- Context,
27
- Effect,
28
- Equivalence,
29
- Exit,
30
- flow,
31
- Layer,
32
- Option,
33
- pipe,
34
- PubSub,
35
- Request,
36
- RequestResolver,
37
- S,
38
- Unify
39
- } from "effect-app"
13
+ import type { NonEmptyReadonlyArray } from "effect-app"
14
+ import { Array, Chunk, Effect, Equivalence, flow, Option, pipe, PubSub, S, Unify } from "effect-app"
40
15
  import { toNonEmptyArray } from "effect-app/Array"
41
16
  import { flatMapOption } from "effect-app/Effect"
42
- import { runTerm } from "effect-app/Pure"
43
- import type { FixEnv, PureEnv } from "effect-app/Pure"
44
- import type { ParseResult, Schema } from "effect-app/Schema"
17
+ import type { Schema } from "effect-app/Schema"
45
18
  import { NonNegativeInt } from "effect-app/Schema"
46
19
  import { setupRequestContextFromCurrent } from "../api/setupRequest.js"
47
- import { type InvalidStateError, NotFoundError, type OptimisticConcurrencyException } from "../errors.js"
20
+ import { NotFoundError } from "../errors.js"
48
21
  import type { FieldValues } from "../filter/types.js"
49
22
  import { make as makeQuery } from "./query.js"
50
- import type { QAll, Query, QueryEnd, QueryProjection, QueryWhere } from "./query.js"
23
+ import type { QAll, QueryProjection } from "./query.js"
51
24
  import * as Q from "./query.js"
25
+ import { extendRepo } from "./Repository/ext.js"
26
+ import type { ExtendedRepository } from "./Repository/ext.js"
52
27
  import { getContextMap } from "./Store/ContextMapContainer.js"
53
28
 
54
- export interface Mapped1<A, IdKey extends keyof A, R> {
55
- all: Effect<A[], ParseResult.ParseError, R>
56
- save: (...xes: readonly A[]) => Effect<void, OptimisticConcurrencyException | ParseResult.ParseError, R>
57
- find: (id: A[IdKey]) => Effect<Option<A>, ParseResult.ParseError, R>
58
- }
59
-
60
- // TODO: auto use project, and select fields from the From side of schema only
61
- export interface Mapped2<A, R> {
62
- all: Effect<A[], ParseResult.ParseError, R>
63
- }
64
-
65
- export interface Mapped<Encoded extends { id: string }> {
66
- <A, R, IdKey extends keyof A>(schema: S.Schema<A, Encoded, R>): Mapped1<A, IdKey, R>
67
- // TODO: constrain on Encoded2 having to contain only fields that fit Encoded
68
- <A, Encoded2, R>(schema: S.Schema<A, Encoded2, R>): Mapped2<A, R>
69
- }
70
-
71
- export interface MM<Repo, Encoded extends { id: string }> {
72
- <A, R, IdKey extends keyof A>(schema: S.Schema<A, Encoded, R>): Effect<Mapped1<A, IdKey, R>, never, Repo>
73
- // TODO: constrain on Encoded2 having to contain only fields that fit Encoded
74
- <A, Encoded2, R>(schema: S.Schema<A, Encoded2, R>): Effect<Mapped2<A, R>, never, Repo>
75
- }
76
-
77
- /**
78
- * @tsplus type Repository
79
- */
80
- export abstract class RepositoryBaseC<
81
- T,
82
- Encoded extends { id: string },
83
- Evt,
84
- ItemType extends string,
85
- IdKey extends keyof T
86
- > {
87
- abstract readonly itemType: ItemType
88
- abstract readonly find: (id: T[IdKey]) => Effect<Option<T>>
89
- abstract readonly all: Effect<T[]>
90
- abstract readonly saveAndPublish: (
91
- items: Iterable<T>,
92
- events?: Iterable<Evt>
93
- ) => Effect<void, InvalidStateError | OptimisticConcurrencyException>
94
- abstract readonly changeFeed: PubSub.PubSub<[T[], "save" | "remove"]>
95
- abstract readonly removeAndPublish: (
96
- items: Iterable<T>,
97
- events?: Iterable<Evt>
98
- ) => Effect<void>
99
-
100
- abstract readonly query: {
101
- <A, R, Encoded2 extends FieldValues, TType extends "one" | "many" | "count" = "many">(
102
- q: (
103
- initial: Query<Encoded>
104
- ) => QueryProjection<Encoded extends Encoded2 ? Encoded2 : never, A, R, TType>
105
- ): Effect.Effect<
106
- TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A,
107
- | (TType extends "many" ? never : NotFoundError<ItemType>)
108
- | (TType extends "count" ? never : S.ParseResult.ParseError),
109
- R
110
- >
111
- <R = never, TType extends "one" | "many" = "many">(
112
- q: (initial: Query<Encoded>) => QAll<Encoded, T, R, TType>
113
- ): Effect.Effect<TType extends "many" ? readonly T[] : T, TType extends "many" ? never : NotFoundError<ItemType>, R>
114
- // <R = never>(q: QAll<Encoded, T, R>): Effect.Effect<readonly T[], never, R>
115
- // <A, R, Encoded2 extends FieldValues>(
116
- // q: QueryProjection<Encoded extends Encoded2 ? Encoded2 : never, A, R>
117
- // ): Effect.Effect<readonly A[], S.ParseResult.ParseError, R>
118
- }
119
-
120
- /** @deprecated use query */
121
- abstract readonly mapped: Mapped<Encoded>
122
- }
123
-
124
- export abstract class RepositoryBaseC1<
125
- T,
126
- Encoded extends { id: string },
127
- Evt,
128
- ItemType extends string,
129
- IdKey extends keyof T
130
- > extends RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey> {
131
- constructor(
132
- public readonly itemType: ItemType
133
- ) {
134
- super()
135
- }
136
- }
137
-
138
- export class RepositoryBaseC2<
139
- T,
140
- Encoded extends { id: string },
141
- Evt,
142
- ItemType extends string,
143
- Ext,
144
- IdKey extends keyof T
145
- > extends RepositoryBaseC1<T, Encoded, Evt, ItemType, IdKey> {
146
- constructor(
147
- itemType: ItemType,
148
- protected readonly impl: Repository<T, Encoded, Evt, ItemType, IdKey> & Ext
149
- ) {
150
- super(itemType)
151
- this.saveAndPublish = this.impl.saveAndPublish
152
- this.removeAndPublish = this.impl.removeAndPublish
153
- this.find = this.impl.find
154
- this.all = this.impl.all
155
- this.changeFeed = this.impl.changeFeed
156
- this.mapped = this.impl.mapped
157
- this.query = this.impl.query
158
- }
159
- // makes super calls a compiler error, as it should
160
- override saveAndPublish
161
- override removeAndPublish
162
- override find
163
- override all
164
- override changeFeed
165
- override mapped
166
- override query
167
- }
168
-
169
- export class RepositoryBaseC3<
170
- T,
171
- Encoded extends { id: string },
172
- Evt,
173
- ItemType extends string,
174
- Ext,
175
- IdKey extends keyof T
176
- > extends RepositoryBaseC2<T, Encoded, Evt, ItemType, Ext, IdKey> {
177
- get(id: T[IdKey]) {
178
- return Effect.andThen(
179
- this
180
- .find(id),
181
- (_) => Effect.mapError(_, () => new NotFoundError<ItemType>({ type: this.itemType, id }))
182
- )
183
- }
184
-
185
- readonly log = (evt: Evt) => AnyPureDSL.log(evt)
186
-
187
- removeById(id: T[IdKey]) {
188
- return Effect.andThen(this.get(id), (_) => this.removeAndPublish([_]))
189
- }
190
-
191
- readonly save = (...items: NonEmptyArray<T>) => this.saveAndPublish(items)
192
-
193
- readonly saveWithEvents = (events: Iterable<Evt>) => (...items: NonEmptyArray<T>) =>
194
- this.saveAndPublish(items, events)
195
-
196
- readonly queryAndSavePure: {
197
- <A, E2, R2, T2 extends T>(
198
- q: (
199
- q: Query<Encoded>
200
- ) => QueryEnd<Encoded, "one">,
201
- pure: Effect<A, E2, FixEnv<R2, Evt, T, T2>>
202
- ): Effect.Effect<
203
- A,
204
- InvalidStateError | OptimisticConcurrencyException | NotFoundError<ItemType> | E2,
205
- Exclude<R2, {
206
- env: PureEnv<Evt, T, T2>
207
- }>
208
- >
209
- <A, E2, R2, T2 extends T>(
210
- q: (
211
- q: Query<Encoded>
212
- ) =>
213
- | Query<Encoded>
214
- | QueryWhere<Encoded>
215
- | QueryEnd<Encoded, "many">,
216
- pure: Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>
217
- ): Effect.Effect<
218
- A,
219
- InvalidStateError | OptimisticConcurrencyException | E2,
220
- Exclude<R2, {
221
- env: PureEnv<Evt, readonly T[], readonly T2[]>
222
- }>
223
- >
224
- <A, E2, R2, T2 extends T>(
225
- q: (
226
- q: Query<Encoded>
227
- ) =>
228
- | Query<Encoded>
229
- | QueryWhere<Encoded>
230
- | QueryEnd<Encoded, "many">,
231
- pure: Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>,
232
- batch: "batched" | number
233
- ): Effect.Effect<
234
- A[],
235
- InvalidStateError | OptimisticConcurrencyException | E2,
236
- Exclude<R2, {
237
- env: PureEnv<Evt, readonly T[], readonly T2[]>
238
- }>
239
- >
240
- } = (q, pure, batch?: "batched" | number) =>
241
- this.query(q).pipe(
242
- Effect.andThen((_) =>
243
- Array.isArray(_)
244
- ? batch === undefined
245
- ? saveManyWithPure_(this, _ as any, pure as any)
246
- : saveManyWithPureBatched_(this, _ as any, pure as any, batch === "batched" ? 100 : batch)
247
- : saveWithPure_(this, _ as any, pure as any)
248
- )
249
- ) as any
250
-
251
- /**
252
- * NOTE: it's not as composable, only useful when the request is simple, and only this part needs request args.
253
- */
254
- readonly handleByIdAndSaveWithPure = <Req extends { id: T[IdKey] }, Context, R, A, E, S2 extends T>(
255
- pure: (req: Req, ctx: Context) => Effect<A, E, FixEnv<R, Evt, T, S2>>
256
- ) =>
257
- (req: Req, ctx: Context) => byIdAndSaveWithPure(this, req.id)(pure(req, ctx))
258
-
259
- saveManyWithPure: {
260
- <R, A, E, S1 extends T, S2 extends T>(
261
- items: Iterable<S1>,
262
- pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>
263
- ): Effect.Effect<
264
- A,
265
- InvalidStateError | OptimisticConcurrencyException | E,
266
- Exclude<R, {
267
- env: PureEnv<Evt, readonly S1[], readonly S2[]>
268
- }>
269
- >
270
- <R, A, E, S1 extends T, S2 extends T>(
271
- items: Iterable<S1>,
272
- pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>,
273
- batch: "batched" | number
274
- ): Effect.Effect<
275
- A[],
276
- InvalidStateError | OptimisticConcurrencyException | E,
277
- Exclude<R, {
278
- env: PureEnv<Evt, readonly S1[], readonly S2[]>
279
- }>
280
- >
281
- } = (items, pure, batch?: "batched" | number) =>
282
- batch
283
- ? Effect.forEach(
284
- Array.chunk_(items, batch === "batched" ? 100 : batch),
285
- (batch) =>
286
- saveAllWithEffectInt(
287
- this,
288
- runTerm(pure, batch)
289
- )
290
- )
291
- : saveAllWithEffectInt(
292
- this,
293
- runTerm(pure, [...items])
294
- )
295
-
296
- byIdAndSaveWithPure: {
297
- <R, A, E, S2 extends T>(
298
- id: T[IdKey],
299
- pure: Effect<A, E, FixEnv<R, Evt, T, S2>>
300
- ): Effect<
301
- A,
302
- InvalidStateError | OptimisticConcurrencyException | NotFoundError<ItemType> | E,
303
- Exclude<R, {
304
- env: PureEnv<Evt, T, S2>
305
- }>
306
- >
307
- } = (id, pure): any => get(this, id).pipe(Effect.flatMap((item) => saveWithPure_(this, item, pure)))
308
-
309
- saveWithPure<
310
- R,
311
- A,
312
- E,
313
- S1 extends T,
314
- S2 extends T
315
- >(
316
- item: S1,
317
- pure: Effect<A, E, FixEnv<R, Evt, S1, S2>>
318
- ) {
319
- return saveAllWithEffectInt(
320
- this,
321
- runTerm(pure, item)
322
- .pipe(Effect.map(([item, events, a]) => [[item], events, a]))
323
- )
324
- }
325
- }
326
-
327
29
  const dedupe = Array.dedupeWith(Equivalence.string)
328
30
 
329
31
  /**
@@ -614,12 +316,12 @@ export function makeRepoInternal<
614
316
  const r: Repository<T, Encoded, Evt, ItemType, IdKey> = {
615
317
  changeFeed,
616
318
  itemType: name,
319
+ idKey,
617
320
  find,
618
321
  all,
619
322
  saveAndPublish,
620
323
  removeAndPublish,
621
324
  query: (q: any) => query(typeof q === "function" ? q(makeQuery()) : q) as any,
622
-
623
325
  /**
624
326
  * @internal
625
327
  */
@@ -786,568 +488,6 @@ export interface Repos<
786
488
 
787
489
  export type GetRepoType<T> = T extends { type: infer R } ? R : never
788
490
 
789
- export interface RepoFunctions<T, Encoded extends { id: string }, Evt, ItemType, IdKey extends keyof T, Service> {
790
- itemType: ItemType
791
- T: T
792
- all: Effect<readonly T[], never, Service>
793
- find: (id: T[IdKey]) => Effect<Option<T>, never, Service>
794
- removeById: (id: T[IdKey]) => Effect<void, NotFoundError<ItemType>, Service>
795
- saveAndPublish: (
796
- items: Iterable<T>,
797
- events?: Iterable<Evt>
798
- ) => Effect<void, InvalidStateError | OptimisticConcurrencyException, Service>
799
- removeAndPublish: (
800
- items: Iterable<T>,
801
- events?: Iterable<Evt>
802
- ) => Effect<void, never, Service>
803
- save: (...items: T[]) => Effect<void, InvalidStateError | OptimisticConcurrencyException, Service>
804
- get: (id: T[IdKey]) => Effect<T, NotFoundError<ItemType>, Service>
805
-
806
- queryAndSavePure: {
807
- <A, E2, R2, T2 extends T>(
808
- q: (
809
- q: Query<Encoded>
810
- ) => QueryEnd<Encoded, "one">,
811
- pure: Effect<A, E2, FixEnv<R2, Evt, T, T2>>
812
- ): Effect.Effect<
813
- A,
814
- InvalidStateError | OptimisticConcurrencyException | NotFoundError<ItemType> | E2,
815
- | Service
816
- | Exclude<R2, {
817
- env: PureEnv<Evt, T, T2>
818
- }>
819
- >
820
- <A, E2, R2, T2 extends T>(
821
- q: (
822
- q: Query<Encoded>
823
- ) =>
824
- | Query<Encoded>
825
- | QueryWhere<Encoded>
826
- | QueryEnd<Encoded, "many">,
827
- pure: Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>
828
- ): Effect.Effect<
829
- A,
830
- InvalidStateError | OptimisticConcurrencyException | E2,
831
- | Service
832
- | Exclude<R2, {
833
- env: PureEnv<Evt, readonly T[], readonly T2[]>
834
- }>
835
- >
836
- <A, E2, R2, T2 extends T>(
837
- q: (
838
- q: Query<Encoded>
839
- ) =>
840
- | Query<Encoded>
841
- | QueryWhere<Encoded>
842
- | QueryEnd<Encoded, "many">,
843
- pure: Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>,
844
- batch: "batched" | number
845
- ): Effect.Effect<
846
- A[],
847
- InvalidStateError | OptimisticConcurrencyException | E2,
848
- | Service
849
- | Exclude<R2, {
850
- env: PureEnv<Evt, readonly T[], readonly T2[]>
851
- }>
852
- >
853
- }
854
-
855
- readonly query: {
856
- <A, R, From extends FieldValues, TType extends "one" | "many" | "count" = "many">(
857
- q: (
858
- initial: Query<Encoded>
859
- ) => QueryProjection<Encoded extends From ? From : never, A, R, TType>
860
- ): Effect.Effect<
861
- TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A,
862
- | (TType extends "many" ? never : NotFoundError<ItemType>)
863
- | (TType extends "count" ? never : S.ParseResult.ParseError),
864
- Service | R
865
- >
866
- <R = never, TType extends "one" | "many" = "many">(
867
- q: (initial: Query<Encoded>) => QAll<Encoded, T, R, TType>
868
- ): Effect.Effect<
869
- TType extends "many" ? readonly T[] : T,
870
- TType extends "many" ? never : NotFoundError<ItemType>,
871
- Service | R
872
- >
873
- // <R = never>(q: QAll<Encoded, T, R>): Effect.Effect<readonly T[], never, Service | R>
874
- // <A, R, From extends FieldValues>(
875
- // q: QueryProjection<Encoded extends From ? From : never, A, R>
876
- // ): Effect.Effect<readonly A[], S.ParseResult.ParseError, Service | R>
877
- }
878
-
879
- byIdAndSaveWithPure: {
880
- <R, A, E, S2 extends T>(
881
- id: T[IdKey],
882
- pure: Effect<A, E, FixEnv<R, Evt, T, S2>>
883
- ): Effect<
884
- A,
885
- InvalidStateError | OptimisticConcurrencyException | E | NotFoundError<ItemType>,
886
- | Service
887
- | Exclude<R, {
888
- env: PureEnv<Evt, T, S2>
889
- }>
890
- >
891
- }
892
-
893
- saveManyWithPure: {
894
- <R, A, E, S1 extends T, S2 extends T>(
895
- items: Iterable<S1>,
896
- pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>
897
- ): Effect.Effect<
898
- A,
899
- InvalidStateError | OptimisticConcurrencyException | E,
900
- Exclude<R, {
901
- env: PureEnv<Evt, readonly S1[], readonly S2[]>
902
- }>
903
- >
904
- <R, A, E, S1 extends T, S2 extends T>(
905
- items: Iterable<S1>,
906
- pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>,
907
- batch: "batched" | number
908
- ): Effect.Effect<
909
- A[],
910
- InvalidStateError | OptimisticConcurrencyException | E,
911
- Exclude<R, {
912
- env: PureEnv<Evt, readonly S1[], readonly S2[]>
913
- }>
914
- >
915
- }
916
-
917
- /** @experimental */
918
- mapped: MM<Service, Encoded>
919
-
920
- use: <X>(
921
- body: (_: Service) => X
922
- ) => X extends Effect<infer A, infer E, infer R> ? Effect<A, E, R | Service> : Effect<X, never, Service>
923
- }
924
-
925
- const makeRepoFunctions = (tag: any, itemType: any) => {
926
- const { all } = Effect.serviceConstants(tag) as any
927
- const {
928
- byIdAndSaveWithPure,
929
- find,
930
- get,
931
- query,
932
- queryAndSavePure,
933
- removeAndPublish,
934
- removeById,
935
- save,
936
- saveAndPublish,
937
- saveManyWithPure
938
- } = Effect.serviceFunctions(tag) as any
939
-
940
- const mapped = (s: any) => Effect.map(tag, (_: any) => _.mapped(s))
941
-
942
- return {
943
- itemType,
944
- all,
945
- byIdAndSaveWithPure,
946
- find,
947
- removeById,
948
- saveAndPublish,
949
- removeAndPublish,
950
- save,
951
- get,
952
- query,
953
- mapped,
954
- queryAndSavePure,
955
- saveManyWithPure,
956
- use: (body: any) => Effect.andThen(tag, body)
957
- }
958
- }
959
-
960
- export const RepositoryBaseImpl = <Service>() => {
961
- return <
962
- Evt = never
963
- >() =>
964
- <ItemType extends string, R, Encoded extends { id: string }, T, IdKey extends keyof T>(
965
- itemType: ItemType,
966
- schema: S.Schema<T, Encoded, R>,
967
- idKey: IdKey,
968
- jitM?: (pm: Encoded) => Encoded
969
- ):
970
- & (abstract new() => RepositoryBaseC1<T, Encoded, Evt, ItemType, IdKey>)
971
- & Context.Tag<Service, Service>
972
- & Repos<
973
- T,
974
- Encoded,
975
- R,
976
- Evt,
977
- ItemType,
978
- IdKey
979
- >
980
- & RepoFunctions<T, Encoded, Evt, ItemType, IdKey, Service> =>
981
- {
982
- const mkRepo = makeRepoInternal<Evt>()(
983
- itemType,
984
- schema,
985
- jitM ? (pm) => jitM(pm as unknown as Encoded) : (pm) => pm as any,
986
- (e, _etag) => ({ ...e, _etag }),
987
- idKey
988
- )
989
- abstract class Cls extends RepositoryBaseC1<T, Encoded, Evt, ItemType, IdKey> {
990
- constructor() {
991
- super(itemType)
992
- }
993
- static readonly make = mkRepo.make
994
- static readonly makeWith = ((a: any, b: any) => Effect.map(mkRepo.make(a), b)) as any
995
-
996
- static readonly Q = Q.make<Encoded>()
997
- static readonly type: Repository<T, Encoded, Evt, ItemType, IdKey> = undefined as any
998
- }
999
- const limit = Error.stackTraceLimit
1000
- Error.stackTraceLimit = 2
1001
- const creationError = new Error()
1002
- Error.stackTraceLimit = limit
1003
- return Context.assignTag<Service>(undefined, creationError)(
1004
- Object.assign(Cls, makeRepoFunctions(Cls, itemType))
1005
- ) as any
1006
- }
1007
- }
1008
-
1009
- export const RepositoryDefaultImpl = <Service, Evt = never>() => {
1010
- const f: {
1011
- <ItemType extends string, R, Encoded extends { id: string }, T, const IdKey extends keyof T>(
1012
- itemType: ItemType,
1013
- schema: S.Schema<T, Encoded, R>,
1014
- idKey: IdKey,
1015
- jitM?: (pm: Encoded) => Encoded
1016
- ):
1017
- & (abstract new(
1018
- impl: Repository<T, Encoded, Evt, ItemType, IdKey>
1019
- ) => RepositoryBaseC3<T, Encoded, Evt, ItemType, {}, IdKey>)
1020
- & Context.Tag<Service, Service>
1021
- & Repos<
1022
- T,
1023
- Encoded,
1024
- R,
1025
- Evt,
1026
- ItemType,
1027
- IdKey
1028
- >
1029
- & RepoFunctions<T, Encoded, Evt, ItemType, IdKey, Service>
1030
- <ItemType extends string, R, Encoded extends { id: string }, T extends { id: unknown }>(
1031
- itemType: ItemType,
1032
- schema: S.Schema<T, Encoded, R>,
1033
- jitM?: (pm: Encoded) => Encoded
1034
- ):
1035
- & (abstract new(
1036
- impl: Repository<T, Encoded, Evt, ItemType, "id">
1037
- ) => RepositoryBaseC3<T, Encoded, Evt, ItemType, {}, "id">)
1038
- & Context.Tag<Service, Service>
1039
- & Repos<
1040
- T,
1041
- Encoded,
1042
- R,
1043
- Evt,
1044
- ItemType,
1045
- "id"
1046
- >
1047
- & RepoFunctions<T, Encoded, Evt, ItemType, "id", Service>
1048
- } = <ItemType extends string, R, Encoded extends { id: string }, T, const IdKey extends keyof T>(
1049
- itemType: ItemType,
1050
- schema: S.Schema<T, Encoded, R>,
1051
- idKeyOrJitM?: IdKey | ((pm: Encoded) => Encoded),
1052
- jitM?: (pm: Encoded) => Encoded
1053
- ) => {
1054
- if (!jitM && idKeyOrJitM !== undefined && typeof idKeyOrJitM === "function") jitM = idKeyOrJitM
1055
- const mkRepo = makeRepoInternal<Evt>()(
1056
- itemType,
1057
- schema,
1058
- jitM ? (pm) => jitM(pm) : (pm) => pm,
1059
- (e, _etag) => ({ ...e, _etag }),
1060
- idKeyOrJitM ?? "id" as any
1061
- )
1062
- abstract class Cls extends RepositoryBaseC3<T, Encoded, Evt, ItemType, {}, IdKey> {
1063
- constructor(
1064
- impl: Repository<T, Encoded, Evt, ItemType, IdKey>
1065
- ) {
1066
- super(itemType, impl)
1067
- }
1068
- static readonly make = mkRepo.make
1069
- static readonly makeWith = ((a: any, b: any) => Effect.map(mkRepo.make(a), b)) as any
1070
-
1071
- static readonly Q = Q.make<Encoded>()
1072
-
1073
- static readonly type: Repository<T, Encoded, Evt, ItemType, IdKey> = undefined as any
1074
- }
1075
- const limit = Error.stackTraceLimit
1076
- Error.stackTraceLimit = 2
1077
- const creationError = new Error()
1078
- Error.stackTraceLimit = limit
1079
- return Context.assignTag<Service>(undefined, creationError)(
1080
- Object.assign(Cls, makeRepoFunctions(Cls, itemType))
1081
- ) as any // impl is missing, but its marked protected
1082
- }
1083
- return f
1084
- }
1085
-
1086
- export const RepositoryDefaultImpl2 = <Service, Evt = never>() => {
1087
- const f: {
1088
- <
1089
- ItemType extends string,
1090
- R,
1091
- Encoded extends { id: string },
1092
- T,
1093
- IdKey extends keyof T,
1094
- E = never,
1095
- RInitial = never,
1096
- R2 = never,
1097
- Layers extends [Layer.Layer.Any, ...Layer.Layer.Any[]] = [Layer.Layer<never>],
1098
- E1 = never,
1099
- R1 = never,
1100
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
1101
- Ext = {}
1102
- >(
1103
- itemType: ItemType,
1104
- schema: S.Schema<T, Encoded, R>,
1105
- options: [Evt] extends [never] ? {
1106
- dependencies?: Layers
1107
- idKey: IdKey
1108
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1109
- partitionValue?: (a: Encoded) => string
1110
- }
1111
- jitM?: (pm: Encoded) => Encoded
1112
- options?: Effect<
1113
- {
1114
- makeInitial?: Effect<readonly T[], E, RInitial>
1115
- ext?: Ext
1116
- },
1117
- E1,
1118
- R1
1119
- >
1120
- }
1121
- : {
1122
- dependencies?: Layers
1123
- idKey: IdKey
1124
- jitM?: (pm: Encoded) => Encoded
1125
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1126
- partitionValue?: (a: Encoded) => string
1127
- }
1128
- options?: Effect<
1129
- {
1130
- publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
1131
- makeInitial?: Effect<readonly T[], E, RInitial>
1132
- ext?: Ext
1133
- },
1134
- E1,
1135
- R1
1136
- >
1137
- }
1138
- ):
1139
- & (abstract new(
1140
- impl: Repository<T, Encoded, Evt, ItemType, IdKey> & Ext
1141
- ) => RepositoryBaseC3<T, Encoded, Evt, ItemType, Ext, IdKey>)
1142
- & Context.Tag<Service, Service>
1143
- & {
1144
- Default: Layer.Layer<
1145
- Service,
1146
- E1 | Layer.Layer.Error<Layers[number]>,
1147
- Exclude<
1148
- R1 | R | StoreMaker,
1149
- { [k in keyof Layers]: Layer.Layer.Success<Layers[k]> }[number]
1150
- >
1151
- >
1152
- DefaultWithoutDependencies: Layer.Layer<
1153
- Service,
1154
- E1,
1155
- R1 | R | StoreMaker
1156
- >
1157
- }
1158
- & Repos<
1159
- T,
1160
- Encoded,
1161
- R,
1162
- Evt,
1163
- ItemType,
1164
- IdKey
1165
- >
1166
- & RepoFunctions<T, Encoded, Evt, ItemType, IdKey, Service>
1167
- <
1168
- ItemType extends string,
1169
- R,
1170
- Encoded extends { id: string },
1171
- T extends { id: unknown },
1172
- E = never,
1173
- RInitial = never,
1174
- R2 = never,
1175
- Layers extends [Layer.Layer.Any, ...Layer.Layer.Any[]] = [Layer.Layer<never>],
1176
- E1 = never,
1177
- R1 = never,
1178
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
1179
- Ext = {}
1180
- >(
1181
- itemType: ItemType,
1182
- schema: S.Schema<T, Encoded, R>,
1183
- options: [Evt] extends [never] ? {
1184
- dependencies?: Layers
1185
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1186
- partitionValue?: (a: Encoded) => string
1187
- }
1188
- jitM?: (pm: Encoded) => Encoded
1189
- options?: Effect<
1190
- {
1191
- makeInitial?: Effect<readonly T[], E, RInitial>
1192
- ext?: Ext
1193
- },
1194
- E1,
1195
- R1
1196
- >
1197
- }
1198
- : {
1199
- dependencies?: Layers
1200
- jitM?: (pm: Encoded) => Encoded
1201
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1202
- partitionValue?: (a: Encoded) => string
1203
- }
1204
- options?: Effect<
1205
- {
1206
- publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
1207
- makeInitial?: Effect<readonly T[], E, RInitial>
1208
- ext?: Ext
1209
- },
1210
- E1,
1211
- R1
1212
- >
1213
- }
1214
- ):
1215
- & (abstract new(
1216
- impl: Repository<T, Encoded, Evt, ItemType, "id"> & Ext
1217
- ) => RepositoryBaseC3<T, Encoded, Evt, ItemType, Ext, "id">)
1218
- & Context.Tag<Service, Service>
1219
- & {
1220
- Default: Layer.Layer<
1221
- Service,
1222
- E1 | Layer.Layer.Error<Layers[number]>,
1223
- Exclude<
1224
- R1 | R | StoreMaker,
1225
- { [k in keyof Layers]: Layer.Layer.Success<Layers[k]> }[number]
1226
- >
1227
- >
1228
- DefaultWithoutDependencies: Layer.Layer<
1229
- Service,
1230
- E1,
1231
- R1 | R | StoreMaker
1232
- >
1233
- }
1234
- & Repos<
1235
- T,
1236
- Encoded,
1237
- R,
1238
- Evt,
1239
- ItemType,
1240
- "id"
1241
- >
1242
- & RepoFunctions<T, Encoded, Evt, ItemType, "id", Service>
1243
- } = <
1244
- ItemType extends string,
1245
- R,
1246
- Encoded extends { id: string },
1247
- T,
1248
- IdKey extends keyof T,
1249
- E = never,
1250
- RInitial = never,
1251
- R2 = never,
1252
- Layers extends [Layer.Layer.Any, ...Layer.Layer.Any[]] = [Layer.Layer<never>],
1253
- E1 = never,
1254
- R1 = never,
1255
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
1256
- Ext = {}
1257
- >(
1258
- itemType: ItemType,
1259
- schema: S.Schema<T, Encoded, R>,
1260
- options: [Evt] extends [never] ? {
1261
- dependencies?: Layers
1262
- idKey?: IdKey
1263
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1264
- partitionValue?: (a: Encoded) => string
1265
- }
1266
- jitM?: (pm: Encoded) => Encoded
1267
- options?: Effect<
1268
- {
1269
- makeInitial?: Effect<readonly T[], E, RInitial>
1270
- ext?: Ext
1271
- },
1272
- E1,
1273
- R1
1274
- >
1275
- }
1276
- : {
1277
- dependencies?: Layers
1278
- idKey?: IdKey
1279
- jitM?: (pm: Encoded) => Encoded
1280
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1281
- partitionValue?: (a: Encoded) => string
1282
- }
1283
- options?: Effect<
1284
- {
1285
- publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
1286
- makeInitial?: Effect<readonly T[], E, RInitial>
1287
- ext?: Ext
1288
- },
1289
- E1,
1290
- R1
1291
- >
1292
- }
1293
- ) => {
1294
- let layerCache = undefined
1295
- let layerCache2 = undefined
1296
- abstract class Cls extends RepositoryBaseC3<
1297
- T,
1298
- Encoded,
1299
- Evt,
1300
- ItemType,
1301
- Ext,
1302
- IdKey
1303
- > {
1304
- constructor(
1305
- impl: Repository<T, Encoded, Evt, ItemType, IdKey> & Ext
1306
- ) {
1307
- super(itemType, impl)
1308
- }
1309
- static readonly Q = Q.make<Encoded>()
1310
- static get DefaultWithoutDependencies() {
1311
- const self = this as any
1312
- return layerCache ??= Effect
1313
- .gen(function*() {
1314
- const opts = yield* options.options ?? Effect.succeed({})
1315
- const mkRepo = makeRepoInternal<Evt>()(
1316
- itemType,
1317
- schema,
1318
- options?.jitM ? (pm) => options.jitM!(pm) : (pm) => pm,
1319
- (e, _etag) => ({ ...e, _etag }),
1320
- options.idKey ?? "id" as any
1321
- )
1322
- const r = yield* mkRepo.make({ ...options, ...opts } as any)
1323
- const repo = new self(Object.assign(r, "ext" in opts ? opts.ext : {}))
1324
- return Layer.succeed(self, repo)
1325
- })
1326
- .pipe(Layer.unwrapEffect)
1327
- }
1328
- static get Default() {
1329
- const self = this as any
1330
- return layerCache2 ??= options.dependencies
1331
- ? self
1332
- .DefaultWithoutDependencies
1333
- .pipe(Layer.provide(options.dependencies as any))
1334
- : self.DefaultWithoutDependencies
1335
- }
1336
- static readonly type: Repository<T, Encoded, Evt, ItemType, IdKey> = undefined as any
1337
- }
1338
- const limit = Error.stackTraceLimit
1339
- Error.stackTraceLimit = 2
1340
- const creationError = new Error()
1341
- Error.stackTraceLimit = limit
1342
- // TODO: actual class name or expect a string identifier - careful with overlapping between modules
1343
- return Context.assignTag<Service>(registerName(itemType + "Repo"), creationError)(
1344
- Object.assign(Cls, makeRepoFunctions(Cls, itemType))
1345
- ) as any // impl is missing, but its marked protected
1346
- }
1347
-
1348
- return f
1349
- }
1350
-
1351
491
  export const makeRepo: {
1352
492
  <
1353
493
  ItemType extends string,
@@ -1371,8 +511,7 @@ export const makeRepo: {
1371
511
  publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
1372
512
  makeInitial?: Effect<readonly T[], E, RInitial>
1373
513
  }
1374
- ): // TODO: drop ext
1375
- Effect.Effect<RepositoryBaseC3<T, Encoded, Evt, ItemType, {}, IdKey>, E, R | RInitial | R2 | StoreMaker>
514
+ ): Effect.Effect<ExtendedRepository<T, Encoded, Evt, ItemType, IdKey>, E, R | RInitial | R2 | StoreMaker>
1376
515
  <
1377
516
  ItemType extends string,
1378
517
  R,
@@ -1393,7 +532,7 @@ export const makeRepo: {
1393
532
  publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
1394
533
  makeInitial?: Effect<readonly T[], E, RInitial>
1395
534
  }
1396
- ): Effect.Effect<RepositoryBaseC3<T, Encoded, Evt, ItemType, {}, "id">, E, R | RInitial | R2 | StoreMaker>
535
+ ): Effect.Effect<ExtendedRepository<T, Encoded, Evt, ItemType, "id">, E, R | RInitial | R2 | StoreMaker>
1397
536
  } = <
1398
537
  ItemType extends string,
1399
538
  R,
@@ -1426,64 +565,6 @@ export const makeRepo: {
1426
565
  options.idKey ?? "id" as any
1427
566
  )
1428
567
  const r = yield* mkRepo.make(options as any)
1429
- const repo = new RepositoryBaseC3(itemType, r)
568
+ const repo = extendRepo(r)
1430
569
  return repo
1431
570
  })
1432
-
1433
- const names = new Map<string, number>()
1434
- const registerName = (name: string) => {
1435
- const existing = names.get(name)
1436
- if (existing === undefined) {
1437
- names.set(name, 1)
1438
- return name
1439
- } else {
1440
- const n = existing + 1
1441
- names.set(name, n)
1442
- return name + "-" + existing
1443
- }
1444
- }
1445
-
1446
- // TODO: integrate with repo
1447
- export const makeRequest = <
1448
- ItemType extends string,
1449
- T,
1450
- Encoded extends { id: string } & FieldValues,
1451
- Evt,
1452
- Service,
1453
- IdKey extends keyof T
1454
- >(repo: Context.Tag<Service, Service> & RepoFunctions<T, Encoded, Evt, ItemType, IdKey, Service>, idKey: IdKey) => {
1455
- type Req =
1456
- & Request.Request<T, NotFoundError<ItemType>>
1457
- & { _tag: `Get${ItemType}`; id: T[IdKey] }
1458
- const _request = Request.tagged<Req>(`Get${repo.itemType}`)
1459
-
1460
- const requestResolver = RequestResolver
1461
- .makeBatched((requests: NonEmptyReadonlyArray<Req>) =>
1462
- (repo
1463
- .query(Q.where("id", "in", requests.map((_) => _.id)) as any) as Effect<readonly T[], never, Service>) // TODO
1464
- .pipe(
1465
- Effect.andThen((items) =>
1466
- Effect.forEach(requests, (r) =>
1467
- Request.complete(
1468
- r,
1469
- Array
1470
- .findFirst(items, (_) => _[idKey] === r.id)
1471
- .pipe(Option.match({
1472
- onNone: () => Exit.fail(new NotFoundError({ type: repo.itemType, id: r.id })),
1473
- onSome: Exit.succeed
1474
- }))
1475
- ), { discard: true })
1476
- ),
1477
- Effect
1478
- .catchAllCause((cause) =>
1479
- Effect.forEach(requests, Request.complete(Exit.failCause(cause)), { discard: true })
1480
- )
1481
- )
1482
- )
1483
- .pipe(
1484
- RequestResolver.batchN(20),
1485
- RequestResolver.contextFromServices(repo)
1486
- )
1487
-
1488
- return (id: T[IdKey]) => Effect.request(_request({ id }), requestResolver)
1489
- }