@effect-app/infra 2.6.0 → 2.7.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 (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/_cjs/services/Repository/legacy.cjs +38 -1
  3. package/_cjs/services/Repository/legacy.cjs.map +1 -1
  4. package/_cjs/services/RepositoryBase.cjs.map +1 -1
  5. package/_cjs/services/Store/service.cjs.map +1 -1
  6. package/_cjs/services/query/dsl.cjs +10 -11
  7. package/_cjs/services/query/dsl.cjs.map +1 -1
  8. package/_cjs/services/query/new-kid-interpreter.cjs +1 -0
  9. package/_cjs/services/query/new-kid-interpreter.cjs.map +1 -1
  10. package/dist/services/Operations.d.ts +3 -3
  11. package/dist/services/Repository/legacy.d.ts +73 -23
  12. package/dist/services/Repository/legacy.d.ts.map +1 -1
  13. package/dist/services/Repository/legacy.js +26 -2
  14. package/dist/services/Repository/service.d.ts +10 -4
  15. package/dist/services/Repository/service.d.ts.map +1 -1
  16. package/dist/services/RepositoryBase.d.ts +2 -2
  17. package/dist/services/RepositoryBase.d.ts.map +1 -1
  18. package/dist/services/RepositoryBase.js +1 -1
  19. package/dist/services/Store/service.d.ts +6 -5
  20. package/dist/services/Store/service.d.ts.map +1 -1
  21. package/dist/services/Store/service.js +1 -1
  22. package/dist/services/query/dsl.d.ts +116 -58
  23. package/dist/services/query/dsl.d.ts.map +1 -1
  24. package/dist/services/query/dsl.js +12 -13
  25. package/dist/services/query/new-kid-interpreter.d.ts +2 -1
  26. package/dist/services/query/new-kid-interpreter.d.ts.map +1 -1
  27. package/dist/services/query/new-kid-interpreter.js +2 -1
  28. package/examples/query.ts +37 -11
  29. package/package.json +1 -1
  30. package/src/services/Repository/legacy.ts +179 -3
  31. package/src/services/Repository/service.ts +20 -12
  32. package/src/services/RepositoryBase.ts +4 -2
  33. package/src/services/Store/service.ts +6 -3
  34. package/src/services/query/dsl.ts +742 -315
  35. package/src/services/query/new-kid-interpreter.ts +10 -3
  36. package/test/dist/query.test.d.ts.map +1 -1
  37. package/test/query.test.ts +245 -11
@@ -2,13 +2,16 @@
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3
3
  import type { NonEmptyReadonlyArray, Option, ParseResult, S } from "effect-app"
4
4
  import { Context, Effect, Layer } from "effect-app"
5
- import type { NotFoundError, OptimisticConcurrencyException } from "effect-app/client"
5
+ import type { InvalidStateError, NotFoundError, OptimisticConcurrencyException } from "effect-app/client"
6
+ import type { FixEnv, PureEnv } from "effect-app/Pure"
7
+ import type { NonNegativeInt } from "effect-app/Schema"
8
+ import type { FieldValues } from "../../filter/types.js"
6
9
  import * as Q from "../query.js"
7
10
  import type { Repos } from "../RepositoryBase.js"
8
11
  import { makeRepoInternal } from "../RepositoryBase.js"
9
12
  import type { StoreConfig, StoreMaker } from "../Store.js"
10
13
  import { type ExtendedRepository, extendRepo } from "./ext.js"
11
- import type { Repository } from "./service.js"
14
+ import type { RefineTHelper, Repository } from "./service.js"
12
15
 
13
16
  const names = new Map<string, number>()
14
17
  const registerName = (name: string) => {
@@ -23,6 +26,177 @@ const registerName = (name: string) => {
23
26
  }
24
27
  }
25
28
 
29
+ /**
30
+ * @deprecated
31
+ */
32
+ export interface RepoFunctions<T, Encoded extends { id: string }, Evt, ItemType, IdKey extends keyof T, Service> {
33
+ itemType: ItemType
34
+ T: T
35
+ all: Effect<readonly T[], never, Service>
36
+ find: (id: T[IdKey]) => Effect<Option<T>, never, Service>
37
+ removeById: (id: T[IdKey]) => Effect<void, NotFoundError<ItemType>, Service>
38
+ saveAndPublish: (
39
+ items: Iterable<T>,
40
+ events?: Iterable<Evt>
41
+ ) => Effect<void, InvalidStateError | OptimisticConcurrencyException, Service>
42
+ removeAndPublish: (
43
+ items: Iterable<T>,
44
+ events?: Iterable<Evt>
45
+ ) => Effect<void, never, Service>
46
+ save: (...items: T[]) => Effect<void, InvalidStateError | OptimisticConcurrencyException, Service>
47
+ get: (id: T[IdKey]) => Effect<T, NotFoundError<ItemType>, Service>
48
+ queryAndSavePure: {
49
+ <A, E2, R2, T2 extends T>(
50
+ q: (
51
+ q: Q.Query<Encoded>
52
+ ) => Q.QueryEnd<Encoded, "one">,
53
+ pure: Effect<A, E2, FixEnv<R2, Evt, T, T2>>
54
+ ): Effect.Effect<
55
+ A,
56
+ InvalidStateError | OptimisticConcurrencyException | NotFoundError<ItemType> | E2,
57
+ | Service
58
+ | Exclude<R2, {
59
+ env: PureEnv<Evt, T, T2>
60
+ }>
61
+ >
62
+ <A, E2, R2, T2 extends T>(
63
+ q: (
64
+ q: Q.Query<Encoded>
65
+ ) =>
66
+ | Q.Query<Encoded>
67
+ | Q.QueryWhere<Encoded>
68
+ | Q.QueryEnd<Encoded, "many">,
69
+ pure: Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>
70
+ ): Effect.Effect<
71
+ A,
72
+ InvalidStateError | OptimisticConcurrencyException | E2,
73
+ | Service
74
+ | Exclude<R2, {
75
+ env: PureEnv<Evt, readonly T[], readonly T2[]>
76
+ }>
77
+ >
78
+ <A, E2, R2, T2 extends T>(
79
+ q: (
80
+ q: Q.Query<Encoded>
81
+ ) =>
82
+ | Q.Query<Encoded>
83
+ | Q.QueryWhere<Encoded>
84
+ | Q.QueryEnd<Encoded, "many">,
85
+ pure: Effect<A, E2, FixEnv<R2, Evt, readonly T[], readonly T2[]>>,
86
+ batch: "batched" | number
87
+ ): Effect.Effect<
88
+ A[],
89
+ InvalidStateError | OptimisticConcurrencyException | E2,
90
+ | Service
91
+ | Exclude<R2, {
92
+ env: PureEnv<Evt, readonly T[], readonly T2[]>
93
+ }>
94
+ >
95
+ }
96
+ readonly query: {
97
+ <A, R, From extends FieldValues, TType extends "one" | "many" | "count" = "many">(
98
+ q: (
99
+ initial: Q.Query<Encoded>
100
+ ) => Q.QueryProjection<Encoded extends From ? From : never, A, R, TType>
101
+ ): Effect.Effect<
102
+ TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A,
103
+ | (TType extends "many" ? never : NotFoundError<ItemType>)
104
+ | (TType extends "count" ? never : S.ParseResult.ParseError),
105
+ R | Service
106
+ >
107
+ <
108
+ R = never,
109
+ TType extends "one" | "many" = "many",
110
+ EncodedRefined extends Encoded = Encoded
111
+ >(
112
+ q: (
113
+ initial: Q.Query<Encoded>
114
+ ) => Q.QAll<Encoded, EncodedRefined, RefineTHelper<T, EncodedRefined>, R, TType>
115
+ ): Effect.Effect<
116
+ TType extends "many" ? readonly RefineTHelper<T, EncodedRefined>[] : RefineTHelper<T, EncodedRefined>,
117
+ TType extends "many" ? never : NotFoundError<ItemType>,
118
+ R | Service
119
+ >
120
+ }
121
+ byIdAndSaveWithPure: {
122
+ <R, A, E, S2 extends T>(
123
+ id: T[IdKey],
124
+ pure: Effect<A, E, FixEnv<R, Evt, T, S2>>
125
+ ): Effect<
126
+ A,
127
+ InvalidStateError | OptimisticConcurrencyException | E | NotFoundError<ItemType>,
128
+ | Service
129
+ | Exclude<R, {
130
+ env: PureEnv<Evt, T, S2>
131
+ }>
132
+ >
133
+ }
134
+ saveManyWithPure: {
135
+ <R, A, E, S1 extends T, S2 extends T>(
136
+ items: Iterable<S1>,
137
+ pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>
138
+ ): Effect.Effect<
139
+ A,
140
+ InvalidStateError | OptimisticConcurrencyException | E,
141
+ Exclude<R, {
142
+ env: PureEnv<Evt, readonly S1[], readonly S2[]>
143
+ }>
144
+ >
145
+ <R, A, E, S1 extends T, S2 extends T>(
146
+ items: Iterable<S1>,
147
+ pure: Effect<A, E, FixEnv<R, Evt, readonly S1[], readonly S2[]>>,
148
+ batch: "batched" | number
149
+ ): Effect.Effect<
150
+ A[],
151
+ InvalidStateError | OptimisticConcurrencyException | E,
152
+ Exclude<R, {
153
+ env: PureEnv<Evt, readonly S1[], readonly S2[]>
154
+ }>
155
+ >
156
+ }
157
+ /** @experimental */
158
+ mapped: MM<Service, Encoded>
159
+ use: <X>(
160
+ body: (_: Service) => X
161
+ ) => X extends Effect<infer A, infer E, infer R> ? Effect<A, E, R | Service> : Effect<X, never, Service>
162
+ }
163
+
164
+ /**
165
+ * @deprecated
166
+ */
167
+ const makeRepoFunctions = (tag: any, itemType: any) => {
168
+ const { all } = Effect.serviceConstants(tag) as any
169
+ const {
170
+ byIdAndSaveWithPure,
171
+ find,
172
+ get,
173
+ query,
174
+ queryAndSavePure,
175
+ removeAndPublish,
176
+ removeById,
177
+ save,
178
+ saveAndPublish,
179
+ saveManyWithPure
180
+ } = Effect.serviceFunctions(tag) as any
181
+ const mapped = (s: any) => Effect.map(tag, (_: any) => _.mapped(s))
182
+ return {
183
+ itemType,
184
+ all,
185
+ byIdAndSaveWithPure,
186
+ find,
187
+ removeById,
188
+ saveAndPublish,
189
+ removeAndPublish,
190
+ save,
191
+ get,
192
+ query,
193
+ mapped,
194
+ queryAndSavePure,
195
+ saveManyWithPure,
196
+ use: (body: any) => Effect.andThen(tag, body)
197
+ }
198
+ }
199
+
26
200
  /** @deprecated use makeRepo/extendRepo */
27
201
  export class RepositoryBase<
28
202
  T,
@@ -132,6 +306,7 @@ export const RepositoryDefaultImpl2 = <Service, Evt = never>() => {
132
306
  impl: Repository<T, Encoded, Evt, ItemType, IdKey, R> & Ext
133
307
  ) => RepositoryBase<T, Encoded, Evt, ItemType, Ext, IdKey, R>)
