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.
Files changed (64) hide show
  1. package/cli/commands/index.ts +98 -4
  2. package/cli/generate.ts +8 -0
  3. package/cli/index.ts +32 -1
  4. package/cli/templates/auth/config.ts +0 -2
  5. package/cli/templates/core/handlers.route.ts +1 -0
  6. package/cli/templates/core/imports.ts +1 -0
  7. package/cli/templates/dashboard/builders/functions/execute-handler.ts +124 -0
  8. package/cli/templates/dashboard/builders/functions/index.ts +22 -0
  9. package/cli/templates/dashboard/builders/functions/render-page/header.ts +20 -0
  10. package/cli/templates/dashboard/builders/functions/render-page/index.ts +33 -0
  11. package/cli/templates/dashboard/builders/functions/render-page/request-panel.ts +67 -0
  12. package/cli/templates/dashboard/builders/functions/render-page/result-panel.ts +19 -0
  13. package/cli/templates/dashboard/builders/functions/render-page/scripts.ts +17 -0
  14. package/cli/templates/dashboard/builders/navigation.ts +122 -0
  15. package/cli/templates/dashboard/builders/storage/index.ts +13 -0
  16. package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -0
  17. package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -0
  18. package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -0
  19. package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -0
  20. package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -0
  21. package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -0
  22. package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -0
  23. package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -0
  24. package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -0
  25. package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -0
  26. package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -0
  27. package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -0
  28. package/cli/templates/dashboard/builders/table-routes/fragments.ts +214 -0
  29. package/cli/templates/dashboard/builders/table-routes/helpers.ts +49 -0
  30. package/cli/templates/dashboard/builders/table-routes/index.ts +8 -0
  31. package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -0
  32. package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +166 -0
  33. package/cli/templates/dashboard/builders/table-routes/table/index.ts +77 -0
  34. package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +111 -0
  35. package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -0
  36. package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -0
  37. package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -0
  38. package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -0
  39. package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +127 -0
  40. package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -0
  41. package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -0
  42. package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -0
  43. package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -0
  44. package/cli/templates/dashboard/components/dashboard-home.ts +23 -0
  45. package/cli/templates/dashboard/components/layout.ts +388 -0
  46. package/cli/templates/dashboard/components/login-page.ts +65 -0
  47. package/cli/templates/dashboard/index.ts +61 -0
  48. package/cli/templates/dashboard/types.ts +9 -0
  49. package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +1 -1
  50. package/cli/templates/handlers/generators/types/core.ts +5 -0
  51. package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +168 -0
  52. package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +133 -0
  53. package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +686 -0
  54. package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +97 -0
  55. package/cli/templates/handlers/generators/types/query-definitions.ts +11 -1083
  56. package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -0
  57. package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +164 -0
  58. package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +85 -0
  59. package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -0
  60. package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +137 -0
  61. package/cli/templates/handlers/generators/types/query-runtime.ts +13 -431
  62. package/cli/utils/schema-discovery.ts +10 -1
  63. package/package.json +1 -1
  64. package/test-better-auth-hash.ts +2 -0
@@ -1,1085 +1,13 @@
1
- export function generateTypesQueryDefinitionsSection(): string {
2
- return `const mergedSchema = {
3
- ...authSchema,
4
- ...schema,
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
- const filter = buildFieldFilter(fieldName, field, fieldValue, fields);
665
- if (filter) {
666
- filters.push(filter);
667
- }
668
- }
669
-
670
- if (filters.length === 0) {
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
  }