appflare 0.2.4 → 0.2.7
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/cli/commands/index.ts +98 -4
- package/cli/generate.ts +8 -0
- package/cli/index.ts +32 -1
- package/cli/templates/auth/config.ts +0 -2
- package/cli/templates/core/handlers.route.ts +1 -0
- package/cli/templates/core/imports.ts +1 -0
- package/cli/templates/dashboard/builders/functions/execute-handler.ts +124 -0
- package/cli/templates/dashboard/builders/functions/index.ts +22 -0
- package/cli/templates/dashboard/builders/functions/render-page/header.ts +20 -0
- package/cli/templates/dashboard/builders/functions/render-page/index.ts +33 -0
- package/cli/templates/dashboard/builders/functions/render-page/request-panel.ts +67 -0
- package/cli/templates/dashboard/builders/functions/render-page/result-panel.ts +19 -0
- package/cli/templates/dashboard/builders/functions/render-page/scripts.ts +17 -0
- package/cli/templates/dashboard/builders/navigation.ts +122 -0
- package/cli/templates/dashboard/builders/storage/index.ts +13 -0
- package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -0
- package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -0
- package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -0
- package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -0
- package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -0
- package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -0
- package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -0
- package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -0
- package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -0
- package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -0
- package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -0
- package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -0
- package/cli/templates/dashboard/builders/table-routes/fragments.ts +214 -0
- package/cli/templates/dashboard/builders/table-routes/helpers.ts +49 -0
- package/cli/templates/dashboard/builders/table-routes/index.ts +8 -0
- package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -0
- package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +166 -0
- package/cli/templates/dashboard/builders/table-routes/table/index.ts +77 -0
- package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +111 -0
- package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -0
- package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -0
- package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -0
- package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -0
- package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +127 -0
- package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -0
- package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -0
- package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -0
- package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -0
- package/cli/templates/dashboard/components/dashboard-home.ts +23 -0
- package/cli/templates/dashboard/components/layout.ts +388 -0
- package/cli/templates/dashboard/components/login-page.ts +65 -0
- package/cli/templates/dashboard/index.ts +61 -0
- package/cli/templates/dashboard/types.ts +9 -0
- package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +1 -1
- package/cli/templates/handlers/generators/types/core.ts +5 -0
- package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +168 -0
- package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +133 -0
- package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +686 -0
- package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +97 -0
- package/cli/templates/handlers/generators/types/query-definitions.ts +11 -1083
- package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -0
- package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +164 -0
- package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +85 -0
- package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -0
- package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +137 -0
- package/cli/templates/handlers/generators/types/query-runtime.ts +13 -431
- package/cli/utils/schema-discovery.ts +10 -1
- package/package.json +1 -1
- package/test-better-auth-hash.ts +2 -0
|
@@ -1,1085 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export const createDb = (database: D1Database) =>
|
|
8
|
-
drizzle(database, { schema: mergedSchema });
|
|
9
|
-
|
|
10
|
-
export type AppflareDb = ReturnType<typeof createDb>;
|
|
11
|
-
|
|
12
|
-
type QueryTables = AppflareDb["query"];
|
|
13
|
-
type TableName = Extract<keyof QueryTables, string>;
|
|
14
|
-
type TableQuery<TName extends TableName> = QueryTables[TName];
|
|
15
|
-
type TableFindManyArgs<TName extends TableName> = TableQuery<TName> extends {
|
|
16
|
-
findMany: (args?: infer TArgs) => Promise<unknown>;
|
|
17
|
-
}
|
|
18
|
-
? TArgs
|
|
19
|
-
: never;
|
|
20
|
-
type TableFindManyMethod<TName extends TableName> = TableQuery<TName> extends {
|
|
21
|
-
findMany: infer TMethod;
|
|
22
|
-
}
|
|
23
|
-
? TMethod
|
|
24
|
-
: never;
|
|
25
|
-
type NativeFindManyWith<TName extends TableName> =
|
|
26
|
-
NonNullable<TableFindManyArgs<TName>> extends { with?: infer TWith }
|
|
27
|
-
? TWith
|
|
28
|
-
: never;
|
|
29
|
-
type ResolveNativeFindManyWith<
|
|
30
|
-
TName extends TableName,
|
|
31
|
-
TArgs extends QueryFindManyArgs<TName> | undefined,
|
|
32
|
-
> = TArgs extends { with?: infer TWith }
|
|
33
|
-
? Extract<TWith, NativeFindManyWith<TName>>
|
|
34
|
-
: never;
|
|
35
|
-
type ResolveTableFindManyArgs<
|
|
36
|
-
TName extends TableName,
|
|
37
|
-
TArgs extends QueryFindManyArgs<TName> | undefined,
|
|
38
|
-
> = TArgs extends undefined
|
|
39
|
-
? undefined
|
|
40
|
-
: Omit<NonNullable<TableFindManyArgs<TName>>, "where" | "with"> &
|
|
41
|
-
(ResolveNativeFindManyWith<TName, TArgs> extends never
|
|
42
|
-
? {}
|
|
43
|
-
: { with?: ResolveNativeFindManyWith<TName, TArgs> });
|
|
44
|
-
type TableFindManyResult<
|
|
45
|
-
TName extends TableName,
|
|
46
|
-
TArgs extends QueryFindManyArgs<TName> | undefined =
|
|
47
|
-
| QueryFindManyArgs<TName>
|
|
48
|
-
| undefined,
|
|
49
|
-
> = TableFindManyMethod<TName> extends (
|
|
50
|
-
args?: ResolveTableFindManyArgs<TName, TArgs>,
|
|
51
|
-
) => infer TResult
|
|
52
|
-
? TResult
|
|
53
|
-
: TableFindManyMethod<TName> extends (args?: unknown) => infer TResult
|
|
54
|
-
? TResult
|
|
55
|
-
: Promise<Array<TableModel<TName>>>;
|
|
56
|
-
type TableFindFirstArgs<TName extends TableName> = TableQuery<TName> extends {
|
|
57
|
-
findFirst: (args?: infer TArgs) => Promise<unknown>;
|
|
58
|
-
}
|
|
59
|
-
? TArgs
|
|
60
|
-
: never;
|
|
61
|
-
type TableFindFirstMethod<TName extends TableName> = TableQuery<TName> extends {
|
|
62
|
-
findFirst: infer TMethod;
|
|
63
|
-
}
|
|
64
|
-
? TMethod
|
|
65
|
-
: never;
|
|
66
|
-
type NativeFindFirstWith<TName extends TableName> =
|
|
67
|
-
NonNullable<TableFindFirstArgs<TName>> extends { with?: infer TWith }
|
|
68
|
-
? TWith
|
|
69
|
-
: never;
|
|
70
|
-
type ResolveNativeFindFirstWith<
|
|
71
|
-
TName extends TableName,
|
|
72
|
-
TArgs extends QueryFindFirstArgs<TName> | undefined,
|
|
73
|
-
> = TArgs extends { with?: infer TWith }
|
|
74
|
-
? Extract<TWith, NativeFindFirstWith<TName>>
|
|
75
|
-
: never;
|
|
76
|
-
type ResolveTableFindFirstArgs<
|
|
77
|
-
TName extends TableName,
|
|
78
|
-
TArgs extends QueryFindFirstArgs<TName> | undefined,
|
|
79
|
-
> = TArgs extends undefined
|
|
80
|
-
? undefined
|
|
81
|
-
: Omit<NonNullable<TableFindFirstArgs<TName>>, "where" | "with"> &
|
|
82
|
-
(ResolveNativeFindFirstWith<TName, TArgs> extends never
|
|
83
|
-
? {}
|
|
84
|
-
: { with?: ResolveNativeFindFirstWith<TName, TArgs> });
|
|
85
|
-
type TableFindFirstResult<
|
|
86
|
-
TName extends TableName,
|
|
87
|
-
TArgs extends QueryFindFirstArgs<TName> | undefined =
|
|
88
|
-
| QueryFindFirstArgs<TName>
|
|
89
|
-
| undefined,
|
|
90
|
-
> = TableFindFirstMethod<TName> extends (
|
|
91
|
-
args?: ResolveTableFindFirstArgs<TName, TArgs>,
|
|
92
|
-
) => infer TResult
|
|
93
|
-
? TResult
|
|
94
|
-
: TableFindFirstMethod<TName> extends (args?: unknown) => infer TResult
|
|
95
|
-
? TResult
|
|
96
|
-
: Promise<TableModel<TName> | null>;
|
|
97
|
-
type TableModel<TName extends TableName> = InferSelectModel<
|
|
98
|
-
(typeof mergedSchema)[TName]
|
|
99
|
-
>;
|
|
100
|
-
type TableInsertModel<TName extends TableName> = InferInsertModel<
|
|
101
|
-
(typeof mergedSchema)[TName]
|
|
102
|
-
>;
|
|
103
|
-
|
|
104
|
-
type Primitive = string | number | boolean | Date;
|
|
105
|
-
type NonNil<T> = Exclude<T, null | undefined>;
|
|
106
|
-
type Friendly<T> = T extends Date ? Date | number : T;
|
|
107
|
-
type Comparable<T> = Friendly<Extract<NonNil<T>, Primitive>>;
|
|
108
|
-
type RegexOperand<T> = T extends string ? string : never;
|
|
109
|
-
|
|
110
|
-
type GeoPoint = {
|
|
111
|
-
latitude: number;
|
|
112
|
-
longitude: number;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
type GeoCoordinates = {
|
|
116
|
-
type?: "Point";
|
|
117
|
-
coordinates: [number, number] | readonly [number, number];
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
type GeoWithinOperand = {
|
|
121
|
-
$geometry: GeoPoint | GeoCoordinates;
|
|
122
|
-
latitudeField?: string;
|
|
123
|
-
longitudeField?: string;
|
|
124
|
-
$minDistance?: number;
|
|
125
|
-
$maxDistance?: number;
|
|
126
|
-
gt?: number;
|
|
127
|
-
gte?: number;
|
|
128
|
-
lt?: number;
|
|
129
|
-
lte?: number;
|
|
130
|
-
$gt?: number;
|
|
131
|
-
$gte?: number;
|
|
132
|
-
$lt?: number;
|
|
133
|
-
$lte?: number;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
type GeoWithinOperandForField<TFieldKey extends string> = Omit<
|
|
137
|
-
GeoWithinOperand,
|
|
138
|
-
"latitudeField" | "longitudeField"
|
|
139
|
-
> & {
|
|
140
|
-
latitudeField?: TFieldKey;
|
|
141
|
-
longitudeField?: TFieldKey;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
type FieldOperators<T, TFieldKey extends string = string> = {
|
|
145
|
-
eq?: Friendly<NonNil<T>>;
|
|
146
|
-
$eq?: Friendly<NonNil<T>>;
|
|
147
|
-
ne?: Friendly<NonNil<T>>;
|
|
148
|
-
$ne?: Friendly<NonNil<T>>;
|
|
149
|
-
in?: ReadonlyArray<Friendly<NonNil<T>>>;
|
|
150
|
-
$in?: ReadonlyArray<Friendly<NonNil<T>>>;
|
|
151
|
-
nin?: ReadonlyArray<Friendly<NonNil<T>>>;
|
|
152
|
-
$nin?: ReadonlyArray<Friendly<NonNil<T>>>;
|
|
153
|
-
gt?: Comparable<T>;
|
|
154
|
-
$gt?: Comparable<T>;
|
|
155
|
-
gte?: Comparable<T>;
|
|
156
|
-
$gte?: Comparable<T>;
|
|
157
|
-
lt?: Comparable<T>;
|
|
158
|
-
$lt?: Comparable<T>;
|
|
159
|
-
lte?: Comparable<T>;
|
|
160
|
-
$lte?: Comparable<T>;
|
|
161
|
-
exists?: boolean;
|
|
162
|
-
regex?: RegexOperand<T>;
|
|
163
|
-
$options?: string;
|
|
164
|
-
geoWithin?: GeoWithinOperandForField<TFieldKey>;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
type WhereFieldValue<T, TFieldKey extends string> =
|
|
168
|
-
| Friendly<NonNil<T>>
|
|
169
|
-
| FieldOperators<T, TFieldKey>;
|
|
170
|
-
|
|
171
|
-
export type WhereInput<TModel extends Record<string, unknown>> = {
|
|
172
|
-
[K in keyof TModel]?: WhereFieldValue<
|
|
173
|
-
TModel[K],
|
|
174
|
-
Extract<keyof TModel, string>
|
|
175
|
-
>;
|
|
176
|
-
} & {
|
|
177
|
-
geoWithin?: GeoWithinOperandForField<Extract<keyof TModel, string>>;
|
|
178
|
-
$geoWithin?: GeoWithinOperandForField<Extract<keyof TModel, string>>;
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
type ColumnData<TColumn> = TColumn extends { _: { data: infer TData } }
|
|
182
|
-
? TData
|
|
183
|
-
: unknown;
|
|
184
|
-
|
|
185
|
-
type InferRelationWhereFields<TConfig> = TConfig extends { where?: infer TWhere }
|
|
186
|
-
? TWhere extends (...args: infer TArgs) => unknown
|
|
187
|
-
? TArgs extends [infer TFields, ...unknown[]]
|
|
188
|
-
? TFields extends Record<string, unknown>
|
|
189
|
-
? TFields
|
|
190
|
-
: never
|
|
191
|
-
: never
|
|
192
|
-
: never
|
|
193
|
-
: never;
|
|
194
|
-
|
|
195
|
-
type RelationModelFromConfig<TConfig extends Record<string, unknown>> =
|
|
196
|
-
InferRelationWhereFields<TConfig> extends infer TFields
|
|
197
|
-
? TFields extends Record<string, unknown>
|
|
198
|
-
? {
|
|
199
|
-
[K in Extract<keyof TFields, string>]: ColumnData<TFields[K]>;
|
|
200
|
-
}
|
|
201
|
-
: Record<string, unknown>
|
|
202
|
-
: Record<string, unknown>;
|
|
203
|
-
|
|
204
|
-
type NumericModelFieldKey<TModel extends Record<string, unknown>> = Extract<
|
|
205
|
-
{
|
|
206
|
-
[K in keyof TModel]-?: NonNullable<TModel[K]> extends number ? K : never;
|
|
207
|
-
}[keyof TModel],
|
|
208
|
-
string
|
|
209
|
-
>;
|
|
210
|
-
|
|
211
|
-
type RelationAvgSelectionInput<TModel extends Record<string, unknown>> =
|
|
212
|
-
Partial<Record<NumericModelFieldKey<TModel>, boolean>>;
|
|
213
|
-
|
|
214
|
-
type RelationAggregateSelectionInput<TModel extends Record<string, unknown>> = {
|
|
215
|
-
_count?: boolean;
|
|
216
|
-
_avg?: RelationAvgSelectionInput<TModel>;
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
type QueryWithRelationInput<TEntry> =
|
|
220
|
-
| (Extract<TEntry, true> extends never ? never : true)
|
|
221
|
-
| (Extract<TEntry, Record<string, unknown>> extends infer TRelation
|
|
222
|
-
? TRelation extends Record<string, unknown>
|
|
223
|
-
? Omit<TRelation, "where" | "with" | "_count" | "_avg"> &
|
|
224
|
-
RelationAggregateSelectionInput<RelationModelFromConfig<TRelation>> & {
|
|
225
|
-
where?: WhereInput<RelationModelFromConfig<TRelation>>;
|
|
226
|
-
with?: TRelation extends { with?: infer TNestedWith }
|
|
227
|
-
? QueryWithInput<TNestedWith>
|
|
228
|
-
: never;
|
|
229
|
-
}
|
|
230
|
-
: never
|
|
231
|
-
: never);
|
|
232
|
-
|
|
233
|
-
type QueryWithInput<TWith> = TWith extends Record<string, unknown>
|
|
234
|
-
? {
|
|
235
|
-
[K in keyof TWith]?: QueryWithRelationInput<TWith[K]>;
|
|
236
|
-
}
|
|
237
|
-
: TWith;
|
|
238
|
-
|
|
239
|
-
export type QueryFindManyArgs<TName extends TableName> = Omit<
|
|
240
|
-
NonNullable<TableFindManyArgs<TName>>,
|
|
241
|
-
"where"
|
|
242
|
-
> & {
|
|
243
|
-
where?: WhereInput<TableModel<TName>>;
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
export type QueryFindFirstArgs<TName extends TableName> = Omit<
|
|
247
|
-
NonNullable<TableFindFirstArgs<TName>>,
|
|
248
|
-
"where"
|
|
249
|
-
> & {
|
|
250
|
-
where?: WhereInput<TableModel<TName>>;
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
export type QueryInsertArgs<TName extends TableName> = {
|
|
254
|
-
values: TableInsertModel<TName> | Array<TableInsertModel<TName>>;
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
export type QueryUpdateArgs<TName extends TableName> = {
|
|
258
|
-
set: Partial<TableInsertModel<TName>>;
|
|
259
|
-
where?: WhereInput<TableModel<TName>>;
|
|
260
|
-
limit?: number;
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
export type QueryDeleteArgs<TName extends TableName> = {
|
|
264
|
-
where?: WhereInput<TableModel<TName>>;
|
|
265
|
-
limit?: number;
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
type AggregateWithInput<TName extends TableName> =
|
|
269
|
-
TableFindManyArgs<TName> extends { with?: infer TWith }
|
|
270
|
-
? QueryWithInput<TWith>
|
|
271
|
-
: never;
|
|
272
|
-
|
|
273
|
-
type NumericFieldKey<TName extends TableName> = NumericModelFieldKey<
|
|
274
|
-
TableModel<TName>
|
|
275
|
-
>;
|
|
276
|
-
|
|
277
|
-
type RelationAggregateAvgResult<TAvg> = TAvg extends Record<string, unknown>
|
|
278
|
-
? {
|
|
279
|
-
[K in Extract<keyof TAvg, string> as TAvg[K] extends true ? K : never]: number;
|
|
280
|
-
}
|
|
281
|
-
: {};
|
|
282
|
-
|
|
283
|
-
type RelationAggregateResult<TEntry> =
|
|
284
|
-
Extract<TEntry, Record<string, unknown>> extends infer TRelation
|
|
285
|
-
? TRelation extends Record<string, unknown>
|
|
286
|
-
? (TRelation extends { _count?: true } ? { count: number } : {}) &
|
|
287
|
-
(TRelation extends { _avg?: infer TAvg }
|
|
288
|
-
? { avg: RelationAggregateAvgResult<TAvg> }
|
|
289
|
-
: {})
|
|
290
|
-
: {}
|
|
291
|
-
: {};
|
|
292
|
-
|
|
293
|
-
type HasRelationAggregateRequest<TEntry> =
|
|
294
|
-
Extract<TEntry, Record<string, unknown>> extends infer TRelation
|
|
295
|
-
? TRelation extends Record<string, unknown>
|
|
296
|
-
? TRelation extends { _count?: true }
|
|
297
|
-
? true
|
|
298
|
-
: TRelation extends { _avg?: Record<string, unknown> }
|
|
299
|
-
? true
|
|
300
|
-
: false
|
|
301
|
-
: false
|
|
302
|
-
: false;
|
|
303
|
-
|
|
304
|
-
type QueryWithAggregateResult<TWith> = TWith extends Record<string, unknown>
|
|
305
|
-
? {
|
|
306
|
-
[K in Extract<keyof TWith, string> as HasRelationAggregateRequest<
|
|
307
|
-
TWith[K]
|
|
308
|
-
> extends true
|
|
309
|
-
? \`\${K}Aggregate\`
|
|
310
|
-
: never]: RelationAggregateResult<TWith[K]>;
|
|
311
|
-
}
|
|
312
|
-
: {};
|
|
313
|
-
|
|
314
|
-
type WithFromArgs<TArgs> = TArgs extends { with?: infer TWith }
|
|
315
|
-
? TWith
|
|
316
|
-
: undefined;
|
|
317
|
-
|
|
318
|
-
type AppendRelationAggregatesToRow<TRow, TWith> = TRow &
|
|
319
|
-
QueryWithAggregateResult<TWith>;
|
|
320
|
-
|
|
321
|
-
type ApplyFindManyWithAggregateResult<TResult, TArgs> =
|
|
322
|
-
TResult extends Promise<infer TRows>
|
|
323
|
-
? TRows extends Array<infer TRow>
|
|
324
|
-
? Promise<
|
|
325
|
-
Array<AppendRelationAggregatesToRow<TRow, WithFromArgs<TArgs>>>
|
|
326
|
-
>
|
|
327
|
-
: TResult
|
|
328
|
-
: TResult;
|
|
329
|
-
|
|
330
|
-
type ApplyFindFirstWithAggregateResult<TResult, TArgs> =
|
|
331
|
-
TResult extends Promise<infer TRow>
|
|
332
|
-
? Promise<
|
|
333
|
-
TRow extends null
|
|
334
|
-
? null
|
|
335
|
-
: AppendRelationAggregatesToRow<TRow, WithFromArgs<TArgs>>
|
|
336
|
-
>
|
|
337
|
-
: TResult;
|
|
338
|
-
|
|
339
|
-
export type QueryCountArgs<TName extends TableName> = {
|
|
340
|
-
where?: WhereInput<TableModel<TName>>;
|
|
341
|
-
field?: Extract<keyof TableModel<TName>, string> | string;
|
|
342
|
-
distinct?: boolean;
|
|
343
|
-
with?: AggregateWithInput<TName>;
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
export type QueryAvgArgs<TName extends TableName> = {
|
|
347
|
-
where?: WhereInput<TableModel<TName>>;
|
|
348
|
-
field: NumericFieldKey<TName> | string;
|
|
349
|
-
distinct?: boolean;
|
|
350
|
-
with?: AggregateWithInput<TName>;
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
export type QueryUpsertArgs<TName extends TableName> = {
|
|
354
|
-
values: TableInsertModel<TName> | Array<TableInsertModel<TName>>;
|
|
355
|
-
target?:
|
|
356
|
-
| Extract<keyof TableInsertModel<TName>, string>
|
|
357
|
-
| Array<Extract<keyof TableInsertModel<TName>, string>>;
|
|
358
|
-
set?: Partial<TableInsertModel<TName>>;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
export type QueryTableApi<TName extends TableName> = {
|
|
362
|
-
findMany: TableFindManyMethod<TName> & (<
|
|
363
|
-
TArgs extends QueryFindManyArgs<TName> | undefined =
|
|
364
|
-
| QueryFindManyArgs<TName>
|
|
365
|
-
| undefined,
|
|
366
|
-
>(
|
|
367
|
-
args?: TArgs,
|
|
368
|
-
) => ApplyFindManyWithAggregateResult<TableFindManyResult<TName, TArgs>, TArgs>);
|
|
369
|
-
findFirst: TableFindFirstMethod<TName> & (<
|
|
370
|
-
TArgs extends QueryFindFirstArgs<TName> | undefined =
|
|
371
|
-
| QueryFindFirstArgs<TName>
|
|
372
|
-
| undefined,
|
|
373
|
-
>(
|
|
374
|
-
args?: TArgs,
|
|
375
|
-
) => ApplyFindFirstWithAggregateResult<TableFindFirstResult<TName, TArgs>, TArgs>);
|
|
376
|
-
insert: (args: QueryInsertArgs<TName>) => Promise<Array<TableModel<TName>>>;
|
|
377
|
-
update: (args: QueryUpdateArgs<TName>) => Promise<Array<TableModel<TName>>>;
|
|
378
|
-
upsert: (args: QueryUpsertArgs<TName>) => Promise<Array<TableModel<TName>>>;
|
|
379
|
-
delete: (args?: QueryDeleteArgs<TName>) => Promise<Array<TableModel<TName>>>;
|
|
380
|
-
count: (args?: QueryCountArgs<TName>) => Promise<number>;
|
|
381
|
-
avg: (args: QueryAvgArgs<TName>) => Promise<number | null>;
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
export type AppflareQueryDb = {
|
|
385
|
-
[TName in TableName]: QueryTableApi<TName>;
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
export type DbMutationKind = "insert" | "update" | "upsert" | "delete";
|
|
389
|
-
|
|
390
|
-
export type DbMutationEvent = {
|
|
391
|
-
kind: DbMutationKind;
|
|
392
|
-
table: string;
|
|
393
|
-
args: Record<string, unknown>;
|
|
394
|
-
rows: unknown[];
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
export type QueryDbOptions = {
|
|
398
|
-
onMutation?: (event: DbMutationEvent) => void;
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
402
|
-
return typeof value === "object" && value !== null;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function readOperatorValue(record: Record<string, unknown>, key: string): unknown {
|
|
406
|
-
if (record[key] !== undefined) {
|
|
407
|
-
return record[key];
|
|
408
|
-
}
|
|
409
|
-
const prefixed = "$" + key;
|
|
410
|
-
return record[prefixed];
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function normalizeGeoPoint(
|
|
414
|
-
value: GeoPoint | GeoCoordinates,
|
|
415
|
-
): { latitude: number; longitude: number } | null {
|
|
416
|
-
if ((value as GeoPoint).latitude !== undefined) {
|
|
417
|
-
const point = value as GeoPoint;
|
|
418
|
-
if (
|
|
419
|
-
typeof point.latitude === "number" &&
|
|
420
|
-
typeof point.longitude === "number"
|
|
421
|
-
) {
|
|
422
|
-
return {
|
|
423
|
-
latitude: point.latitude,
|
|
424
|
-
longitude: point.longitude,
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
return null;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
const coordinates = (value as GeoCoordinates).coordinates;
|
|
431
|
-
if (!Array.isArray(coordinates) || coordinates.length < 2) {
|
|
432
|
-
return null;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const [longitude, latitude] = coordinates;
|
|
436
|
-
if (typeof latitude !== "number" || typeof longitude !== "number") {
|
|
437
|
-
return null;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return {
|
|
441
|
-
latitude,
|
|
442
|
-
longitude,
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
function createGeoDistanceFilter(
|
|
447
|
-
operand: GeoWithinOperand,
|
|
448
|
-
fields: Record<string, unknown>,
|
|
449
|
-
tableName?: string,
|
|
450
|
-
fallbackLatitudeField?: string,
|
|
451
|
-
): SQL | undefined {
|
|
452
|
-
const geometry = normalizeGeoPoint(operand.$geometry);
|
|
453
|
-
if (!geometry) {
|
|
454
|
-
return undefined;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const latitudeField = operand.latitudeField ?? fallbackLatitudeField;
|
|
458
|
-
const longitudeField = operand.longitudeField;
|
|
459
|
-
if (!latitudeField || !longitudeField) {
|
|
460
|
-
return undefined;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
const latitudeColumn = fields[latitudeField];
|
|
464
|
-
if (!latitudeColumn) {
|
|
465
|
-
console.warn(
|
|
466
|
-
"Invalid geoWithin latitudeField",
|
|
467
|
-
tableName ?? "unknown",
|
|
468
|
-
latitudeField,
|
|
469
|
-
);
|
|
470
|
-
return undefined;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const longitudeColumn = fields[longitudeField];
|
|
474
|
-
if (!longitudeColumn) {
|
|
475
|
-
console.warn(
|
|
476
|
-
"Invalid geoWithin longitudeField",
|
|
477
|
-
tableName ?? "unknown",
|
|
478
|
-
longitudeField,
|
|
479
|
-
);
|
|
480
|
-
return undefined;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const distanceExpression = sql<number>\`(
|
|
484
|
-
6371000 * acos(
|
|
485
|
-
cos(radians(\${geometry.latitude})) * cos(radians(\${latitudeColumn}))
|
|
486
|
-
* cos(radians(\${longitudeColumn}) - radians(\${geometry.longitude}))
|
|
487
|
-
+ sin(radians(\${geometry.latitude})) * sin(radians(\${latitudeColumn}))
|
|
488
|
-
)
|
|
489
|
-
)\`;
|
|
490
|
-
|
|
491
|
-
const minDistance =
|
|
492
|
-
operand.$minDistance ??
|
|
493
|
-
operand.gte ??
|
|
494
|
-
operand.$gte ??
|
|
495
|
-
operand.gt ??
|
|
496
|
-
operand.$gt;
|
|
497
|
-
const maxDistance =
|
|
498
|
-
operand.$maxDistance ??
|
|
499
|
-
operand.lte ??
|
|
500
|
-
operand.$lte ??
|
|
501
|
-
operand.lt ??
|
|
502
|
-
operand.$lt;
|
|
503
|
-
|
|
504
|
-
const filters: SQL[] = [];
|
|
505
|
-
if (typeof minDistance === "number") {
|
|
506
|
-
filters.push(sql\`\${distanceExpression} >= \${minDistance}\`);
|
|
507
|
-
}
|
|
508
|
-
if (typeof maxDistance === "number") {
|
|
509
|
-
filters.push(sql\`\${distanceExpression} <= \${maxDistance}\`);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (filters.length === 0) {
|
|
513
|
-
return undefined;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (filters.length === 1) {
|
|
517
|
-
return filters[0];
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
return and(...filters);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function buildFieldFilter(
|
|
524
|
-
fieldName: string,
|
|
525
|
-
field: unknown,
|
|
526
|
-
value: unknown,
|
|
527
|
-
fields: Record<string, unknown>,
|
|
528
|
-
): SQL | undefined {
|
|
529
|
-
if (!isRecord(value) || value instanceof Date || Array.isArray(value)) {
|
|
530
|
-
return eq(field as never, value as never);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
const filters: SQL[] = [];
|
|
534
|
-
const eqValue = readOperatorValue(value, "eq");
|
|
535
|
-
if (eqValue !== undefined) {
|
|
536
|
-
filters.push(eq(field as never, eqValue as never));
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
const neValue = readOperatorValue(value, "ne");
|
|
540
|
-
if (neValue !== undefined) {
|
|
541
|
-
filters.push(ne(field as never, neValue as never));
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
const inValue = readOperatorValue(value, "in");
|
|
545
|
-
if (Array.isArray(inValue) && inValue.length > 0) {
|
|
546
|
-
filters.push(inArray(field as never, inValue as never));
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const ninValue = readOperatorValue(value, "nin");
|
|
550
|
-
if (Array.isArray(ninValue) && ninValue.length > 0) {
|
|
551
|
-
filters.push(notInArray(field as never, ninValue as never));
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
const gtValue = readOperatorValue(value, "gt");
|
|
555
|
-
if (gtValue !== undefined) {
|
|
556
|
-
filters.push(gt(field as never, gtValue as never));
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
const gteValue = readOperatorValue(value, "gte");
|
|
560
|
-
if (gteValue !== undefined) {
|
|
561
|
-
filters.push(gte(field as never, gteValue as never));
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const ltValue = readOperatorValue(value, "lt");
|
|
565
|
-
if (ltValue !== undefined) {
|
|
566
|
-
filters.push(lt(field as never, ltValue as never));
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const lteValue = readOperatorValue(value, "lte");
|
|
570
|
-
if (lteValue !== undefined) {
|
|
571
|
-
filters.push(lte(field as never, lteValue as never));
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
if (typeof value.exists === "boolean") {
|
|
575
|
-
filters.push(value.exists ? isNotNull(field as never) : isNull(field as never));
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if (typeof value.regex === "string") {
|
|
579
|
-
const caseInsensitive = typeof value.$options === "string" && value.$options.includes("i");
|
|
580
|
-
const pattern = "%" + value.regex + "%";
|
|
581
|
-
if (caseInsensitive) {
|
|
582
|
-
filters.push(
|
|
583
|
-
sql\`lower(\${field as never}) like lower(\${pattern})\`,
|
|
584
|
-
);
|
|
585
|
-
} else {
|
|
586
|
-
filters.push(sql\`\${field as never} like \${pattern}\`);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (isRecord(value.geoWithin)) {
|
|
591
|
-
const geoFilter = createGeoDistanceFilter(
|
|
592
|
-
value.geoWithin as GeoWithinOperand,
|
|
593
|
-
fields,
|
|
594
|
-
undefined,
|
|
595
|
-
fieldName,
|
|
596
|
-
);
|
|
597
|
-
if (geoFilter) {
|
|
598
|
-
filters.push(geoFilter);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
if (filters.length === 0) {
|
|
603
|
-
return eq(field as never, value as never);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
if (filters.length === 1) {
|
|
607
|
-
return filters[0];
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
return and(...filters);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
function buildWhereFilter(
|
|
614
|
-
table: unknown,
|
|
615
|
-
where?: Record<string, unknown>,
|
|
616
|
-
tableName?: string,
|
|
617
|
-
): SQL | undefined {
|
|
618
|
-
if (!table || !where || Object.keys(where).length === 0) {
|
|
619
|
-
return undefined;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
const fields = getTableColumns(table as never) as Record<string, unknown>;
|
|
623
|
-
return buildWhereFilterFromFields(fields, where, tableName);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
function buildWhereFilterFromFields(
|
|
627
|
-
fields: Record<string, unknown>,
|
|
628
|
-
where?: Record<string, unknown>,
|
|
629
|
-
tableName?: string,
|
|
630
|
-
): SQL | undefined {
|
|
631
|
-
if (!where || Object.keys(where).length === 0) {
|
|
632
|
-
return undefined;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const filters: SQL[] = [];
|
|
636
|
-
|
|
637
|
-
const topLevelGeoWithin = isRecord(where.geoWithin)
|
|
638
|
-
? (where.geoWithin as GeoWithinOperand)
|
|
639
|
-
: isRecord(where.$geoWithin)
|
|
640
|
-
? (where.$geoWithin as GeoWithinOperand)
|
|
641
|
-
: undefined;
|
|
642
|
-
|
|
643
|
-
if (topLevelGeoWithin) {
|
|
644
|
-
const geoFilter = createGeoDistanceFilter(topLevelGeoWithin, fields, tableName);
|
|
645
|
-
if (geoFilter) {
|
|
646
|
-
filters.push(geoFilter);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
for (const [fieldName, fieldValue] of Object.entries(where)) {
|
|
651
|
-
if (fieldName === "geoWithin" || fieldName === "$geoWithin") {
|
|
652
|
-
continue;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
if (fieldValue === undefined) {
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
const field = fields[fieldName];
|
|
660
|
-
if (!field) {
|
|
661
|
-
continue;
|
|
662
|
-
}
|
|
1
|
+
import { generateFilterAndWhereTypesSection } from "./query-definitions/filter-and-where-types";
|
|
2
|
+
import { generateQueryApiTypesSection } from "./query-definitions/query-api-types";
|
|
3
|
+
import { generateQueryHelperFunctionsSection } from "./query-definitions/query-helper-functions";
|
|
4
|
+
import { generateSchemaAndTableTypesSection } from "./query-definitions/schema-and-table-types";
|
|
663
5
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
return undefined;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if (filters.length === 1) {
|
|
675
|
-
return filters[0];
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return and(...filters);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
function transformWithRelations(withValue: unknown): unknown {
|
|
682
|
-
return transformWithRelationsAndExtractAggregates(withValue).with;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
type RelationWithAggregatePlanEntry = {
|
|
686
|
-
count: boolean;
|
|
687
|
-
avgFields: string[];
|
|
688
|
-
nested?: RelationWithAggregatePlan;
|
|
689
|
-
};
|
|
690
|
-
|
|
691
|
-
type RelationWithAggregatePlan = Record<string, RelationWithAggregatePlanEntry>;
|
|
692
|
-
|
|
693
|
-
function hasRelationAggregatePlanEntries(
|
|
694
|
-
plan: RelationWithAggregatePlan | undefined,
|
|
695
|
-
): boolean {
|
|
696
|
-
return !!plan && Object.keys(plan).length > 0;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
function extractRelationAvgFieldNames(value: unknown): string[] {
|
|
700
|
-
if (!isRecord(value)) {
|
|
701
|
-
return [];
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
return Object.entries(value)
|
|
705
|
-
.filter(([, enabled]) => enabled === true)
|
|
706
|
-
.map(([fieldName]) => fieldName);
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
function transformWithRelationsAndExtractAggregates(withValue: unknown): {
|
|
710
|
-
with: unknown;
|
|
711
|
-
aggregatePlan: RelationWithAggregatePlan | undefined;
|
|
712
|
-
} {
|
|
713
|
-
if (!isRecord(withValue)) {
|
|
714
|
-
return {
|
|
715
|
-
with: withValue,
|
|
716
|
-
aggregatePlan: undefined,
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
const transformed: Record<string, unknown> = {};
|
|
721
|
-
const aggregatePlan: RelationWithAggregatePlan = {};
|
|
722
|
-
for (const [relationName, relationValue] of Object.entries(withValue)) {
|
|
723
|
-
if (typeof relationValue === "boolean") {
|
|
724
|
-
transformed[relationName] = relationValue;
|
|
725
|
-
continue;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
if (!isRecord(relationValue)) {
|
|
729
|
-
transformed[relationName] = relationValue;
|
|
730
|
-
continue;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
const relationConfig: Record<string, unknown> = {
|
|
734
|
-
...relationValue,
|
|
735
|
-
};
|
|
736
|
-
const countRequested = relationConfig._count === true;
|
|
737
|
-
const avgFieldNames = extractRelationAvgFieldNames(relationConfig._avg);
|
|
738
|
-
delete relationConfig._count;
|
|
739
|
-
delete relationConfig._avg;
|
|
740
|
-
|
|
741
|
-
let nestedAggregatePlan: RelationWithAggregatePlan | undefined;
|
|
742
|
-
|
|
743
|
-
const relationWhere = relationConfig.where;
|
|
744
|
-
if (relationWhere !== undefined && !isRecord(relationWhere)) {
|
|
745
|
-
throw new Error(
|
|
746
|
-
"Invalid relational with.where for relation " +
|
|
747
|
-
relationName +
|
|
748
|
-
": only shorthand object filters are supported.",
|
|
749
|
-
);
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
if (isRecord(relationWhere)) {
|
|
753
|
-
relationConfig.where = (fields: Record<string, unknown>) => {
|
|
754
|
-
const filter = buildWhereFilterFromFields(
|
|
755
|
-
fields,
|
|
756
|
-
relationWhere,
|
|
757
|
-
relationName,
|
|
758
|
-
);
|
|
759
|
-
if (filter) {
|
|
760
|
-
return filter;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
return sql\`1 = 1\`;
|
|
764
|
-
};
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
if (relationConfig.with !== undefined) {
|
|
768
|
-
const nestedTransform = transformWithRelationsAndExtractAggregates(
|
|
769
|
-
relationConfig.with,
|
|
770
|
-
);
|
|
771
|
-
relationConfig.with = nestedTransform.with;
|
|
772
|
-
nestedAggregatePlan = nestedTransform.aggregatePlan;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
if (
|
|
776
|
-
countRequested ||
|
|
777
|
-
avgFieldNames.length > 0 ||
|
|
778
|
-
hasRelationAggregatePlanEntries(nestedAggregatePlan)
|
|
779
|
-
) {
|
|
780
|
-
aggregatePlan[relationName] = {
|
|
781
|
-
count: countRequested,
|
|
782
|
-
avgFields: avgFieldNames,
|
|
783
|
-
...(nestedAggregatePlan
|
|
784
|
-
? { nested: nestedAggregatePlan }
|
|
785
|
-
: {}),
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
transformed[relationName] = relationConfig;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
return {
|
|
793
|
-
with: transformed,
|
|
794
|
-
aggregatePlan: hasRelationAggregatePlanEntries(aggregatePlan)
|
|
795
|
-
? aggregatePlan
|
|
796
|
-
: undefined,
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
function computeAverageOrZero(values: number[]): number {
|
|
801
|
-
if (values.length === 0) {
|
|
802
|
-
return 0;
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
const total = values.reduce((sum, current) => sum + current, 0);
|
|
806
|
-
return total / values.length;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
function applyRelationAggregatePlanToRow(
|
|
810
|
-
row: unknown,
|
|
811
|
-
plan: RelationWithAggregatePlan,
|
|
812
|
-
): unknown {
|
|
813
|
-
if (!isRecord(row)) {
|
|
814
|
-
return row;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
const nextRow: Record<string, unknown> = {
|
|
818
|
-
...row,
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
for (const [relationName, relationPlan] of Object.entries(plan)) {
|
|
822
|
-
const relationData = nextRow[relationName];
|
|
823
|
-
const relationEntries = Array.isArray(relationData)
|
|
824
|
-
? relationData.filter((entry) => entry !== null && entry !== undefined)
|
|
825
|
-
: relationData === null || relationData === undefined
|
|
826
|
-
? []
|
|
827
|
-
: [relationData];
|
|
828
|
-
|
|
829
|
-
if (relationPlan.nested) {
|
|
830
|
-
if (Array.isArray(relationData)) {
|
|
831
|
-
nextRow[relationName] = relationData.map((entry) =>
|
|
832
|
-
applyRelationAggregatePlanToRow(entry, relationPlan.nested as RelationWithAggregatePlan),
|
|
833
|
-
);
|
|
834
|
-
} else if (isRecord(relationData)) {
|
|
835
|
-
nextRow[relationName] = applyRelationAggregatePlanToRow(
|
|
836
|
-
relationData,
|
|
837
|
-
relationPlan.nested,
|
|
838
|
-
);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (!relationPlan.count && relationPlan.avgFields.length === 0) {
|
|
843
|
-
continue;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
const aggregatePayload: Record<string, unknown> = {};
|
|
847
|
-
if (relationPlan.count) {
|
|
848
|
-
aggregatePayload.count = relationEntries.length;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
if (relationPlan.avgFields.length > 0) {
|
|
852
|
-
const averagePayload: Record<string, number> = {};
|
|
853
|
-
for (const fieldName of relationPlan.avgFields) {
|
|
854
|
-
const numericValues = relationEntries
|
|
855
|
-
.map((entry) =>
|
|
856
|
-
isRecord(entry) ? toFiniteNumber(entry[fieldName]) : null,
|
|
857
|
-
)
|
|
858
|
-
.filter((value): value is number => value !== null);
|
|
859
|
-
averagePayload[fieldName] = computeAverageOrZero(numericValues);
|
|
860
|
-
}
|
|
861
|
-
aggregatePayload.avg = averagePayload;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
nextRow[relationName + "Aggregate"] = aggregatePayload;
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
return nextRow;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
function applyRelationAggregatePlanToResult(
|
|
871
|
-
result: unknown,
|
|
872
|
-
plan: RelationWithAggregatePlan | undefined,
|
|
873
|
-
): unknown {
|
|
874
|
-
if (!hasRelationAggregatePlanEntries(plan)) {
|
|
875
|
-
return result;
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
if (Array.isArray(result)) {
|
|
879
|
-
return result.map((row) => applyRelationAggregatePlanToRow(row, plan));
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
if (result === null || result === undefined) {
|
|
883
|
-
return result;
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
return applyRelationAggregatePlanToRow(result, plan);
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
function hasAggregateWithConstraints(withValue: unknown): boolean {
|
|
890
|
-
if (!isRecord(withValue)) {
|
|
891
|
-
return false;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
for (const relationValue of Object.values(withValue)) {
|
|
895
|
-
if (!isRecord(relationValue)) {
|
|
896
|
-
continue;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
if (isRecord(relationValue.where)) {
|
|
900
|
-
return true;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
if (hasAggregateWithConstraints(relationValue.with)) {
|
|
904
|
-
return true;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
return false;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
function rowMatchesAggregateWithConstraints(
|
|
912
|
-
row: unknown,
|
|
913
|
-
withValue: unknown,
|
|
914
|
-
): boolean {
|
|
915
|
-
if (!isRecord(withValue)) {
|
|
916
|
-
return true;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
if (!isRecord(row)) {
|
|
920
|
-
return !hasAggregateWithConstraints(withValue);
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
for (const [relationName, relationValue] of Object.entries(withValue)) {
|
|
924
|
-
if (!isRecord(relationValue)) {
|
|
925
|
-
continue;
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
const nestedWith = relationValue.with;
|
|
929
|
-
const requiresPresence =
|
|
930
|
-
isRecord(relationValue.where) || hasAggregateWithConstraints(nestedWith);
|
|
931
|
-
if (!requiresPresence) {
|
|
932
|
-
continue;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
const relationData = row[relationName];
|
|
936
|
-
if (Array.isArray(relationData)) {
|
|
937
|
-
if (relationData.length === 0) {
|
|
938
|
-
return false;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
if (hasAggregateWithConstraints(nestedWith)) {
|
|
942
|
-
const hasNestedMatch = relationData.some((entry) =>
|
|
943
|
-
rowMatchesAggregateWithConstraints(entry, nestedWith),
|
|
944
|
-
);
|
|
945
|
-
if (!hasNestedMatch) {
|
|
946
|
-
return false;
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
if (!isRecord(relationData)) {
|
|
954
|
-
return false;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
if (
|
|
958
|
-
hasAggregateWithConstraints(nestedWith) &&
|
|
959
|
-
!rowMatchesAggregateWithConstraints(relationData, nestedWith)
|
|
960
|
-
) {
|
|
961
|
-
return false;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
return true;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
function splitAggregateFieldPath(field: string): string[] {
|
|
969
|
-
return field
|
|
970
|
-
.split(".")
|
|
971
|
-
.map((segment) => segment.trim())
|
|
972
|
-
.filter((segment) => segment.length > 0);
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
function collectValuesFromPath(
|
|
976
|
-
value: unknown,
|
|
977
|
-
pathSegments: string[],
|
|
978
|
-
index = 0,
|
|
979
|
-
): unknown[] {
|
|
980
|
-
if (index >= pathSegments.length) {
|
|
981
|
-
return [value];
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
if (value === null || value === undefined) {
|
|
985
|
-
return [];
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
if (Array.isArray(value)) {
|
|
989
|
-
return value.flatMap((entry) =>
|
|
990
|
-
collectValuesFromPath(entry, pathSegments, index),
|
|
991
|
-
);
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
if (!isRecord(value)) {
|
|
995
|
-
return [];
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
const nextValue = value[pathSegments[index]];
|
|
999
|
-
return collectValuesFromPath(nextValue, pathSegments, index + 1);
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
function createDistinctValueKey(value: unknown): string {
|
|
1003
|
-
if (value instanceof Date) {
|
|
1004
|
-
return "date:" + value.toISOString();
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
if (Array.isArray(value)) {
|
|
1008
|
-
return (
|
|
1009
|
-
"[" + value.map((entry) => createDistinctValueKey(entry)).join(",") + "]"
|
|
1010
|
-
);
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
if (isRecord(value)) {
|
|
1014
|
-
const keys = Object.keys(value).sort((left, right) => left.localeCompare(right));
|
|
1015
|
-
return (
|
|
1016
|
-
"{" +
|
|
1017
|
-
keys
|
|
1018
|
-
.map((key) => JSON.stringify(key) + ":" + createDistinctValueKey(value[key]))
|
|
1019
|
-
.join(",") +
|
|
1020
|
-
"}"
|
|
1021
|
-
);
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
if (value === null) {
|
|
1025
|
-
return "null";
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
if (value === undefined) {
|
|
1029
|
-
return "undefined";
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
if (typeof value === "number" && Number.isNaN(value)) {
|
|
1033
|
-
return "number:NaN";
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
return typeof value + ":" + String(value);
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
function countAggregateValues(values: unknown[], distinct: boolean): number {
|
|
1040
|
-
const nonNullValues = values.filter(
|
|
1041
|
-
(value) => value !== null && value !== undefined,
|
|
1042
|
-
);
|
|
1043
|
-
if (!distinct) {
|
|
1044
|
-
return nonNullValues.length;
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
const seen = new Set<string>();
|
|
1048
|
-
for (const value of nonNullValues) {
|
|
1049
|
-
seen.add(createDistinctValueKey(value));
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
return seen.size;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
function toFiniteNumber(value: unknown): number | null {
|
|
1056
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1057
|
-
return null;
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
return value;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
function averageAggregateValues(values: number[]): number | null {
|
|
1064
|
-
if (values.length === 0) {
|
|
1065
|
-
return null;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const total = values.reduce((sum, current) => sum + current, 0);
|
|
1069
|
-
return total / values.length;
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
function inferConflictTarget(table: unknown): string[] {
|
|
1073
|
-
if (!table) {
|
|
1074
|
-
return [];
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
const columns = getTableColumns(table as never) as Record<string, unknown>;
|
|
1078
|
-
if (columns.id) {
|
|
1079
|
-
return ["id"];
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
return [];
|
|
1083
|
-
}
|
|
1084
|
-
`;
|
|
6
|
+
export function generateTypesQueryDefinitionsSection(): string {
|
|
7
|
+
return [
|
|
8
|
+
generateSchemaAndTableTypesSection(),
|
|
9
|
+
generateFilterAndWhereTypesSection(),
|
|
10
|
+
generateQueryApiTypesSection(),
|
|
11
|
+
generateQueryHelperFunctionsSection(),
|
|
12
|
+
].join("\n\n");
|
|
1085
13
|
}
|