134
308
  & Context.Tag<Service, Service>
309
+ & RepoFunctions<T, Encoded, Evt, ItemType, IdKey, Service>
135
310
  & {
136
311
  Default: Layer.Layer<
137
312
  Service,
@@ -207,6 +382,7 @@ export const RepositoryDefaultImpl2 = <Service, Evt = never>() => {
207
382
  impl: Repository<T, Encoded, Evt, ItemType, "id", R> & Ext
208
383
  ) => RepositoryBase<T, Encoded, Evt, ItemType, Ext, "id", R>)
209
384
  & Context.Tag<Service, Service>
385
+ & RepoFunctions<T, Encoded, Evt, ItemType, "id", Service>
210
386
  & {
211
387
  Default: Layer.Layer<
212
388
  Service,
@@ -327,7 +503,7 @@ export const RepositoryDefaultImpl2 = <Service, Evt = never>() => {
327
503
  Error.stackTraceLimit = limit
328
504
  // TODO: actual class name or expect a string identifier - careful with overlapping between modules
329
505
  return Context.assignTag<Service>(registerName(itemType + "Repo"), creationError)(
330
- Cls
506
+ Object.assign(Cls, makeRepoFunctions(Cls, itemType))
331
507
  ) as any // impl is missing, but its marked protected
332
508
  }
333
509
 
@@ -1,5 +1,4 @@
1
- import type { ParseResult } from "effect"
2
- import type { Effect, Option, PubSub } from "effect-app"
1
+ import type { Effect, Option, PubSub, S } from "effect-app"
3
2
  import type { InvalidStateError, NotFoundError, OptimisticConcurrencyException } from "effect-app/client"
4
3
  import type { NonNegativeInt } from "effect-app/Schema/numbers"
5
4
  import type { FieldValues } from "../../filter/types.js"
@@ -32,24 +31,33 @@ export interface Repository<
32
31
  ) => Effect<void, never, R>
