@effect-app/infra 2.3.5 → 2.4.1

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 +109 -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/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 +100 -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 +7 -237
  32. package/dist/services/RepositoryBase.d.ts.map +1 -1
  33. package/dist/services/RepositoryBase.js +5 -200
  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 +12 -931
  44. package/test/query.test.ts +10 -8
@@ -0,0 +1,351 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3
+ import type { NonEmptyReadonlyArray, Option, ParseResult, S } from "effect-app"
4
+ import { Context, Effect, Layer } from "effect-app"
5
+ import type { NotFoundError, OptimisticConcurrencyException } from "effect-app/client"
6
+ import * as Q from "../query.js"
7
+ import type { Repos } from "../RepositoryBase.js"
8
+ import { makeRepoInternal } from "../RepositoryBase.js"
9
+ import type { StoreConfig, StoreMaker } from "../Store.js"
10
+ import { type ExtendedRepository, extendRepo } from "./ext.js"
11
+ import type { Repository } from "./service.js"
12
+
13
+ const names = new Map<string, number>()
14
+ const registerName = (name: string) => {
15
+ const existing = names.get(name)
16
+ if (existing === undefined) {
17
+ names.set(name, 1)
18
+ return name
19
+ } else {
20
+ const n = existing + 1
21
+ names.set(name, n)
22
+ return name + "-" + existing
23
+ }
24
+ }
25
+
26
+ /** @deprecated use makeRepo/extendRepo */
27
+ export class RepositoryBase<T, Encoded extends { id: string }, Evt, ItemType extends string, Ext, IdKey extends keyof T>
28
+ implements ExtendedRepository<T, Encoded, Evt, ItemType, IdKey>
29
+ {
30
+ constructor(protected readonly impl: ExtendedRepository<T, Encoded, Evt, ItemType, IdKey> & Ext) {
31
+ this.saveAndPublish = this.impl.saveAndPublish
32
+ this.removeAndPublish = this.impl.removeAndPublish
33
+ this.find = this.impl.find
34
+ this.all = this.impl.all
35
+ this.changeFeed = this.impl.changeFeed
36
+ this.mapped = this.impl.mapped
37
+ this.query = this.impl.query
38
+ this.get = this.impl.get
39
+ this.itemType = this.impl.itemType
40
+ this.idKey = this.impl.idKey
41
+ this.log = this.impl.log
42
+ this.removeById = this.impl.removeById
43
+ this.save = this.impl.save
44
+ this.saveWithEvents = this.impl.saveWithEvents
45
+ this.queryAndSavePure = this.impl.queryAndSavePure
46
+ this.saveManyWithPure = this.impl.saveManyWithPure
47
+ this.byIdAndSaveWithPure = this.impl.byIdAndSaveWithPure
48
+ this.saveWithPure = this.impl.saveWithPure
49
+ this.request = this.impl.request
50
+ }
51
+ get: (id: T[IdKey]) => Effect<T, NotFoundError<ItemType>>
52
+ idKey
53
+ request
54
+ itemType
55
+ saveAndPublish
56
+ removeAndPublish
57
+ find
58
+ all
59
+ changeFeed
60
+ mapped
61
+ query
62
+ log
63
+ removeById
64
+ save
65
+ saveWithEvents
66
+ queryAndSavePure
67
+ saveManyWithPure
68
+ byIdAndSaveWithPure
69
+ saveWithPure
70
+ }
71
+
72
+ export const RepositoryDefaultImpl2 = <Service, Evt = never>() => {
73
+ const f: {
74
+ <
75
+ ItemType extends string,
76
+ R,
77
+ Encoded extends { id: string },
78
+ T,
79
+ IdKey extends keyof T,
80
+ E = never,
81
+ RInitial = never,
82
+ R2 = never,
83
+ Layers extends [Layer.Layer.Any, ...Layer.Layer.Any[]] = [Layer.Layer<never>],
84
+ E1 = never,
85
+ R1 = never,
86
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
87
+ Ext = {}
88
+ >(
89
+ itemType: ItemType,
90
+ schema: S.Schema<T, Encoded, R>,
91
+ options: [Evt] extends [never] ? {
92
+ dependencies?: Layers
93
+ idKey: IdKey
94
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
95
+ partitionValue?: (a: Encoded) => string
96
+ }
97
+ jitM?: (pm: Encoded) => Encoded
98
+ options?: Effect<
99
+ {
100
+ makeInitial?: Effect<readonly T[], E, RInitial>
101
+ ext?: Ext
102
+ },
103
+ E1,
104
+ R1
105
+ >
106
+ }
107
+ : {
108
+ dependencies?: Layers
109
+ idKey: IdKey
110
+ jitM?: (pm: Encoded) => Encoded
111
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
112
+ partitionValue?: (a: Encoded) => string
113
+ }
114
+ options?: Effect<
115
+ {
116
+ publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
117
+ makeInitial?: Effect<readonly T[], E, RInitial>
118
+ ext?: Ext
119
+ },
120
+ E1,
121
+ R1
122
+ >
123
+ }
124
+ ):
125
+ & (abstract new(
126
+ impl: Repository<T, Encoded, Evt, ItemType, IdKey> & Ext
127
+ ) => RepositoryBase<T, Encoded, Evt, ItemType, Ext, IdKey>)
128
+ & Context.Tag<Service, Service>
129
+ & {
130
+ Default: Layer.Layer<
131
+ Service,
132
+ E1 | Layer.Layer.Error<Layers[number]>,
133
+ Exclude<
134
+ R1 | R | StoreMaker,
135
+ { [k in keyof Layers]: Layer.Layer.Success<Layers[k]> }[number]
136
+ >
137
+ >
138
+ DefaultWithoutDependencies: Layer.Layer<
139
+ Service,
140
+ E1,
141
+ R1 | R | StoreMaker
142
+ >
143
+ }
144
+ & Repos<
145
+ T,
146
+ Encoded,
147
+ R,
148
+ Evt,
149
+ ItemType,
150
+ IdKey
151
+ >
152
+ <
153
+ ItemType extends string,
154
+ R,
155
+ Encoded extends { id: string },
156
+ T extends { id: unknown },
157
+ E = never,
158
+ RInitial = never,
159
+ R2 = never,
160
+ Layers extends [Layer.Layer.Any, ...Layer.Layer.Any[]] = [Layer.Layer<never>],
161
+ E1 = never,
162
+ R1 = never,
163
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
164
+ Ext = {}
165
+ >(
166
+ itemType: ItemType,
167
+ schema: S.Schema<T, Encoded, R>,
168
+ options: [Evt] extends [never] ? {
169
+ dependencies?: Layers
170
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
171
+ partitionValue?: (a: Encoded) => string
172
+ }
173
+ jitM?: (pm: Encoded) => Encoded
174
+ options?: Effect<
175
+ {
176
+ makeInitial?: Effect<readonly T[], E, RInitial>
177
+ ext?: Ext
178
+ },
179
+ E1,
180
+ R1
181
+ >
182
+ }
183
+ : {
184
+ dependencies?: Layers
185
+ jitM?: (pm: Encoded) => Encoded
186
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
187
+ partitionValue?: (a: Encoded) => string
188
+ }
189
+ options?: Effect<
190
+ {
191
+ publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
192
+ makeInitial?: Effect<readonly T[], E, RInitial>
193
+ ext?: Ext
194
+ },
195
+ E1,
196
+ R1
197
+ >
198
+ }
199
+ ):
200
+ & (abstract new(
201
+ impl: Repository<T, Encoded, Evt, ItemType, "id"> & Ext
202
+ ) => RepositoryBase<T, Encoded, Evt, ItemType, Ext, "id">)
203
+ & Context.Tag<Service, Service>
204
+ & {
205
+ Default: Layer.Layer<
206
+ Service,
207
+ E1 | Layer.Layer.Error<Layers[number]>,
208
+ Exclude<
209
+ R1 | R | StoreMaker,
210
+ { [k in keyof Layers]: Layer.Layer.Success<Layers[k]> }[number]
211
+ >
212
+ >
213
+ DefaultWithoutDependencies: Layer.Layer<
214
+ Service,
215
+ E1,
216
+ R1 | R | StoreMaker
217
+ >
218
+ }
219
+ & Repos<
220
+ T,
221
+ Encoded,
222
+ R,
223
+ Evt,
224
+ ItemType,
225
+ "id"
226
+ >
227
+ } = <
228
+ ItemType extends string,
229
+ R,
230
+ Encoded extends { id: string },
231
+ T,
232
+ IdKey extends keyof T,
233
+ E = never,
234
+ RInitial = never,
235
+ R2 = never,
236
+ Layers extends [Layer.Layer.Any, ...Layer.Layer.Any[]] = [Layer.Layer<never>],
237
+ E1 = never,
238
+ R1 = never,
239
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
240
+ Ext = {}
241
+ >(
242
+ itemType: ItemType,
243
+ schema: S.Schema<T, Encoded, R>,
244
+ options: [Evt] extends [never] ? {
245
+ dependencies?: Layers
246
+ idKey?: IdKey
247
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
248
+ partitionValue?: (a: Encoded) => string
249
+ }
250
+ jitM?: (pm: Encoded) => Encoded
251
+ options?: Effect<
252
+ {
253
+ makeInitial?: Effect<readonly T[], E, RInitial>
254
+ ext?: Ext
255
+ },
256
+ E1,
257
+ R1
258
+ >
259
+ }
260
+ : {
261
+ dependencies?: Layers
262
+ idKey?: IdKey
263
+ jitM?: (pm: Encoded) => Encoded
264
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
265
+ partitionValue?: (a: Encoded) => string
266
+ }
267
+ options?: Effect<
268
+ {
269
+ publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
270
+ makeInitial?: Effect<readonly T[], E, RInitial>
271
+ ext?: Ext
272
+ },
273
+ E1,
274
+ R1
275
+ >
276
+ }
277
+ ) => {
278
+ let layerCache = undefined
279
+ let layerCache2 = undefined
280
+ abstract class Cls extends RepositoryBase<
281
+ T,
282
+ Encoded,
283
+ Evt,
284
+ ItemType,
285
+ Ext,
286
+ IdKey
287
+ > {
288
+ static readonly Q = Q.make<Encoded>()
289
+ static get DefaultWithoutDependencies() {
290
+ const self = this as any
291
+ return layerCache ??= Effect
292
+ .gen(function*() {
293
+ const opts = yield* options.options ?? Effect.succeed({})
294
+ const mkRepo = makeRepoInternal<Evt>()(
295
+ itemType,
296
+ schema,
297
+ options?.jitM ? (pm) => options.jitM!(pm) : (pm) => pm,
298
+ (e, _etag) => ({ ...e, _etag }),
299
+ options.idKey ?? "id" as any
300
+ )
301
+ const r = yield* mkRepo.make({ ...options, ...opts } as any)
302
+ const repo = new self(Object.assign(extendRepo(r), "ext" in opts ? opts.ext : {}))
303
+ return Layer.succeed(self, repo)
304
+ })
305
+ .pipe(Layer.unwrapEffect)
306
+ }
307
+ static get Default() {
308
+ const self = this as any
309
+ return layerCache2 ??= options.dependencies
310
+ ? self
311
+ .DefaultWithoutDependencies
312
+ .pipe(Layer.provide(options.dependencies as any))
313
+ : self.DefaultWithoutDependencies
314
+ }
315
+ static readonly type: Repository<T, Encoded, Evt, ItemType, IdKey> = undefined as any
316
+ }
317
+ const limit = Error.stackTraceLimit
318
+ Error.stackTraceLimit = 2
319
+ const creationError = new Error()
320
+ Error.stackTraceLimit = limit
321
+ // TODO: actual class name or expect a string identifier - careful with overlapping between modules
322
+ return Context.assignTag<Service>(registerName(itemType + "Repo"), creationError)(
323
+ Cls
324
+ ) as any // impl is missing, but its marked protected
325
+ }
326
+
327
+ return f
328
+ }
329
+
330
+ export interface Mapped1<A, IdKey extends keyof A, R> {
331
+ all: Effect<A[], ParseResult.ParseError, R>
332
+ save: (...xes: readonly A[]) => Effect<void, OptimisticConcurrencyException | ParseResult.ParseError, R>
333
+ find: (id: A[IdKey]) => Effect<Option<A>, ParseResult.ParseError, R>
334
+ }
335
+
336
+ // TODO: auto use project, and select fields from the From side of schema only
337
+ export interface Mapped2<A, R> {
338
+ all: Effect<A[], ParseResult.ParseError, R>
339
+ }
340
+
341
+ export interface Mapped<Encoded extends { id: string }> {
342
+ <A, R, IdKey extends keyof A>(schema: S.Schema<A, Encoded, R>): Mapped1<A, IdKey, R>
343
+ // TODO: constrain on Encoded2 having to contain only fields that fit Encoded
344
+ <A, Encoded2, R>(schema: S.Schema<A, Encoded2, R>): Mapped2<A, R>
345
+ }
346
+
347
+ export interface MM<Repo, Encoded extends { id: string }> {
348
+ <A, R, IdKey extends keyof A>(schema: S.Schema<A, Encoded, R>): Effect<Mapped1<A, IdKey, R>, never, Repo>
349
+ // TODO: constrain on Encoded2 having to contain only fields that fit Encoded
350
+ <A, Encoded2, R>(schema: S.Schema<A, Encoded2, R>): Effect<Mapped2<A, R>, never, Repo>
351
+ }
@@ -1,4 +1,10 @@
1
- import type { RepositoryBaseC } from "../RepositoryBase.js"
1
+ import type { ParseResult } from "effect"
2
+ import type { Effect, Option, PubSub } from "effect-app"
3
+ import type { InvalidStateError, NotFoundError, OptimisticConcurrencyException } from "effect-app/client"
4
+ import type { NonNegativeInt } from "effect-app/Schema/numbers"
5
+ import type { FieldValues } from "../../filter/types.js"
6
+ import type { QAll, Query, QueryProjection } from "../query.js"
7
+ import type { Mapped } from "./legacy.js"
2
8
 
