@effect-app/infra 2.3.4 → 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 (44) hide show
  1. package/CHANGELOG.md +12 -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 +12 -233
  12. package/_cjs/services/RepositoryBase.cjs.map +1 -1
  13. package/dist/services/OperationsRepo.d.ts +4 -1
  14. package/dist/services/OperationsRepo.d.ts.map +1 -1
  15. package/dist/services/OperationsRepo.js +12 -3
  16. package/dist/services/QueueMaker/errors.d.ts +1 -1
  17. package/dist/services/QueueMaker/errors.d.ts.map +1 -1
  18. package/dist/services/Repository/dsl.d.ts.map +1 -1
  19. package/dist/services/Repository/dsl.js +1 -1
  20. package/dist/services/Repository/ext.d.ts +43 -104
  21. package/dist/services/Repository/ext.d.ts.map +1 -1
  22. package/dist/services/Repository/ext.js +59 -93
  23. package/dist/services/Repository/legacy.d.ts +139 -0
  24. package/dist/services/Repository/legacy.d.ts.map +1 -0
  25. package/dist/services/Repository/legacy.js +99 -0
  26. package/dist/services/Repository/service.d.ts +21 -2
  27. package/dist/services/Repository/service.d.ts.map +1 -1
  28. package/dist/services/Repository.d.ts +1 -0
  29. package/dist/services/Repository.d.ts.map +1 -1
  30. package/dist/services/Repository.js +2 -1
  31. package/dist/services/RepositoryBase.d.ts +13 -256
  32. package/dist/services/RepositoryBase.d.ts.map +1 -1
  33. package/dist/services/RepositoryBase.js +10 -208
  34. package/dist/services/query/new-kid-interpreter.d.ts +1 -1
  35. package/examples/query.ts +3 -2
  36. package/package.json +11 -1
  37. package/src/services/OperationsRepo.ts +13 -4
  38. package/src/services/Repository/dsl.ts +1 -0
  39. package/src/services/Repository/ext.ts +269 -250
  40. package/src/services/Repository/legacy.ts +351 -0
  41. package/src/services/Repository/service.ts +44 -2
  42. package/src/services/Repository.ts +1 -0
  43. package/src/services/RepositoryBase.ts +64 -1007
  44. 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,461 +488,8 @@ 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
- } = <
491
+ export const makeRepo: {
492
+ <
1244
493
  ItemType extends string,
1245
494
  R,
1246
495
  Encoded extends { id: string },
@@ -1249,265 +498,73 @@ export const RepositoryDefaultImpl2 = <Service, Evt = never>() => {
1249
498
  E = never,
1250
499
  RInitial = never,
1251
500
  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 = {}
501
+ Evt = never
1257
502
  >(
1258
503
  itemType: ItemType,
1259
504
  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
505
+ options: {
506
+ idKey: IdKey
507
+ jitM?: (pm: Encoded) => Encoded
508
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
509
+ partitionValue?: (a: Encoded) => string
1335
510
  }
1336
- static readonly type: Repository<T, Encoded, Evt, ItemType, IdKey> = undefined as any
511
+ publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
512
+ makeInitial?: Effect<readonly T[], E, RInitial>
1337
513
  }
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
- export const makeRepo = <Evt = never>() => {
1352
- const f: {
1353
- <
1354
- ItemType extends string,
1355
- R,
1356
- Encoded extends { id: string },
1357
- T,
1358
- IdKey extends keyof T,
1359
- E = never,
1360
- RInitial = never,
1361
- R2 = never
1362
- >(
1363
- itemType: ItemType,
1364
- schema: S.Schema<T, Encoded, R>,
1365
- options: [Evt] extends [never] ? {
1366
- idKey: IdKey
1367
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1368
- partitionValue?: (a: Encoded) => string
1369
- }
1370
- jitM?: (pm: Encoded) => Encoded
1371
- makeInitial?: Effect<readonly T[], E, RInitial>
1372
- }
1373
- : {
1374
- idKey: IdKey
1375
- jitM?: (pm: Encoded) => Encoded
1376
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1377
- partitionValue?: (a: Encoded) => string
1378
- }
1379
- publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
1380
- makeInitial?: Effect<readonly T[], E, RInitial>
1381
- }
1382
- ): // TODO: drop ext
1383
- Effect.Effect<RepositoryBaseC3<T, Encoded, Evt, ItemType, {}, IdKey>, E, R | RInitial | R2 | StoreMaker>
1384
- <
1385
- ItemType extends string,
1386
- R,
1387
- Encoded extends { id: string },
1388
- T extends { id: unknown },
1389
- E = never,
1390
- RInitial = never,
1391
- R2 = never
1392
- >(
1393
- itemType: ItemType,
1394
- schema: S.Schema<T, Encoded, R>,
1395
- options: [Evt] extends [never] ? {
1396
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1397
- partitionValue?: (a: Encoded) => string
1398
- }
1399
- jitM?: (pm: Encoded) => Encoded
1400
- makeInitial?: Effect<readonly T[], E, RInitial>
1401
- }
1402
- : {
1403
- jitM?: (pm: Encoded) => Encoded
1404
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1405
- partitionValue?: (a: Encoded) => string
1406
- }
1407
- publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
1408
- makeInitial?: Effect<readonly T[], E, RInitial>
1409
- }
1410
- ): Effect.Effect<RepositoryBaseC3<T, Encoded, Evt, ItemType, {}, "id">, E, R | RInitial | R2 | StoreMaker>
1411
- } = <
514
+ ): Effect.Effect<ExtendedRepository<T, Encoded, Evt, ItemType, IdKey>, E, R | RInitial | R2 | StoreMaker>
515
+ <
1412
516
  ItemType extends string,