33
32
 
34
33
  readonly query: {
35
- <A, R, Encoded2 extends FieldValues, TType extends "one" | "many" | "count" = "many">(
34
+ <A, R, From extends FieldValues, TType extends "one" | "many" | "count" = "many">(
36
35
  q: (
37
36
  initial: Query<Encoded>
38
- ) => QueryProjection<Encoded extends Encoded2 ? Encoded2 : never, A, R, TType>
37
+ ) => QueryProjection<Encoded extends From ? From : never, A, R, TType>
39
38
  ): Effect.Effect<
40
39
  TType extends "many" ? readonly A[] : TType extends "count" ? NonNegativeInt : A,
41
40
  | (TType extends "many" ? never : NotFoundError<ItemType>)
42
- | (TType extends "count" ? never : ParseResult.ParseError),
41
+ | (TType extends "count" ? never : S.ParseResult.ParseError),
42
+ R
43
+ >
44
+ <
45
+ R = never,
46
+ TType extends "one" | "many" = "many",
47
+ EncodedRefined extends Encoded = Encoded
48
+ >(
49
+ q: (initial: Query<Encoded>) => QAll<Encoded, EncodedRefined, RefineTHelper<T, EncodedRefined>, R, TType>
50
+ ): Effect.Effect<
51
+ TType extends "many" ? readonly RefineTHelper<T, EncodedRefined>[] : RefineTHelper<T, EncodedRefined>,
52
+ TType extends "many" ? never : NotFoundError<ItemType>,
43
53
  R
44
54
  >
45
- <R = never, TType extends "one" | "many" = "many">(
46
- q: (initial: Query<Encoded>) => QAll<Encoded, T, R, TType>
47
- ): Effect.Effect<TType extends "many" ? readonly T[] : T, TType extends "many" ? never : NotFoundError<ItemType>, R>
48
- // <R = never>(q: QAll<Encoded, T, R>): Effect.Effect<readonly T[], never, R>
49
- // <A, R, Encoded2 extends FieldValues>(
50
- // q: QueryProjection<Encoded extends Encoded2 ? Encoded2 : never, A, R>
51
- // ): Effect.Effect<readonly A[], S.ParseResult.ParseError, R>
52
55
  }
