@effect-app/infra 0.196.4 → 0.196.6
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.
- package/CHANGELOG.md +12 -0
- package/_cjs/services/RepositoryBase.cjs +7 -1
- package/_cjs/services/RepositoryBase.cjs.map +1 -1
- package/_cjs/services/query.cjs +203 -0
- package/_cjs/services/query.cjs.map +1 -0
- package/dist/services/RepositoryBase.d.ts +22 -2
- package/dist/services/RepositoryBase.d.ts.map +1 -1
- package/dist/services/RepositoryBase.js +9 -4
- package/dist/services/query.d.ts +164 -0
- package/dist/services/query.d.ts.map +1 -0
- package/dist/services/query.js +153 -0
- package/package.json +14 -4
- package/src/services/RepositoryBase.ts +40 -7
- package/src/services/query.ts +458 -0
|
@@ -23,6 +23,8 @@ import { makeFilters } from "@effect-app/infra/filter"
|
|
|
23
23
|
import { Effect, S } from "effect-app"
|
|
24
24
|
import { type FixEnv, runTerm } from "effect-app/Pure"
|
|
25
25
|
import { type InvalidStateError, NotFoundError, type OptimisticConcurrencyException } from "../errors.js"
|
|
26
|
+
import type { FieldValues } from "../filter/types.js"
|
|
27
|
+
import { make as makeQuery, type QAll, type Query, query, type QueryProjection } from "./query.js"
|
|
26
28
|
import { ContextMapContainer } from "./Store/ContextMapContainer.js"
|
|
27
29
|
import * as Q from "./Store/filterApi/query.js"
|
|
28
30
|
|
|
@@ -88,6 +90,19 @@ export abstract class RepositoryBaseC<
|
|
|
88
90
|
) => Effect<void>
|
|
89
91
|
|
|
90
92
|
abstract readonly mapped: Mapped<PM, Omit<PM, "_etag">>
|
|
93
|
+
abstract readonly q2: {
|
|
94
|
+
<A, R, From extends FieldValues>(
|
|
95
|
+
q: (initial: Query<Omit<PM, "_etag">>) => QueryProjection<Omit<PM, "_etag"> extends From ? From : never, A, R>
|
|
96
|
+
): Effect.Effect<readonly A[], S.ParseResult.ParseError, R>
|
|
97
|
+
<R = never>(
|
|
98
|
+
q: (initial: Query<Omit<PM, "_etag">>) => QAll<Omit<PM, "_etag">, T, R>
|
|
99
|
+
): Effect.Effect<readonly T[], never, R>
|
|
100
|
+
|
|
101
|
+
<A, R, From extends FieldValues>(
|
|
102
|
+
q: QueryProjection<Omit<PM, "_etag"> extends From ? From : never, A, R>
|
|
103
|
+
): Effect.Effect<readonly A[], S.ParseResult.ParseError, R>
|
|
104
|
+
<R = never>(q: QAll<Omit<PM, "_etag">, T, R>): Effect.Effect<readonly T[], never, R>
|
|
105
|
+
}
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
export abstract class RepositoryBaseC1<
|
|
@@ -121,6 +136,7 @@ export class RepositoryBaseC2<
|
|
|
121
136
|
this.utils = this.impl.utils
|
|
122
137
|
this.changeFeed = this.impl.changeFeed
|
|
123
138
|
this.mapped = this.impl.mapped
|
|
139
|
+
this.q2 = this.impl.q2
|
|
124
140
|
}
|
|
125
141
|
// makes super calls a compiler error, as it should
|
|
126
142
|
override saveAndPublish
|
|
@@ -130,6 +146,7 @@ export class RepositoryBaseC2<
|
|
|
130
146
|
override utils
|
|
131
147
|
override changeFeed
|
|
132
148
|
override mapped
|
|
149
|
+
override q2
|
|
133
150
|
}
|
|
134
151
|
|
|
135
152
|
const anyQb = Q.QueryBuilder.make<any>()
|
|
@@ -782,7 +799,8 @@ export function makeRepo<
|
|
|
782
799
|
find,
|
|
783
800
|
all,
|
|
784
801
|
saveAndPublish,
|
|
785
|
-
removeAndPublish
|
|
802
|
+
removeAndPublish,
|
|
803
|
+
q2: (q: any) => query(r, typeof q === "function" ? q(makeQuery()) : q) as any
|
|
786
804
|
}
|
|
787
805
|
return r
|
|
788
806
|
})
|
|
@@ -970,26 +988,41 @@ export interface RepoFunctions<T extends { id: unknown }, PM extends { id: strin
|
|
|
970
988
|
save: (...items: T[]) => Effect<void, InvalidStateError | OptimisticConcurrencyException, Service>
|
|
971
989
|
get: (id: T["id"]) => Effect<T, NotFoundError<ItemType>, Service>
|
|
972
990
|
|
|
973
|
-
query: (b: (fn: Q.FilterTest<PM>, fields: Q.Filter<PM, never>) => Q.QueryBuilder<PM>) => Effect<T[], never,
|
|
991
|
+
query: (b: (fn: Q.FilterTest<PM>, fields: Q.Filter<PM, never>) => Q.QueryBuilder<PM>) => Effect<T[], never, Service>
|
|
974
992
|
queryLegacy: <S = T>(map: {
|
|
975
993
|
filter?: Filter<PM>
|
|
976
994
|
collect?: (t: T) => Option<S>
|
|
977
995
|
limit?: number
|
|
978
996
|
skip?: number
|
|
979
|
-
}) => Effect<S[], never,
|
|
997
|
+
}) => Effect<S[], never, Service>
|
|
980
998
|
|
|
981
999
|
mapped: MM<Service, PM, Omit<PM, "_etag">>
|
|
1000
|
+
|
|
1001
|
+
readonly q2: {
|
|
1002
|
+
<A, R>(
|
|
1003
|
+
q: (initial: Query<Omit<PM, "_etag">>) => QueryProjection<Omit<PM, "_etag">, A, R>
|
|
1004
|
+
): Effect.Effect<readonly A[], S.ParseResult.ParseError, Service | R>
|
|
1005
|
+
<R = never>(
|
|
1006
|
+
q: (initial: Query<Omit<PM, "_etag">>) => QAll<Omit<PM, "_etag">, T, R>
|
|
1007
|
+
): Effect.Effect<readonly T[], never, Service | R>
|
|
1008
|
+
|
|
1009
|
+
<A, R>(
|
|
1010
|
+
q: QueryProjection<Omit<PM, "_etag">, A, R>
|
|
1011
|
+
): Effect.Effect<readonly A[], S.ParseResult.ParseError, Service | R>
|
|
1012
|
+
<R = never>(q: QAll<Omit<PM, "_etag">, T, R>): Effect.Effect<readonly T[], never, Service | R>
|
|
1013
|
+
}
|
|
982
1014
|
}
|
|
983
1015
|
|
|
984
1016
|
const makeRepoFunctions = (tag: any) => {
|
|
985
1017
|
const { all } = Effect.serviceConstants(tag) as any
|
|
986
|
-
const { find, get, query, queryLegacy, removeAndPublish, removeById, save, saveAndPublish } = Effect
|
|
987
|
-
|
|
988
|
-
|
|
1018
|
+
const { find, get, q2, query, queryLegacy, removeAndPublish, removeById, save, saveAndPublish } = Effect
|
|
1019
|
+
.serviceFunctions(
|
|
1020
|
+
tag
|
|
1021
|
+
) as any
|
|
989
1022
|
|
|
990
1023
|
const mapped = (s: any) => tag.map((_: any) => _.mapped(s))
|
|
991
1024
|
|
|
992
|
-
return { all, find, removeById, saveAndPublish, removeAndPublish, save, get, query, queryLegacy, mapped }
|
|
1025
|
+
return { all, find, removeById, saveAndPublish, removeAndPublish, save, get, query, queryLegacy, q2, mapped }
|
|
993
1026
|
}
|
|
994
1027
|
|
|
995
1028
|
export const RepositoryBaseImpl = <Service>() => {
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import type { FieldValues } from "@effect-app/infra/filter/types"
|
|
3
|
+
import type { FieldPath, FieldPathValue } from "@effect-app/infra/filter/types/path/eager"
|
|
4
|
+
import type { Repository } from "@effect-app/infra/services/Repository"
|
|
5
|
+
import type { PersistenceModelType } from "@effect-app/infra/services/Store"
|
|
6
|
+
import type { Ops } from "@effect-app/infra/services/Store/filterApi/proxy"
|
|
7
|
+
import type { FilterResult, QueryBuilder } from "@effect-app/infra/services/Store/filterApi/query"
|
|
8
|
+
import { Data, Effect, Match, pipe, S, Unify } from "effect-app"
|
|
9
|
+
import type { NoInfer } from "effect/Types"
|
|
10
|
+
|
|
11
|
+
export type QAll<TFieldValues extends FieldValues, A = TFieldValues, R = never> =
|
|
12
|
+
| Query<TFieldValues>
|
|
13
|
+
| QueryWhere<TFieldValues>
|
|
14
|
+
| QueryEnd<TFieldValues>
|
|
15
|
+
| QueryProjection<TFieldValues, A, R>
|
|
16
|
+
|
|
17
|
+
export interface Query<TFieldValues extends FieldValues> {
|
|
18
|
+
readonly args: { TFieldValues: TFieldValues }
|
|
19
|
+
readonly _id: unique symbol
|
|
20
|
+
}
|
|
21
|
+
export interface QueryWhere<TFieldValues extends FieldValues> extends Query<TFieldValues> {
|
|
22
|
+
readonly args: { TFieldValues: TFieldValues }
|
|
23
|
+
readonly _id2: unique symbol
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface QueryEnd<TFieldValues extends FieldValues> {
|
|
27
|
+
readonly args: { TFieldValues: TFieldValues }
|
|
28
|
+
readonly _id3: unique symbol
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface QueryProjection<TFieldValues extends FieldValues, A = TFieldValues, R = never> {
|
|
32
|
+
readonly args: { TFieldValues: TFieldValues; A: A; R: R }
|
|
33
|
+
readonly _id4: unique symbol
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Impl, later
|
|
37
|
+
export type Q<TFieldValues extends FieldValues> =
|
|
38
|
+
| Initial<TFieldValues>
|
|
39
|
+
| Where<TFieldValues>
|
|
40
|
+
| And<TFieldValues>
|
|
41
|
+
| Or<TFieldValues>
|
|
42
|
+
| Order<TFieldValues, any>
|
|
43
|
+
| Page<TFieldValues>
|
|
44
|
+
| Project<any, TFieldValues, any>
|
|
45
|
+
|
|
46
|
+
export class Initial<TFieldValues extends FieldValues> extends Data.TaggedClass("value")<{ value: "initial" }>
|
|
47
|
+
implements Query<TFieldValues>
|
|
48
|
+
{
|
|
49
|
+
readonly _id: any
|
|
50
|
+
readonly args: any
|
|
51
|
+
constructor() {
|
|
52
|
+
super({ value: "initial" as const })
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class Where<TFieldValues extends FieldValues> extends Data.TaggedClass("where")<{
|
|
57
|
+
current: Query<TFieldValues>
|
|
58
|
+
operation: [string, Ops, any] | [string, any]
|
|
59
|
+
}> implements QueryWhere<TFieldValues> {
|
|
60
|
+
_id2!: any
|
|
61
|
+
_id: any
|
|
62
|
+
readonly args: any
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class And<TFieldValues extends FieldValues> extends Data.TaggedClass("and")<{
|
|
66
|
+
current: Query<TFieldValues>
|
|
67
|
+
operation: [string, Ops, any] | [string, any] | ((q: Query<TFieldValues>) => QueryWhere<TFieldValues>)
|
|
68
|
+
}> implements QueryWhere<TFieldValues> {
|
|
69
|
+
_id2!: any
|
|
70
|
+
_id: any
|
|
71
|
+
readonly args: any
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class Or<TFieldValues extends FieldValues> extends Data.TaggedClass("or")<{
|
|
75
|
+
current: Query<TFieldValues>
|
|
76
|
+
operation: [string, Ops, any] | [string, any] | ((q: Query<TFieldValues>) => QueryWhere<TFieldValues>)
|
|
77
|
+
}> implements QueryWhere<TFieldValues> {
|
|
78
|
+
_id2!: any
|
|
79
|
+
_id: any
|
|
80
|
+
readonly args: any
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class Page<TFieldValues extends FieldValues> extends Data.TaggedClass("page")<{
|
|
84
|
+
current: Query<TFieldValues> | QueryEnd<TFieldValues>
|
|
85
|
+
limit?: number | undefined
|
|
86
|
+
skip?: number | undefined
|
|
87
|
+
}> implements QueryEnd<TFieldValues> {
|
|
88
|
+
_id3!: any
|
|
89
|
+
readonly args: any
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export class Order<TFieldValues extends FieldValues, TFieldName extends FieldPath<TFieldValues>>
|
|
93
|
+
extends Data.TaggedClass("order")<{
|
|
94
|
+
current: Query<TFieldValues> | QueryEnd<TFieldValues>
|
|
95
|
+
field: TFieldName
|
|
96
|
+
desc?: boolean | undefined
|
|
97
|
+
}>
|
|
98
|
+
implements QueryEnd<TFieldValues>
|
|
99
|
+
{
|
|
100
|
+
_id3!: any
|
|
101
|
+
readonly args: any
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class Project<A, TFieldValues extends FieldValues, R> extends Data.TaggedClass("project")<{
|
|
105
|
+
current: Query<TFieldValues> | QueryEnd<TFieldValues>
|
|
106
|
+
schema: S.Schema<A, TFieldValues, R>
|
|
107
|
+
}> implements QueryProjection<TFieldValues, A, R> {
|
|
108
|
+
_id4!: any
|
|
109
|
+
readonly args: any
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const wh: any = (...operation: any[]) => (current: any[]) => new Where({ current, operation } as any)
|
|
113
|
+
export const where: FilterWhere = wh
|
|
114
|
+
|
|
115
|
+
export const and: FilterContinuation = (...operation: any[]) => (current: any) =>
|
|
116
|
+
new And({ current, operation: typeof operation[0] === "function" ? operation[0] : operation } as any)
|
|
117
|
+
|
|
118
|
+
export const or: FilterContinuation = (...operation: any[]) => (current: any) =>
|
|
119
|
+
new Or({ current, operation: typeof operation[0] === "function" ? operation[0] : operation } as any)
|
|
120
|
+
|
|
121
|
+
export const order: <TFieldValues extends FieldValues, TFieldName extends FieldPath<TFieldValues>>(
|
|
122
|
+
field: TFieldName,
|
|
123
|
+
desc?: boolean
|
|
124
|
+
) => (current: Query<TFieldValues> | QueryEnd<TFieldValues>) => QueryEnd<TFieldValues> = (field, desc) => (current) =>
|
|
125
|
+
new Order({ current, field, desc })
|
|
126
|
+
|
|
127
|
+
export const page: <TFieldValues extends FieldValues>(
|
|
128
|
+
page: { limit?: number; skip?: number }
|
|
129
|
+
) => (current: Query<TFieldValues> | QueryEnd<TFieldValues>) => QueryEnd<TFieldValues> =
|
|
130
|
+
({ limit, skip }) => (current) =>
|
|
131
|
+
new Page({
|
|
132
|
+
current,
|
|
133
|
+
limit,
|
|
134
|
+
skip
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
export const project: <TFieldValues extends FieldValues, A, R>(
|
|
138
|
+
schema: S.Schema<A, TFieldValues, R>
|
|
139
|
+
// TODO: should be able to use a subset of FieldValues and use select
|
|
140
|
+
) => (current: Query<TFieldValues> | QueryEnd<TFieldValues>) => QueryProjection<TFieldValues, A, R> =
|
|
141
|
+
(schema) => (current) => new Project({ current, schema })
|
|
142
|
+
|
|
143
|
+
// TODO: able to switch to Order and Limit/Take, which are "enders"
|
|
144
|
+
export type FilterWheres = {
|
|
145
|
+
<
|
|
146
|
+
TFieldValues extends FieldValues,
|
|
147
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
148
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
149
|
+
>(
|
|
150
|
+
path: TFieldName,
|
|
151
|
+
value: V
|
|
152
|
+
): (
|
|
153
|
+
current: Query<TFieldValues>
|
|
154
|
+
) => QueryWhere<TFieldValues>
|
|
155
|
+
<
|
|
156
|
+
TFieldValues extends FieldValues,
|
|
157
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
158
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
159
|
+
>(
|
|
160
|
+
path: TFieldName,
|
|
161
|
+
op: "neq",
|
|
162
|
+
value: V
|
|
163
|
+
): (
|
|
164
|
+
current: Query<TFieldValues>
|
|
165
|
+
) => QueryWhere<TFieldValues>
|
|
166
|
+
<
|
|
167
|
+
TFieldValues extends FieldValues,
|
|
168
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
169
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
170
|
+
>(
|
|
171
|
+
path: TFieldName,
|
|
172
|
+
op: "gt" | "gte" | "lt" | "lte",
|
|
173
|
+
value: V // only numbers?
|
|
174
|
+
): (
|
|
175
|
+
current: Query<TFieldValues>
|
|
176
|
+
) => QueryWhere<TFieldValues>
|
|
177
|
+
<
|
|
178
|
+
TFieldValues extends FieldValues,
|
|
179
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
180
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
181
|
+
>(
|
|
182
|
+
path: TFieldName,
|
|
183
|
+
op: "startsWith" | "endsWith" | "contains" | "notContains" | "notStartsWith" | "notEndsWith",
|
|
184
|
+
value: V // only strings?
|
|
185
|
+
): (
|
|
186
|
+
current: Query<TFieldValues>
|
|
187
|
+
) => QueryWhere<TFieldValues>
|
|
188
|
+
<
|
|
189
|
+
TFieldValues extends FieldValues,
|
|
190
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
191
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
192
|
+
>(
|
|
193
|
+
path: TFieldName,
|
|
194
|
+
op: "in" | "notIn",
|
|
195
|
+
value: readonly V[]
|
|
196
|
+
): (
|
|
197
|
+
current: Query<TFieldValues>
|
|
198
|
+
) => QueryWhere<TFieldValues>
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export type FilterWhere = {
|
|
202
|
+
<
|
|
203
|
+
TFieldValues extends FieldValues,
|
|
204
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
205
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
206
|
+
>(f: {
|
|
207
|
+
path: TFieldName
|
|
208
|
+
op: Ops
|
|
209
|
+
value: V
|
|
210
|
+
}): (
|
|
211
|
+
current: Query<TFieldValues>
|
|
212
|
+
) => QueryWhere<TFieldValues>
|
|
213
|
+
} & FilterWheres
|
|
214
|
+
|
|
215
|
+
export type FilterContinuations = {
|
|
216
|
+
<
|
|
217
|
+
TFieldValues extends FieldValues,
|
|
218
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
219
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
220
|
+
>(
|
|
221
|
+
path: TFieldName,
|
|
222
|
+
value: V
|
|
223
|
+
): (
|
|
224
|
+
current: QueryWhere<TFieldValues>
|
|
225
|
+
) => QueryWhere<TFieldValues>
|
|
226
|
+
<
|
|
227
|
+
TFieldValues extends FieldValues,
|
|
228
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
229
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
230
|
+
>(
|
|
231
|
+
path: TFieldName,
|
|
232
|
+
op: "neq",
|
|
233
|
+
value: V
|
|
234
|
+
): (
|
|
235
|
+
current: QueryWhere<TFieldValues>
|
|
236
|
+
) => QueryWhere<TFieldValues>
|
|
237
|
+
<
|
|
238
|
+
TFieldValues extends FieldValues,
|
|
239
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
240
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
241
|
+
>(
|
|
242
|
+
path: TFieldName,
|
|
243
|
+
op: "gt" | "gte" | "lt" | "lte",
|
|
244
|
+
value: V // only numbers?
|
|
245
|
+
): (
|
|
246
|
+
current: QueryWhere<TFieldValues>
|
|
247
|
+
) => QueryWhere<TFieldValues>
|
|
248
|
+
<
|
|
249
|
+
TFieldValues extends FieldValues,
|
|
250
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
251
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
252
|
+
>(
|
|
253
|
+
path: TFieldName,
|
|
254
|
+
op: "startsWith" | "endsWith" | "contains" | "notContains" | "notStartsWith" | "notEndsWith",
|
|
255
|
+
value: V // only strings?
|
|
256
|
+
): (
|
|
257
|
+
current: QueryWhere<TFieldValues>
|
|
258
|
+
) => QueryWhere<TFieldValues>
|
|
259
|
+
<
|
|
260
|
+
TFieldValues extends FieldValues,
|
|
261
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
262
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
263
|
+
>(
|
|
264
|
+
path: TFieldName,
|
|
265
|
+
op: "in" | "notIn",
|
|
266
|
+
value: readonly V[]
|
|
267
|
+
): (
|
|
268
|
+
current: QueryWhere<TFieldValues>
|
|
269
|
+
) => QueryWhere<TFieldValues>
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export type FilterContinuation = {
|
|
273
|
+
<TFieldValues extends FieldValues>(
|
|
274
|
+
fb: (
|
|
275
|
+
current: Query<TFieldValues>
|
|
276
|
+
) => QueryWhere<TFieldValues>
|
|
277
|
+
): (
|
|
278
|
+
current: QueryWhere<TFieldValues>
|
|
279
|
+
) => QueryWhere<TFieldValues>
|
|
280
|
+
<
|
|
281
|
+
TFieldValues extends FieldValues,
|
|
282
|
+
TFieldName extends FieldPath<TFieldValues>,
|
|
283
|
+
V extends FieldPathValue<TFieldValues, TFieldName>
|
|
284
|
+
>(f: {
|
|
285
|
+
path: TFieldName
|
|
286
|
+
op: Ops
|
|
287
|
+
value: V
|
|
288
|
+
}): (
|
|
289
|
+
current: QueryWhere<TFieldValues>
|
|
290
|
+
) => QueryWhere<TFieldValues>
|
|
291
|
+
} & FilterContinuations
|
|
292
|
+
|
|
293
|
+
export const make: <TFieldValues extends FieldValues>() => Query<TFieldValues> = () => new Initial()
|
|
294
|
+
export interface A {
|
|
295
|
+
a: string
|
|
296
|
+
b: number
|
|
297
|
+
c: boolean
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const interpret = <TFieldValues extends FieldValues, A = TFieldValues, R = never>(_: QAll<TFieldValues, A, R>) => {
|
|
301
|
+
const values: FilterResult[] = []
|
|
302
|
+
const a = _ as Q<TFieldValues>
|
|
303
|
+
|
|
304
|
+
let schema: S.Schema<A, TFieldValues, R> | undefined = undefined as any
|
|
305
|
+
let limit: number | undefined = undefined as any
|
|
306
|
+
let skip: number | undefined = undefined as any
|
|
307
|
+
|
|
308
|
+
const upd = (v: { schema: any; limit: any; skip: any; filter: readonly FilterResult[] }) => {
|
|
309
|
+
values.push(...v.filter)
|
|
310
|
+
limit = v.limit
|
|
311
|
+
skip = v.skip
|
|
312
|
+
schema = v.schema
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
pipe(
|
|
316
|
+
a,
|
|
317
|
+
Match.valueTags({
|
|
318
|
+
value: () => {
|
|
319
|
+
// values.push(value)
|
|
320
|
+
},
|
|
321
|
+
where: ({ current, operation }) => {
|
|
322
|
+
upd(interpret(current))
|
|
323
|
+
values.push(
|
|
324
|
+
{
|
|
325
|
+
t: "where",
|
|
326
|
+
path: operation[0],
|
|
327
|
+
op: operation.length === 2 ? "eq" : operation[1],
|
|
328
|
+
value: operation.length === 2 ? operation[1] : operation[2]
|
|
329
|
+
}
|
|
330
|
+
)
|
|
331
|
+
},
|
|
332
|
+
and: ({ current, operation }) => {
|
|
333
|
+
upd(interpret(current))
|
|
334
|
+
if (typeof operation === "function") {
|
|
335
|
+
values.push(
|
|
336
|
+
{ t: "and-scope", result: interpret(operation(make())).filter }
|
|
337
|
+
)
|
|
338
|
+
} else {
|
|
339
|
+
values.push(
|
|
340
|
+
{
|
|
341
|
+
t: "and",
|
|
342
|
+
path: operation[0],
|
|
343
|
+
op: operation.length === 2 ? "eq" : operation[1],
|
|
344
|
+
value: operation.length === 2 ? operation[1] : operation[2]
|
|
345
|
+
}
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
or: ({ current, operation }) => {
|
|
350
|
+
upd(interpret(current))
|
|
351
|
+
if (typeof operation === "function") {
|
|
352
|
+
values.push(
|
|
353
|
+
{ t: "or-scope", result: interpret(operation(make())).filter }
|
|
354
|
+
)
|
|
355
|
+
} else {
|
|
356
|
+
values.push(
|
|
357
|
+
{
|
|
358
|
+
t: "or",
|
|
359
|
+
path: operation[0],
|
|
360
|
+
op: operation.length === 2 ? "eq" : operation[1],
|
|
361
|
+
value: operation.length === 2 ? operation[1] : operation[2]
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
order: ({ current }) => {
|
|
367
|
+
upd(interpret(current))
|
|
368
|
+
console.log("missing order implementation")
|
|
369
|
+
},
|
|
370
|
+
page: (v) => {
|
|
371
|
+
upd(interpret(v.current))
|
|
372
|
+
limit = v.limit
|
|
373
|
+
skip = v.skip
|
|
374
|
+
},
|
|
375
|
+
project: (v) => {
|
|
376
|
+
upd(interpret(v.current))
|
|
377
|
+
schema = v.schema
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
// TODO
|
|
383
|
+
return {
|
|
384
|
+
filter: values,
|
|
385
|
+
limit,
|
|
386
|
+
skip,
|
|
387
|
+
schema
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export const query: {
|
|
392
|
+
<
|
|
393
|
+
T extends { id: unknown },
|
|
394
|
+
PM extends PersistenceModelType<string>,
|
|
395
|
+
Evt,
|
|
396
|
+
ItemType extends string,
|
|
397
|
+
A,
|
|
398
|
+
R,
|
|
399
|
+
From extends FieldValues
|
|
400
|
+
>(
|
|
401
|
+
repo: Repository<T, PM, Evt, ItemType>,
|
|
402
|
+
q: QueryProjection<PM extends From ? From : never, A, R>
|
|
403
|
+
): Effect.Effect<readonly A[], S.ParseResult.ParseError, R>
|
|
404
|
+
<
|
|
405
|
+
T extends { id: unknown },
|
|
406
|
+
PM extends PersistenceModelType<string>,
|
|
407
|
+
Evt,
|
|
408
|
+
ItemType extends string,
|
|
409
|
+
A,
|
|
410
|
+
R
|
|
411
|
+
>(
|
|
412
|
+
repo: Repository<T, PM, Evt, ItemType>,
|
|
413
|
+
q: QAll<NoInfer<PM>, A, R>
|
|
414
|
+
): Effect.Effect<readonly T[], never, R>
|
|
415
|
+
} = (<
|
|
416
|
+
T extends { id: unknown },
|
|
417
|
+
PM extends PersistenceModelType<string>,
|
|
418
|
+
Evt,
|
|
419
|
+
ItemType extends string,
|
|
420
|
+
A,
|
|
421
|
+
R
|
|
422
|
+
>(repo: Repository<T, PM, Evt, ItemType>, q: QAll<PM, A, R>) => {
|
|
423
|
+
// TODO: Native interpreter for each db adapter, instead of the intermediate "new-kid" format
|
|
424
|
+
const a = interpret(q)
|
|
425
|
+
const schema = a.schema
|
|
426
|
+
let select: (keyof PM)[] = []
|
|
427
|
+
if (schema) {
|
|
428
|
+
let t = schema.ast
|
|
429
|
+
if (S.AST.isTransform(t)) {
|
|
430
|
+
t = t.from
|
|
431
|
+
}
|
|
432
|
+
if (S.AST.isTypeLiteral(t)) {
|
|
433
|
+
select = t.propertySignatures.map((_) => _.name) as any
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return pipe(
|
|
437
|
+
a.filter.length === 0
|
|
438
|
+
? repo.utils.all
|
|
439
|
+
: repo.utils.filter({
|
|
440
|
+
limit: a.limit,
|
|
441
|
+
skip: a.skip,
|
|
442
|
+
select: select.toNonEmpty.value ?? undefined,
|
|
443
|
+
filter: a.filter.length
|
|
444
|
+
? {
|
|
445
|
+
type: "new-kid" as const,
|
|
446
|
+
build: () => a.filter
|
|
447
|
+
} as any as QueryBuilder<any>
|
|
448
|
+
: undefined
|
|
449
|
+
}),
|
|
450
|
+
Effect.flatMap((_) =>
|
|
451
|
+
Unify.unify(
|
|
452
|
+
schema
|
|
453
|
+
? repo.utils.parseMany2(_, schema as any)
|
|
454
|
+
: repo.utils.parseMany(_)
|
|
455
|
+
)
|
|
456
|
+
)
|
|
457
|
+
)
|
|
458
|
+
}) as any
|