effect-app 4.0.0-beta.248 → 4.0.0-beta.249
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 +9 -1
- package/dist/Emailer.d.ts +51 -0
- package/dist/Emailer.d.ts.map +1 -0
- package/dist/Emailer.js +7 -0
- package/dist/Model/Repository/Registry.d.ts +21 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +18 -0
- package/dist/Model/Repository/ext.d.ts +60 -0
- package/dist/Model/Repository/ext.d.ts.map +1 -0
- package/dist/Model/Repository/ext.js +122 -0
- package/dist/Model/Repository/internal/internal.d.ts +62 -0
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -0
- package/dist/Model/Repository/internal/internal.js +413 -0
- package/dist/Model/Repository/legacy.d.ts +21 -0
- package/dist/Model/Repository/legacy.d.ts.map +1 -0
- package/dist/Model/Repository/legacy.js +2 -0
- package/dist/Model/Repository/makeRepo.d.ts +53 -0
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -0
- package/dist/Model/Repository/makeRepo.js +27 -0
- package/dist/Model/Repository/service.d.ts +97 -0
- package/dist/Model/Repository/service.d.ts.map +1 -0
- package/dist/Model/Repository/service.js +2 -0
- package/dist/Model/Repository/validation.d.ts +71 -0
- package/dist/Model/Repository/validation.d.ts.map +1 -0
- package/dist/Model/Repository/validation.js +32 -0
- package/dist/Model/Repository.d.ts +7 -0
- package/dist/Model/Repository.d.ts.map +1 -0
- package/dist/Model/Repository.js +7 -0
- package/dist/Model/dsl.d.ts +33 -0
- package/dist/Model/dsl.d.ts.map +1 -0
- package/dist/Model/dsl.js +43 -0
- package/dist/Model/filter/filterApi.d.ts +30 -0
- package/dist/Model/filter/filterApi.d.ts.map +1 -0
- package/dist/Model/filter/filterApi.js +2 -0
- package/dist/Model/filter/types/errors.d.ts +29 -0
- package/dist/Model/filter/types/errors.d.ts.map +1 -0
- package/dist/Model/filter/types/errors.js +2 -0
- package/dist/Model/filter/types/fields.d.ts +15 -0
- package/dist/Model/filter/types/fields.d.ts.map +1 -0
- package/dist/Model/filter/types/fields.js +2 -0
- package/dist/Model/filter/types/path/common.d.ts +316 -0
- package/dist/Model/filter/types/path/common.d.ts.map +1 -0
- package/dist/Model/filter/types/path/common.js +2 -0
- package/dist/Model/filter/types/path/eager.d.ts +95 -0
- package/dist/Model/filter/types/path/eager.d.ts.map +1 -0
- package/dist/Model/filter/types/path/eager.js +31 -0
- package/dist/Model/filter/types/path/index.d.ts +4 -0
- package/dist/Model/filter/types/path/index.d.ts.map +1 -0
- package/dist/Model/filter/types/path/index.js +3 -0
- package/dist/Model/filter/types/utils.d.ts +79 -0
- package/dist/Model/filter/types/utils.d.ts.map +1 -0
- package/dist/Model/filter/types/utils.js +2 -0
- package/dist/Model/filter/types/validator.d.ts +30 -0
- package/dist/Model/filter/types/validator.d.ts.map +1 -0
- package/dist/Model/filter/types/validator.js +2 -0
- package/dist/Model/filter/types.d.ts +5 -0
- package/dist/Model/filter/types.d.ts.map +1 -0
- package/dist/Model/filter/types.js +7 -0
- package/dist/Model/query/dsl.d.ts +446 -0
- package/dist/Model/query/dsl.d.ts.map +1 -0
- package/dist/Model/query/dsl.js +342 -0
- package/dist/Model/query/new-kid-interpreter.d.ts +136 -0
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -0
- package/dist/Model/query/new-kid-interpreter.js +336 -0
- package/dist/Model/query.d.ts +15 -0
- package/dist/Model/query.d.ts.map +1 -0
- package/dist/Model/query.js +3 -0
- package/dist/Model.d.ts +5 -0
- package/dist/Model.d.ts.map +1 -0
- package/dist/Model.js +5 -0
- package/dist/QueueMaker.d.ts +13 -0
- package/dist/QueueMaker.d.ts.map +1 -0
- package/dist/QueueMaker.js +4 -0
- package/dist/RequestContext.d.ts +103 -0
- package/dist/RequestContext.d.ts.map +1 -0
- package/dist/RequestContext.js +49 -0
- package/dist/Store.d.ts +147 -0
- package/dist/Store.d.ts.map +1 -0
- package/dist/Store.js +95 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/runtime.d.ts +19 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +40 -0
- package/dist/toast.d.ts +51 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +34 -0
- package/dist/withToast.d.ts +30 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +64 -0
- package/package.json +113 -1
- package/src/Emailer.ts +51 -0
- package/src/Model/Repository/Registry.ts +34 -0
- package/src/Model/Repository/ext.ts +375 -0
- package/src/Model/Repository/internal/internal.ts +708 -0
- package/src/Model/Repository/legacy.ts +29 -0
- package/src/Model/Repository/makeRepo.ts +144 -0
- package/src/Model/Repository/service.ts +639 -0
- package/src/Model/Repository/validation.ts +31 -0
- package/src/Model/Repository.ts +6 -0
- package/src/Model/dsl.ts +129 -0
- package/src/Model/filter/filterApi.ts +60 -0
- package/src/Model/filter/types/errors.ts +47 -0
- package/src/Model/filter/types/fields.ts +50 -0
- package/src/Model/filter/types/path/common.ts +404 -0
- package/src/Model/filter/types/path/eager.ts +297 -0
- package/src/Model/filter/types/path/index.ts +4 -0
- package/src/Model/filter/types/utils.ts +128 -0
- package/src/Model/filter/types/validator.ts +46 -0
- package/src/Model/filter/types.ts +6 -0
- package/src/Model/query/dsl.ts +2546 -0
- package/src/Model/query/new-kid-interpreter.ts +484 -0
- package/src/Model/query.ts +13 -0
- package/src/Model.ts +4 -0
- package/src/QueueMaker.ts +19 -0
- package/src/RequestContext.ts +62 -0
- package/src/Store.ts +243 -0
- package/src/index.ts +2 -0
- package/src/runtime.ts +56 -0
- package/src/toast.ts +54 -0
- package/src/withToast.ts +133 -0
- package/test/dist/rpc-dynamic-middleware.test.d.ts.map +1 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
3
|
+
import { identity, pipe } from "effect/Function"
|
|
4
|
+
import * as Match from "effect/Match"
|
|
5
|
+
import * as SchemaAST from "effect/SchemaAST"
|
|
6
|
+
import * as Array from "../../Array.js"
|
|
7
|
+
import { toNonEmptyArray } from "../../Array.js"
|
|
8
|
+
import * as Option from "../../Option.js"
|
|
9
|
+
import * as S from "../../Schema.js"
|
|
10
|
+
import { dropUndefinedT } from "../../utils.js"
|
|
11
|
+
import type { FilterResult } from "../filter/filterApi.js"
|
|
12
|
+
import type { FieldValues } from "../filter/types.js"
|
|
13
|
+
import type { FieldPath } from "../filter/types/path/eager.js"
|
|
14
|
+
import { make, type Q, type QAll } from "../query/dsl.js"
|
|
15
|
+
|
|
16
|
+
export type AggregateIrExpression =
|
|
17
|
+
| { readonly _tag: "agg-count" }
|
|
18
|
+
| { readonly _tag: "agg-count-when"; readonly filter: readonly FilterResult[] }
|
|
19
|
+
| { readonly _tag: "agg-sum"; readonly field: string }
|
|
20
|
+
| { readonly _tag: "agg-min"; readonly field: string }
|
|
21
|
+
| { readonly _tag: "agg-max"; readonly field: string }
|
|
22
|
+
|
|
23
|
+
export type AggregateIrItem =
|
|
24
|
+
| AggregateIrExpression
|
|
25
|
+
| { readonly _tag: "agg-field"; readonly path: string }
|
|
26
|
+
|
|
27
|
+
export type ComputedProjectionMathIrExpression =
|
|
28
|
+
| {
|
|
29
|
+
readonly _tag: "field"
|
|
30
|
+
readonly field: string
|
|
31
|
+
}
|
|
32
|
+
| {
|
|
33
|
+
readonly _tag: "mul"
|
|
34
|
+
readonly left: ComputedProjectionMathIrExpression
|
|
35
|
+
readonly right: ComputedProjectionMathIrExpression
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ComputedProjectionIrExpression =
|
|
39
|
+
| {
|
|
40
|
+
readonly _tag: "relation-count"
|
|
41
|
+
readonly path: string
|
|
42
|
+
readonly filter: readonly FilterResult[]
|
|
43
|
+
}
|
|
44
|
+
| {
|
|
45
|
+
readonly _tag: "relation-any"
|
|
46
|
+
readonly path: string
|
|
47
|
+
readonly filter: readonly FilterResult[]
|
|
48
|
+
}
|
|
49
|
+
| {
|
|
50
|
+
readonly _tag: "relation-every"
|
|
51
|
+
readonly path: string
|
|
52
|
+
readonly filter: readonly FilterResult[]
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
readonly _tag: "relation-distinct-count"
|
|
56
|
+
readonly path: string
|
|
57
|
+
readonly field: string
|
|
58
|
+
readonly filter: readonly FilterResult[]
|
|
59
|
+
}
|
|
60
|
+
| {
|
|
61
|
+
readonly _tag: "relation-sum"
|
|
62
|
+
readonly path: string
|
|
63
|
+
readonly field: string
|
|
64
|
+
readonly filter: readonly FilterResult[]
|
|
65
|
+
}
|
|
66
|
+
| {
|
|
67
|
+
readonly _tag: "relation-sum-expr"
|
|
68
|
+
readonly path: string
|
|
69
|
+
readonly expression: ComputedProjectionMathIrExpression
|
|
70
|
+
readonly filter: readonly FilterResult[]
|
|
71
|
+
}
|
|
72
|
+
| {
|
|
73
|
+
readonly _tag: "relation-sum-expr-by"
|
|
74
|
+
readonly path: string
|
|
75
|
+
readonly expression: ComputedProjectionMathIrExpression
|
|
76
|
+
readonly unit: string
|
|
77
|
+
readonly filter: readonly FilterResult[]
|
|
78
|
+
}
|
|
79
|
+
| {
|
|
80
|
+
readonly _tag: "relation-sum-expr-normalized"
|
|
81
|
+
readonly path: string
|
|
82
|
+
readonly expression: ComputedProjectionMathIrExpression
|
|
83
|
+
readonly unit: string
|
|
84
|
+
readonly toBase: string
|
|
85
|
+
readonly factors: Readonly<Record<string, number>>
|
|
86
|
+
readonly filter: readonly FilterResult[]
|
|
87
|
+
}
|
|
88
|
+
| {
|
|
89
|
+
readonly _tag: "relation-collect"
|
|
90
|
+
readonly path: string
|
|
91
|
+
readonly field: string
|
|
92
|
+
readonly distinct: boolean
|
|
93
|
+
readonly filter: readonly FilterResult[]
|
|
94
|
+
}
|
|
95
|
+
| {
|
|
96
|
+
readonly _tag: "relation-collect-fields"
|
|
97
|
+
readonly path: string
|
|
98
|
+
readonly fields: readonly string[]
|
|
99
|
+
readonly distinct: boolean
|
|
100
|
+
readonly filter: readonly FilterResult[]
|
|
101
|
+
}
|
|
102
|
+
| {
|
|
103
|
+
readonly _tag: "relation-length"
|
|
104
|
+
readonly path: string
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
type Result<TFieldValues extends FieldValues, A = TFieldValues, R = never> = {
|
|
108
|
+
filter: FilterResult[]
|
|
109
|
+
schema: S.Codec<A, TFieldValues, R> | undefined
|
|
110
|
+
limit: number | undefined
|
|
111
|
+
skip: number | undefined
|
|
112
|
+
order: { key: FieldPath<TFieldValues>; direction: "ASC" | "DESC" }[]
|
|
113
|
+
ttype: "one" | "many" | "count" | undefined
|
|
114
|
+
mode: "collect" | "project" | "transform" | "aggregate" | undefined
|
|
115
|
+
computed: Record<string, ComputedProjectionIrExpression> | undefined
|
|
116
|
+
aggregateMap: Record<string, AggregateIrItem> | undefined
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const interpret = <
|
|
120
|
+
TFieldValues extends FieldValues,
|
|
121
|
+
TFieldValuesRefined extends TFieldValues = TFieldValues,
|
|
122
|
+
A = TFieldValues,
|
|
123
|
+
R = never
|
|
124
|
+
>(_: QAll<TFieldValues, TFieldValuesRefined, A, R>) => {
|
|
125
|
+
const a = _ as Q<TFieldValues>
|
|
126
|
+
|
|
127
|
+
const data: Result<TFieldValues, any, any> = {
|
|
128
|
+
filter: [],
|
|
129
|
+
schema: undefined,
|
|
130
|
+
limit: undefined,
|
|
131
|
+
skip: undefined,
|
|
132
|
+
order: [],
|
|
133
|
+
ttype: undefined,
|
|
134
|
+
mode: undefined,
|
|
135
|
+
computed: undefined,
|
|
136
|
+
aggregateMap: undefined
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const upd = (
|
|
140
|
+
v: Result<TFieldValues, any, any>
|
|
141
|
+
) => {
|
|
142
|
+
data.filter.push(...v.filter)
|
|
143
|
+
data.order.push(...v.order)
|
|
144
|
+
if (v.limit !== undefined) data.limit = v.limit
|
|
145
|
+
if (v.skip !== undefined) data.skip = v.skip
|
|
146
|
+
if (v.ttype !== undefined) data.ttype = v.ttype
|
|
147
|
+
if (v.schema !== undefined) data.schema = v.schema
|
|
148
|
+
if (v.mode !== undefined) data.mode = v.mode
|
|
149
|
+
if (v.computed !== undefined) data.computed = v.computed
|
|
150
|
+
if (v.aggregateMap !== undefined) data.aggregateMap = v.aggregateMap
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const applyPath = (path: string) => (_: FilterResult): FilterResult =>
|
|
154
|
+
_.t === "where" || _.t === "and" || _.t === "or"
|
|
155
|
+
? { ..._, path: `${path}.-1.${_.path}` }
|
|
156
|
+
: { ..._, result: _.result.map(applyPath(path)) }
|
|
157
|
+
|
|
158
|
+
pipe(
|
|
159
|
+
a,
|
|
160
|
+
Match.valueTags({
|
|
161
|
+
value: () => {
|
|
162
|
+
// data.filter.push(value)
|
|
163
|
+
},
|
|
164
|
+
where: ({ current, operation, relation, subPath }) => {
|
|
165
|
+
upd(interpret(current))
|
|
166
|
+
if (typeof operation === "function") {
|
|
167
|
+
data.filter.push(
|
|
168
|
+
{
|
|
169
|
+
t: "where-scope",
|
|
170
|
+
result: interpret(operation(make())).filter.map(subPath ? applyPath(subPath) : identity),
|
|
171
|
+
relation
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
} else {
|
|
175
|
+
data.filter.push(
|
|
176
|
+
{
|
|
177
|
+
t: "where",
|
|
178
|
+
path: operation[0],
|
|
179
|
+
op: operation.length === 2 ? "eq" : operation[1],
|
|
180
|
+
value: operation.length === 2 ? operation[1] : operation[2]
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
and: ({ current, operation, relation }) => {
|
|
186
|
+
upd(interpret(current))
|
|
187
|
+
if (typeof operation === "function") {
|
|
188
|
+
data.filter.push(
|
|
189
|
+
{ t: "and-scope", result: interpret(operation(make())).filter, relation }
|
|
190
|
+
)
|
|
191
|
+
} else {
|
|
192
|
+
data.filter.push(
|
|
193
|
+
{
|
|
194
|
+
t: "and",
|
|
195
|
+
path: operation[0],
|
|
196
|
+
op: operation.length === 2 ? "eq" : operation[1],
|
|
197
|
+
value: operation.length === 2 ? operation[1] : operation[2]
|
|
198
|
+
}
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
or: ({ current, operation, relation }) => {
|
|
203
|
+
upd(interpret(current))
|
|
204
|
+
if (typeof operation === "function") {
|
|
205
|
+
data.filter.push(
|
|
206
|
+
{ t: "or-scope", result: interpret(operation(make())).filter, relation }
|
|
207
|
+
)
|
|
208
|
+
} else {
|
|
209
|
+
data.filter.push(
|
|
210
|
+
{
|
|
211
|
+
t: "or",
|
|
212
|
+
path: operation[0],
|
|
213
|
+
op: operation.length === 2 ? "eq" : operation[1],
|
|
214
|
+
value: operation.length === 2 ? operation[1] : operation[2]
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
one: ({ current }) => {
|
|
220
|
+
upd(interpret(current))
|
|
221
|
+
data.limit = 1
|
|
222
|
+
data.ttype = "one"
|
|
223
|
+
},
|
|
224
|
+
count: ({ current }) => {
|
|
225
|
+
upd(interpret(current))
|
|
226
|
+
data.ttype = "count"
|
|
227
|
+
data.schema = S.Struct({ id: S.String }) as any
|
|
228
|
+
},
|
|
229
|
+
order: ({ current, direction, field }) => {
|
|
230
|
+
upd(interpret(current))
|
|
231
|
+
data.order.push({ key: field, direction })
|
|
232
|
+
},
|
|
233
|
+
page: (v) => {
|
|
234
|
+
upd(interpret(v.current))
|
|
235
|
+
data.limit = v.take
|
|
236
|
+
data.skip = v.skip
|
|
237
|
+
},
|
|
238
|
+
project: (v) => {
|
|
239
|
+
upd(interpret(v.current))
|
|
240
|
+
if (v.mode === "aggregate" && v.aggregateMap) {
|
|
241
|
+
data.schema = v.schema
|
|
242
|
+
data.mode = "aggregate"
|
|
243
|
+
data.aggregateMap = Object.fromEntries(
|
|
244
|
+
Object.entries(v.aggregateMap).map(([key, expression]) => {
|
|
245
|
+
switch (expression._tag) {
|
|
246
|
+
case "agg-field":
|
|
247
|
+
return [key, { _tag: "agg-field" as const, path: expression.path }]
|
|
248
|
+
case "agg-count":
|
|
249
|
+
return [key, { _tag: "agg-count" as const }]
|
|
250
|
+
case "agg-count-when": {
|
|
251
|
+
const filter = interpret(expression.operation(make())).filter
|
|
252
|
+
return [key, { _tag: "agg-count-when" as const, filter }]
|
|
253
|
+
}
|
|
254
|
+
case "agg-sum":
|
|
255
|
+
return [key, { _tag: "agg-sum" as const, field: expression.field }]
|
|
256
|
+
case "agg-min":
|
|
257
|
+
return [key, { _tag: "agg-min" as const, field: expression.field }]
|
|
258
|
+
case "agg-max":
|
|
259
|
+
return [key, { _tag: "agg-max" as const, field: expression.field }]
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
)
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
if (v.computed && v.mode === "transform") {
|
|
266
|
+
throw new Error("Computed projections require mode 'project' or 'collect', not 'transform'")
|
|
267
|
+
}
|
|
268
|
+
data.schema = v.schema
|
|
269
|
+
data.mode = v.computed
|
|
270
|
+
? v.mode === "collect" ? "collect" : "project"
|
|
271
|
+
: v.mode
|
|
272
|
+
data.computed = v.computed
|
|
273
|
+
? Object.fromEntries(
|
|
274
|
+
Object.entries(v.computed).map(([key, expression]) => {
|
|
275
|
+
const e = expression
|
|
276
|
+
const op = "operation" in e ? e.operation : undefined
|
|
277
|
+
const filter = op ? interpret(op(make())).filter.map(applyPath(e.path)) : []
|
|
278
|
+
switch (e._tag) {
|
|
279
|
+
case "relation-count":
|
|
280
|
+
case "relation-any":
|
|
281
|
+
case "relation-every":
|
|
282
|
+
return [key, { _tag: e._tag, path: e.path, filter } as ComputedProjectionIrExpression]
|
|
283
|
+
case "relation-distinct-count":
|
|
284
|
+
case "relation-sum":
|
|
285
|
+
return [
|
|
286
|
+
key,
|
|
287
|
+
{ _tag: e._tag, path: e.path, field: e.field, filter } as ComputedProjectionIrExpression
|
|
288
|
+
]
|
|
289
|
+
case "relation-sum-expr":
|
|
290
|
+
return [
|
|
291
|
+
key,
|
|
292
|
+
{ _tag: e._tag, path: e.path, expression: e.expression, filter } as ComputedProjectionIrExpression
|
|
293
|
+
]
|
|
294
|
+
case "relation-sum-expr-by":
|
|
295
|
+
return [
|
|
296
|
+
key,
|
|
297
|
+
{
|
|
298
|
+
_tag: e._tag,
|
|
299
|
+
path: e.path,
|
|
300
|
+
expression: e.expression,
|
|
301
|
+
unit: e.unit,
|
|
302
|
+
filter
|
|
303
|
+
} as ComputedProjectionIrExpression
|
|
304
|
+
]
|
|
305
|
+
case "relation-sum-expr-normalized":
|
|
306
|
+
return [
|
|
307
|
+
key,
|
|
308
|
+
{
|
|
309
|
+
_tag: e._tag,
|
|
310
|
+
path: e.path,
|
|
311
|
+
expression: e.expression,
|
|
312
|
+
unit: e.unit,
|
|
313
|
+
toBase: e.toBase,
|
|
314
|
+
factors: e.factors,
|
|
315
|
+
filter
|
|
316
|
+
} as ComputedProjectionIrExpression
|
|
317
|
+
]
|
|
318
|
+
case "relation-collect":
|
|
319
|
+
return [
|
|
320
|
+
key,
|
|
321
|
+
{
|
|
322
|
+
_tag: e._tag,
|
|
323
|
+
path: e.path,
|
|
324
|
+
field: e.field,
|
|
325
|
+
distinct: e.distinct,
|
|
326
|
+
filter
|
|
327
|
+
} as ComputedProjectionIrExpression
|
|
328
|
+
]
|
|
329
|
+
case "relation-collect-fields":
|
|
330
|
+
return [
|
|
331
|
+
key,
|
|
332
|
+
{
|
|
333
|
+
_tag: e._tag,
|
|
334
|
+
path: e.path,
|
|
335
|
+
fields: e.fields,
|
|
336
|
+
distinct: e.distinct,
|
|
337
|
+
filter
|
|
338
|
+
} as ComputedProjectionIrExpression
|
|
339
|
+
]
|
|
340
|
+
case "relation-length":
|
|
341
|
+
return [key, { _tag: e._tag, path: e.path } as ComputedProjectionIrExpression]
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
)
|
|
345
|
+
: undefined
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
return data
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const walkTransformation = (t: S.AST.AST): S.AST.AST => {
|
|
354
|
+
if (S.AST.isDeclaration(t) && t.typeParameters.length > 0) {
|
|
355
|
+
return walkTransformation(t.typeParameters[0]!)
|
|
356
|
+
}
|
|
357
|
+
return t
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export const toFilter = <
|
|
361
|
+
TFieldValues extends FieldValues,
|
|
362
|
+
A,
|
|
363
|
+
R,
|
|
364
|
+
TFieldValuesRefined extends TFieldValues = TFieldValues
|
|
365
|
+
>(
|
|
366
|
+
q: QAll<TFieldValues, TFieldValuesRefined, A, R>,
|
|
367
|
+
baseSchema?: S.Schema<unknown>
|
|
368
|
+
) => {
|
|
369
|
+
// TODO: Native interpreter for each db adapter, instead of the intermediate "new-kid" format
|
|
370
|
+
const a = interpret(q)
|
|
371
|
+
|
|
372
|
+
// Aggregate mode: build select entirely from aggregateMap (no schema-driven field list)
|
|
373
|
+
if (a.mode === "aggregate" && a.aggregateMap) {
|
|
374
|
+
const aggSelect = Object.entries(a.aggregateMap).map(([key, item]) => {
|
|
375
|
+
if (item._tag === "agg-field") {
|
|
376
|
+
return { key, path: item.path }
|
|
377
|
+
}
|
|
378
|
+
return { key, aggregate: item }
|
|
379
|
+
})
|
|
380
|
+
return dropUndefinedT({
|
|
381
|
+
t: null as unknown as TFieldValues,
|
|
382
|
+
limit: a.limit,
|
|
383
|
+
skip: a.skip,
|
|
384
|
+
select: Option.getOrUndefined(toNonEmptyArray(aggSelect)) as any,
|
|
385
|
+
schema: a.schema,
|
|
386
|
+
computed: undefined,
|
|
387
|
+
order: Option.getOrUndefined(toNonEmptyArray(a.order)),
|
|
388
|
+
ttype: a.ttype,
|
|
389
|
+
mode: "aggregate" as const,
|
|
390
|
+
filter: a.filter.length ? a.filter : undefined
|
|
391
|
+
})
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const schema = a.schema
|
|
395
|
+
let select: (keyof TFieldValues | { key: string; subKeys: string[] } | {
|
|
396
|
+
key: string
|
|
397
|
+
computed: ComputedProjectionIrExpression
|
|
398
|
+
})[] = []
|
|
399
|
+
// TODO: support more complex (nested) schemas?
|
|
400
|
+
if (schema) {
|
|
401
|
+
const t = walkTransformation(SchemaAST.toEncoded(schema.ast))
|
|
402
|
+
if (S.AST.isObjects(t)) {
|
|
403
|
+
select = t.propertySignatures.map((_) => _.name as string)
|
|
404
|
+
for (const prop of t.propertySignatures) {
|
|
405
|
+
if (S.AST.isArrays(prop.type)) {
|
|
406
|
+
// make sure we only select when there are actually type literals in the tuple...
|
|
407
|
+
// otherwise we might be dealing with strings etc.
|
|
408
|
+
// TODO; be more strict, can't support arrays with unions that have non TypeLiteral members etc..
|
|
409
|
+
const arraySelect = {
|
|
410
|
+
key: prop.name as string,
|
|
411
|
+
subKeys: Array.flatMap(
|
|
412
|
+
prop.type.rest,
|
|
413
|
+
(x) => {
|
|
414
|
+
const t = walkTransformation(x)
|
|
415
|
+
return S.AST.isObjects(t) ? t.propertySignatures.map((y) => y.name as string) : []
|
|
416
|
+
}
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
if (arraySelect.subKeys.length > 0) {
|
|
420
|
+
select.push(arraySelect)
|
|
421
|
+
// make sure we don't double select?
|
|
422
|
+
if (select.includes(prop.name as string)) {
|
|
423
|
+
select.splice(select.indexOf(prop.name as string), 1)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const computed = a.computed
|
|
431
|
+
const getSelectKey = (_: (typeof select)[number]) => {
|
|
432
|
+
if (typeof _ === "string") {
|
|
433
|
+
return _
|
|
434
|
+
}
|
|
435
|
+
if (typeof _ === "object" && _ !== null && "key" in _) {
|
|
436
|
+
return _.key
|
|
437
|
+
}
|
|
438
|
+
return String(_)
|
|
439
|
+
}
|
|
440
|
+
const schemaKeys = select.map(getSelectKey)
|
|
441
|
+
const nonEncodedSchemaKeys = (() => {
|
|
442
|
+
if (!baseSchema) {
|
|
443
|
+
return [] as string[]
|
|
444
|
+
}
|
|
445
|
+
const encoded = walkTransformation(SchemaAST.toEncoded(baseSchema.ast))
|
|
446
|
+
if (!S.AST.isObjects(encoded)) {
|
|
447
|
+
return [] as string[]
|
|
448
|
+
}
|
|
449
|
+
const encodedKeys = encoded.propertySignatures.map((_) => _.name as string)
|
|
450
|
+
return schemaKeys.filter((key) => !encodedKeys.includes(key))
|
|
451
|
+
})()
|
|
452
|
+
const missingComputedKeys = nonEncodedSchemaKeys.filter((key) => !(computed && key in computed))
|
|
453
|
+
|
|
454
|
+
if (Array.isArrayNonEmpty(missingComputedKeys)) {
|
|
455
|
+
throw new Error(`Missing computed projections for schema keys: ${missingComputedKeys.join(", ")}`)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (computed) {
|
|
459
|
+
const computedKeys = Object.keys(computed)
|
|
460
|
+
const extraComputedKeys = computedKeys.filter((key) => !schemaKeys.includes(key))
|
|
461
|
+
if (Array.isArrayNonEmpty(extraComputedKeys)) {
|
|
462
|
+
throw new Error(`Computed projection keys must exist in projection schema: ${extraComputedKeys.join(", ")}`)
|
|
463
|
+
}
|
|
464
|
+
select = select.filter((_) => {
|
|
465
|
+
const key = getSelectKey(_)
|
|
466
|
+
return !(key in computed)
|
|
467
|
+
})
|
|
468
|
+
select.push(...Object.entries(computed).map(([key, expression]) => ({ key, computed: expression })))
|
|
469
|
+
}
|
|
470
|
+
return dropUndefinedT({
|
|
471
|
+
t: null as unknown as TFieldValues,
|
|
472
|
+
limit: a.limit,
|
|
473
|
+
skip: a.skip,
|
|
474
|
+
select: Option.getOrUndefined(toNonEmptyArray(select)),
|
|
475
|
+
schema,
|
|
476
|
+
computed,
|
|
477
|
+
order: Option.getOrUndefined(toNonEmptyArray(a.order)),
|
|
478
|
+
ttype: a.ttype,
|
|
479
|
+
mode: a.mode ?? "transform",
|
|
480
|
+
filter: a.filter.length
|
|
481
|
+
? a.filter
|
|
482
|
+
: undefined
|
|
483
|
+
})
|
|
484
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./query/dsl.js"
|
|
2
|
+
export * from "./query/new-kid-interpreter.js"
|
|
3
|
+
|
|
4
|
+
export interface RawQuery<Encoded, Out> {
|
|
5
|
+
cosmos: (vals: { name: string }) => {
|
|
6
|
+
query: string
|
|
7
|
+
parameters: {
|
|
8
|
+
name: string
|
|
9
|
+
value: any
|
|
10
|
+
}[]
|
|
11
|
+
}
|
|
12
|
+
memory: (t: readonly Encoded[]) => readonly Out[]
|
|
13
|
+
}
|
package/src/Model.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type * as Scope from "effect/Scope"
|
|
2
|
+
import type { NonEmptyReadonlyArray } from "./Array.js"
|
|
3
|
+
import type * as Effect from "./Effect.js"
|
|
4
|
+
import { RequestContext } from "./RequestContext.js"
|
|
5
|
+
|
|
6
|
+
export interface QueueBase<Evt, DrainEvt> {
|
|
7
|
+
drain: <DrainE, DrainR>(
|
|
8
|
+
makeHandleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
|
|
9
|
+
sessionId?: string
|
|
10
|
+
) => Effect.Effect<never, never, Scope.Scope | DrainR>
|
|
11
|
+
publish: (
|
|
12
|
+
...messages: NonEmptyReadonlyArray<Evt>
|
|
13
|
+
) => Effect.Effect<void>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface QueueMakerOps {}
|
|
17
|
+
export const QueueMaker: QueueMakerOps = {}
|
|
18
|
+
|
|
19
|
+
export const QueueMeta = RequestContext
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as Context from "./Context.js"
|
|
2
|
+
import { UserProfileId } from "./ids.js"
|
|
3
|
+
import * as S from "./Schema.js"
|
|
4
|
+
import { NonEmptyString255 } from "./Schema.js"
|
|
5
|
+
|
|
6
|
+
export const Locale = S.Literals(["en", "de"])
|
|
7
|
+
export type Locale = typeof Locale.Type
|
|
8
|
+
|
|
9
|
+
export class LocaleRef extends Context.Reference("Locale", { defaultValue: (): Locale => "en" }) {}
|
|
10
|
+
|
|
11
|
+
export class RequestContext extends S.Opaque<
|
|
12
|
+
RequestContext,
|
|
13
|
+
RequestContext.Encoded
|
|
14
|
+
>()(S.Struct({
|
|
15
|
+
span: S.Struct({
|
|
16
|
+
traceId: S.String,
|
|
17
|
+
spanId: S.String,
|
|
18
|
+
sampled: S.Boolean
|
|
19
|
+
}),
|
|
20
|
+
name: NonEmptyString255,
|
|
21
|
+
locale: Locale,
|
|
22
|
+
sourceId: S.optional(NonEmptyString255), // TODO?
|
|
23
|
+
namespace: NonEmptyString255,
|
|
24
|
+
/** @deprecated */
|
|
25
|
+
userProfile: S.optional(S.Struct({ sub: UserProfileId })) //
|
|
26
|
+
})) {
|
|
27
|
+
// static Tag = Context.Tag<RequestContext>()
|
|
28
|
+
|
|
29
|
+
static toMonitoring(this: void, self: RequestContext) {
|
|
30
|
+
return {
|
|
31
|
+
operationName: self.name,
|
|
32
|
+
locale: self.locale
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const spanAttributes = (ctx: Pick<RequestContext, "locale" | "namespace"> & Partial<RequestContext>) => ({
|
|
38
|
+
"code.function.name": ctx.name,
|
|
39
|
+
"app.locale": ctx.locale,
|
|
40
|
+
"app.tenant.id": ctx.namespace,
|
|
41
|
+
...ctx.sourceId ? { "client.id": ctx.sourceId } : {},
|
|
42
|
+
...(ctx.userProfile?.sub
|
|
43
|
+
? {
|
|
44
|
+
"user.id": ctx
|
|
45
|
+
.userProfile
|
|
46
|
+
.sub,
|
|
47
|
+
"user.roles": "roles" in ctx
|
|
48
|
+
.userProfile
|
|
49
|
+
? ctx.userProfile.roles
|
|
50
|
+
: undefined
|
|
51
|
+
}
|
|
52
|
+
: {})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// codegen:start {preset: model}
|
|
56
|
+
//
|
|
57
|
+
export namespace RequestContext {
|
|
58
|
+
export interface Encoded extends S.StructNestedEncoded<typeof RequestContext> {}
|
|
59
|
+
}
|
|
60
|
+
//
|
|
61
|
+
// codegen:end
|
|
62
|
+
//
|