53
56
  /** @deprecated use query */
54
57
  readonly mapped: Mapped<Encoded>
55
58
  }
59
+
60
+ export type RefineTHelper<T, EncodedRefined> = EncodedRefined extends { _tag: any }
61
+ ? T extends { _tag: any } ? Extract<T, { _tag: EncodedRefined["_tag"] }>
62
+ : T
63
+ : T
@@ -258,8 +258,10 @@ export function makeRepoInternal<
258
258
  <A, R, From extends FieldValues>(
259
259
  q: QueryProjection<Encoded extends From ? From : never, A, R>
260
260
  ): Effect.Effect<readonly A[], S.ParseResult.ParseError, R>
261
- <A, R>(q: QAll<NoInfer<Encoded>, A, R>): Effect.Effect<readonly A[], never, R>
262
- } = (<A, R>(q: QAll<Encoded, A, R>) => {
261
+ <A, R, EncodedRefined extends Encoded = Encoded>(
262
+ q: QAll<NoInfer<Encoded>, NoInfer<EncodedRefined>, A, R>
263
+ ): Effect.Effect<readonly A[], never, R>
264
+ } = (<A, R, EncodedRefined extends Encoded = Encoded>(q: QAll<Encoded, EncodedRefined, A, R>) => {
263
265
  const a = Q.toFilter(q)
264
266
  const eff = a.mode === "project"
265
267
  ? filter(a)
@@ -3,6 +3,8 @@ import type { UniqueKey } from "@azure/cosmos"
3
3
  import { Context, Effect } from "effect-app"
4
4
  import type { NonEmptyReadonlyArray, Option, Secret } from "effect-app"
5
5
  import type { OptimisticConcurrencyException } from "../../errors.js"
6
+ import type { FieldValues } from "../../filter/types.js"
7
+ import type { FieldPath } from "../../filter/types/path/index.js"
6
8
  import type { FilterResult } from "./filterApi/query.js"
7
9
 
8
10
  export type StoreConfig<E> = {
@@ -34,15 +36,16 @@ export type Where =
34
36
 
35
37
  export type Filter = readonly FilterResult[]
36
38
 
37
- export interface O<Encoded extends { id: string }> {
38
- key: keyof Encoded
39
+ export interface O<TFieldValues extends FieldValues> {
40
+ key: FieldPath<TFieldValues>
39
41
  direction: "ASC" | "DESC"
40
42
  }
41
43
 
42
44
  export interface FilterArgs<Encoded extends { id: string }, U extends keyof Encoded = never> {
45
+ t: Encoded
43
46
  filter?: Filter | undefined
44
47
  select?: NonEmptyReadonlyArray<U> | undefined
45
- order?: NonEmptyReadonlyArray<O<Encoded>>
48
+ order?: NonEmptyReadonlyArray<O<NoInfer<Encoded>>>
46
49
  limit?: number | undefined
47
50
  skip?: number | undefined
48
51
  }