1413
517
  R,
1414
518
  Encoded extends { id: string },
1415
- T,
1416
- IdKey extends keyof T,
519
+ T extends { id: unknown },
1417
520
  E = never,
1418
521
  RInitial = never,
1419
- R2 = never
522
+ R2 = never,
523
+ Evt = never
1420
524
  >(
1421
525
  itemType: ItemType,
1422
526
  schema: S.Schema<T, Encoded, R>,
1423
- options: [Evt] extends [never] ? {
1424
- idKey?: IdKey
1425
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1426
- partitionValue?: (a: Encoded) => string
1427
- }
1428
- jitM?: (pm: Encoded) => Encoded
1429
- makeInitial?: Effect<readonly T[], E, RInitial>
1430
- }
1431
- : {
1432
- idKey?: IdKey
1433
- jitM?: (pm: Encoded) => Encoded
1434
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
1435
- partitionValue?: (a: Encoded) => string
1436
- }
1437
- publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
1438
- makeInitial?: Effect<readonly T[], E, RInitial>
527
+ options: {
528
+ jitM?: (pm: Encoded) => Encoded
529
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
530
+ partitionValue?: (a: Encoded) => string
1439
531
  }
1440
- ) =>
1441
- Effect.gen(function*() {
1442
- const mkRepo = makeRepoInternal<Evt>()(
1443
- itemType,
1444
- schema,
1445
- options?.jitM ? (pm) => options.jitM!(pm) : (pm) => pm,
1446
- (e, _etag) => ({ ...e, _etag }),
1447
- options.idKey ?? "id" as any
1448
- )
1449
- const r = yield* mkRepo.make(options)
1450
- const repo = new RepositoryBaseC3(itemType, r)
1451
- return repo
1452
- })
1453
-
1454
- return f
1455
- }
1456
-
1457
- const names = new Map<string, number>()
1458
- const registerName = (name: string) => {
1459
- const existing = names.get(name)
1460
- if (existing === undefined) {
1461
- names.set(name, 1)
1462
- return name
1463
- } else {
1464
- const n = existing + 1
1465
- names.set(name, n)
1466
- return name + "-" + existing
1467
- }
1468
- }
1469
-
1470
- // TODO: integrate with repo
1471
- export const makeRequest = <
532
+ publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
533
+ makeInitial?: Effect<readonly T[], E, RInitial>
534
+ }
535
+ ): Effect.Effect<ExtendedRepository<T, Encoded, Evt, ItemType, "id">, E, R | RInitial | R2 | StoreMaker>
536
+ } = <
1472
537
  ItemType extends string,
538
+ R,
539
+ Encoded extends { id: string },
1473
540
  T,
1474
- Encoded extends { id: string } & FieldValues,
1475
- Evt,
1476
- Service,
1477
- IdKey extends keyof T
1478
- >(repo: Context.Tag<Service, Service> & RepoFunctions<T, Encoded, Evt, ItemType, IdKey, Service>, idKey: IdKey) => {
1479
- type Req =
1480
- & Request.Request<T, NotFoundError<ItemType>>
1481
- & { _tag: `Get${ItemType}`; id: T[IdKey] }
1482
- const _request = Request.tagged<Req>(`Get${repo.itemType}`)
1483
-
1484
- const requestResolver = RequestResolver
1485
- .makeBatched((requests: NonEmptyReadonlyArray<Req>) =>
1486
- (repo
1487
- .query(Q.where("id", "in", requests.map((_) => _.id)) as any) as Effect<readonly T[], never, Service>) // TODO
1488
- .pipe(
1489
- Effect.andThen((items) =>
1490
- Effect.forEach(requests, (r) =>
1491
- Request.complete(
1492
- r,
1493
- Array
1494
- .findFirst(items, (_) => _[idKey] === r.id)
1495
- .pipe(Option.match({
1496
- onNone: () => Exit.fail(new NotFoundError({ type: repo.itemType, id: r.id })),
1497
- onSome: Exit.succeed
1498
- }))
1499
- ), { discard: true })
1500
- ),
1501
- Effect
1502
- .catchAllCause((cause) =>
1503
- Effect.forEach(requests, Request.complete(Exit.failCause(cause)), { discard: true })
1504
- )
1505
- )
1506
- )
1507
- .pipe(
1508
- RequestResolver.batchN(20),
1509
- RequestResolver.contextFromServices(repo)
541
+ IdKey extends keyof T,
542
+ E = never,
543
+ RInitial = never,
544
+ R2 = never,
545
+ Evt = never
546
+ >(
547
+ itemType: ItemType,
548
+ schema: S.Schema<T, Encoded, R>,
549
+ options: {
550
+ idKey?: IdKey
551
+ jitM?: (pm: Encoded) => Encoded
552
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
553
+ partitionValue?: (a: Encoded) => string
554
+ }
555
+ publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
556
+ makeInitial?: Effect<readonly T[], E, RInitial>
557
+ }
558
+ ) =>
559
+ Effect.gen(function*() {
560
+ const mkRepo = makeRepoInternal<Evt>()(
561
+ itemType,
562
+ schema,
563
+ options?.jitM ? (pm) => options.jitM!(pm) : (pm) => pm,
564
+ (e, _etag) => ({ ...e, _etag }),
565
+ options.idKey ?? "id" as any
1510
566
  )
1511
-
1512
- return (id: T[IdKey]) => Effect.request(_request({ id }), requestResolver)
1513
- }
567
+ const r = yield* mkRepo.make(options as any)
568
+ const repo = extendRepo(r)
569
+ return repo
570
+ })