3
9
  /**
4
10
  * @tsplus type Repository
@@ -9,4 +15,40 @@ export interface Repository<
9
15
  Evt,
10
16
  ItemType extends string,
11
17
  IdKey extends keyof T
12
- > extends RepositoryBaseC<T, Encoded, Evt, ItemType, IdKey> {}
18
+ > {
19
+ readonly itemType: ItemType
20
+ readonly idKey: IdKey
21
+ readonly find: (id: T[IdKey]) => Effect<Option<T>>
22
+ readonly all: Effect<T[]>
23
+ readonly saveAndPublish: (
24
+ items: Iterable<T>,
25
+ events?: Iterable<Evt>
26
+ ) => Effect<void, InvalidStateError | OptimisticConcurrencyException>
27
+ readonly changeFeed: PubSub.PubSub<[T[], "save" | "remove"]>
28
+ readonly removeAndPublish: (
29
+ items: Iterable<T>,
30
+ events?: Iterable<Evt>
31
+ ) => Effect<void>
32
+
33
+ readonly query: {
34
+ <A, R, Encoded2 extends FieldValues, TType extends "one" | "many" | "count" = "many">(
35
+ q: (
36
+ initial: Query<Encoded>
37
+ ) => QueryProjection<Encoded extends Encoded2 ? Encoded2 : never, A, R, TType>
38
+ ): Effect.Effect<
39
+ TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A,
40
+ | (TType extends "many" ? never : NotFoundError<ItemType>)
41
+ | (TType extends "count" ? never : ParseResult.ParseError),
42
+ R
43
+ >
44
+ <R = never, TType extends "one" | "many" = "many">(
45
+ q: (initial: Query<Encoded>) => QAll<Encoded, T, R, TType>
46
+ ): Effect.Effect<TType extends "many" ? readonly T[] : T, TType extends "many" ? never : NotFoundError<ItemType>, R>
47
+ // <R = never>(q: QAll<Encoded, T, R>): Effect.Effect<readonly T[], never, R>
48
+ // <A, R, Encoded2 extends FieldValues>(
49
+ // q: QueryProjection<Encoded extends Encoded2 ? Encoded2 : never, A, R>
50
+ // ): Effect.Effect<readonly A[], S.ParseResult.ParseError, R>
51
+ }
52
+ /** @deprecated use query */
53
+ readonly mapped: Mapped<Encoded>
54
+ }
@@ -1,3 +1,4 @@
1
1
  export * from "./Repository/dsl.js"
2
2
  export * from "./Repository/ext.js"
3
+ export * from "./Repository/legacy.js"
3
4
  export * from "./Repository/service.js"