@@ -109,6 +109,13 @@ type ParserError<Message extends string> = { error: true } & Message
109
109
type GenericStringError = ParserError < 'Received a generic string' >
110
110
export type SelectQueryError < Message extends string > = { error : true } & Message
111
111
112
+ /**
113
+ * Creates a new {@link ParserError} if the given input is not already a parser error.
114
+ */
115
+ type CreateParserErrorIfRequired < Input , Message extends string > = Input extends ParserError < string >
116
+ ? Input
117
+ : ParserError < Message >
118
+
112
119
/**
113
120
* Trims whitespace from the left of the input.
114
121
*/
@@ -118,6 +125,9 @@ type EatWhitespace<Input extends string> = string extends Input
118
125
? EatWhitespace < Remainder >
119
126
: Input
120
127
128
+ /**
129
+ * Returns a boolean representing whether there is a foreign key with the given name.
130
+ */
121
131
type HasFKey < FKeyName , Relationships > = Relationships extends [ infer R ]
122
132
? R extends { foreignKeyName : FKeyName }
123
133
? true
@@ -128,6 +138,9 @@ type HasFKey<FKeyName, Relationships> = Relationships extends [infer R]
128
138
: HasFKey < FKeyName , Rest >
129
139
: false
130
140
141
+ /**
142
+ * Returns a boolean representing whether there the foreign key has a unique constraint.
143
+ */
131
144
type HasUniqueFKey < FKeyName , Relationships > = Relationships extends [ infer R ]
132
145
? R extends { foreignKeyName : FKeyName ; isOneToOne : true }
133
146
? true
@@ -138,6 +151,10 @@ type HasUniqueFKey<FKeyName, Relationships> = Relationships extends [infer R]
138
151
: HasUniqueFKey < FKeyName , Rest >
139
152
: false
140
153
154
+ /**
155
+ * Returns a boolean representing whether there is a foreign key referencing
156
+ * a given relation.
157
+ */
141
158
type HasFKeyToFRel < FRelName , Relationships > = Relationships extends [ infer R ]
142
159
? R extends { referencedRelation : FRelName }
143
160
? true
@@ -161,8 +178,9 @@ type HasUniqueFKeyToFRel<FRelName, Relationships> = Relationships extends [infer
161
178
/**
162
179
* Constructs a type definition for a single field of an object.
163
180
*
164
- * @param Definitions Record of definitions, possibly generated from PostgREST's OpenAPI spec.
165
- * @param Name Name of the table being queried.
181
+ * @param Schema Database schema.
182
+ * @param Row Type of a row in the given table.
183
+ * @param Relationships Relationships between different tables in the database.
166
184
* @param Field Single field parsed by `ParseQuery`.
167
185
*/
168
186
type ConstructFieldDefinition <
@@ -231,12 +249,12 @@ type ConstructFieldDefinition<
231
249
: Child [ ]
232
250
: never
233
251
}
252
+ : Field extends { name : string ; type : infer T }
253
+ ? { [ K in Field [ 'name' ] ] : T }
234
254
: Field extends { name : string ; original : string }
235
255
? Field [ 'original' ] extends keyof Row
236
256
? { [ K in Field [ 'name' ] ] : Row [ Field [ 'original' ] ] }
237
257
: SelectQueryError < `Referencing missing column \`${Field [ 'original' ] } \``>
238
- : Field extends { name : string ; type : infer T }
239
- ? { [ K in Field [ 'name' ] ] : T }
240
258
: Record < string , unknown >
241
259
242
260
/**
@@ -246,8 +264,7 @@ type ConstructFieldDefinition<
246
264
*/
247
265
248
266
/**
249
- * Reads a consecutive sequence of more than 1 letter,
250
- * where letters are `[0-9a-zA-Z_]`.
267
+ * Reads a consecutive sequence of 1 or more letter, where letters are `[0-9a-zA-Z_]`.
251
268
*/
252
269
type ReadLetters < Input extends string > = string extends Input
253
270
? GenericStringError
@@ -266,7 +283,7 @@ type ReadLettersHelper<Input extends string, Acc extends string> = string extend
266
283
: [ Acc , '' ]
267
284
268
285
/**
269
- * Reads a consecutive sequence of more than 1 double-quoted letters,
286
+ * Reads a consecutive sequence of 1 or more double-quoted letters,
270
287
* where letters are `[^"]`.
271
288
*/
272
289
type ReadQuotedLetters < Input extends string > = string extends Input
@@ -289,7 +306,7 @@ type ReadQuotedLettersHelper<Input extends string, Acc extends string> = string
289
306
290
307
/**
291
308
* Parses a (possibly double-quoted) identifier.
292
- * For now, identifiers are just sequences of more than 1 letter .
309
+ * Identifiers are sequences of 1 or more letters .
293
310
*/
294
311
type ParseIdentifier < Input extends string > = ReadLetters < Input > extends [
295
312
infer Name ,
@@ -301,46 +318,29 @@ type ParseIdentifier<Input extends string> = ReadLetters<Input> extends [
301
318
: ParserError < `No (possibly double-quoted) identifier at \`${Input } \``>
302
319
303
320
/**
304
- * Parses a node.
305
- * A node is one of the following:
306
- * - `*`
321
+ * Parses a field without preceding field renaming.
322
+ * A field is one of the following:
307
323
* - `field`
308
324
* - `field::type`
309
325
* - `field->json...`
310
326
* - `field(nodes)`
311
327
* - `field!hint(nodes)`
312
328
* - `field!inner(nodes)`
313
329
* - `field!hint!inner(nodes)`
314
- * - `renamed_field:field`
315
- * - `renamed_field:field::type`
316
- * - `renamed_field:field->json...`
317
- * - `renamed_field:field(nodes)`
318
- * - `renamed_field:field!hint(nodes)`
319
- * - `renamed_field:field!inner(nodes)`
320
- * - `renamed_field:field!hint!inner(nodes)`
321
330
*
322
- * TODO: more support for JSON operators `-> `, `->>`.
331
+ * TODO: support type casting of JSON operators `a->b::type `, `a ->>b::type `.
323
332
*/
324
- type ParseNode < Input extends string > = Input extends ''
333
+ type ParseField < Input extends string > = Input extends ''
325
334
? ParserError < 'Empty string' >
326
- : // `*`
327
- Input extends `*${infer Remainder } `
328
- ? [ { star : true } , EatWhitespace < Remainder > ]
329
335
: ParseIdentifier < Input > extends [ infer Name , `${infer Remainder } `]
330
- ? EatWhitespace < Remainder > extends `::${infer Remainder } `
331
- ? ParseIdentifier < Remainder > extends [ infer CastType , `${infer Remainder } `]
332
- ? // `field::type`
333
- CastType extends PostgreSQLTypes
334
- ? [ { name : Name ; type : TypeScriptTypes < CastType > } , EatWhitespace < Remainder > ]
335
- : never
336
- : ParserError < `Unexpected type cast at \`${Input } \``>
337
- : EatWhitespace < Remainder > extends `!inner${infer Remainder } `
336
+ ? EatWhitespace < Remainder > extends `!inner${infer Remainder } `
338
337
? ParseEmbeddedResource < EatWhitespace < Remainder > > extends [ infer Fields , `${infer Remainder } `]
339
338
? // `field!inner(nodes)`
340
339
[ { name : Name ; original : Name ; children : Fields } , EatWhitespace < Remainder > ]
341
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
342
- ? ParseEmbeddedResource < EatWhitespace < Remainder > >
343
- : ParserError < 'Expected embedded resource after `!inner`' >
340
+ : CreateParserErrorIfRequired <
341
+ ParseEmbeddedResource < EatWhitespace < Remainder > > ,
342
+ 'Expected embedded resource after `!inner`'
343
+ >
344
344
: EatWhitespace < Remainder > extends `!${infer Remainder } `
345
345
? ParseIdentifier < EatWhitespace < Remainder > > extends [ infer Hint , `${infer Remainder } `]
346
346
? EatWhitespace < Remainder > extends `!inner${infer Remainder } `
@@ -350,89 +350,21 @@ type ParseNode<Input extends string> = Input extends ''
350
350
]
351
351
? // `field!hint!inner(nodes)`
352
352
[ { name : Name ; original : Name ; hint : Hint ; children : Fields } , EatWhitespace < Remainder > ]
353
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
354
- ? ParseEmbeddedResource < EatWhitespace < Remainder > >
355
- : ParserError < 'Expected embedded resource after `!inner`' >
353
+ : CreateParserErrorIfRequired <
354
+ ParseEmbeddedResource < EatWhitespace < Remainder > > ,
355
+ 'Expected embedded resource after `!inner`'
356
+ >
356
357
: ParseEmbeddedResource < EatWhitespace < Remainder > > extends [
357
358
infer Fields ,
358
359
`${infer Remainder } `
359
360
]
360
361
? // `field!hint(nodes)`
361
362
[ { name : Name ; original : Name ; hint : Hint ; children : Fields } , EatWhitespace < Remainder > ]
362
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
363
- ? ParseEmbeddedResource < EatWhitespace < Remainder > >
364
- : ParserError < 'Expected embedded resource after `!hint`' >
363
+ : CreateParserErrorIfRequired <
364
+ ParseEmbeddedResource < EatWhitespace < Remainder > > ,
365
+ 'Expected embedded resource after `!hint`'
366
+ >
365
367
: ParserError < 'Expected identifier after `!`' >
366
- : EatWhitespace < Remainder > extends `:${infer Remainder } `
367
- ? ParseIdentifier < EatWhitespace < Remainder > > extends [ infer OriginalName , `${infer Remainder } `]
368
- ? EatWhitespace < Remainder > extends `::${infer Remainder } `
369
- ? ParseIdentifier < Remainder > extends [ infer CastType , `${infer Remainder } `]
370
- ? // `renamed_field:field::type`
371
- CastType extends PostgreSQLTypes
372
- ? [ { name : Name ; type : TypeScriptTypes < CastType > } , EatWhitespace < Remainder > ]
373
- : never
374
- : ParserError < `Unexpected type cast at \`${Input } \``>
375
- : EatWhitespace < Remainder > extends `!inner${infer Remainder } `
376
- ? ParseEmbeddedResource < EatWhitespace < Remainder > > extends [
377
- infer Fields ,
378
- `${infer Remainder } `
379
- ]
380
- ? // `renamed_field:field!inner(nodes)`
381
- [ { name : Name ; original : OriginalName ; children : Fields } , EatWhitespace < Remainder > ]
382
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
383
- ? ParseEmbeddedResource < EatWhitespace < Remainder > >
384
- : ParserError < 'Expected embedded resource after `!inner`' >
385
- : EatWhitespace < Remainder > extends `!${infer Remainder } `
386
- ? ParseIdentifier < EatWhitespace < Remainder > > extends [ infer Hint , `${infer Remainder } `]
387
- ? EatWhitespace < Remainder > extends `!inner${infer Remainder } `
388
- ? ParseEmbeddedResource < EatWhitespace < Remainder > > extends [
389
- infer Fields ,
390
- `${infer Remainder } `
391
- ]
392
- ? // `renamed_field:field!hint!inner(nodes)`
393
- [
394
- { name : Name ; original : OriginalName ; hint : Hint ; children : Fields } ,
395
- EatWhitespace < Remainder >
396
- ]
397
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
398
- ? ParseEmbeddedResource < EatWhitespace < Remainder > >
399
- : ParserError < 'Expected embedded resource after `!inner`' >
400
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends [
401
- infer Fields ,
402
- `${infer Remainder } `
403
- ]
404
- ? // `renamed_field:field!hint(nodes)`
405
- [
406
- {
407
- name : Name
408
- original : OriginalName
409
- hint : Hint
410
- children : Fields
411
- } ,
412
- EatWhitespace < Remainder >
413
- ]
414
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
415
- ? ParseEmbeddedResource < EatWhitespace < Remainder > >
416
- : ParserError < 'Expected embedded resource after `!hint`' >
417
- : ParserError < 'Expected identifier after `!`' >
418
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends [
419
- infer Fields ,
420
- `${infer Remainder } `
421
- ]
422
- ? // `renamed_field:field(nodes)`
423
- [ { name : Name ; original : OriginalName ; children : Fields } , EatWhitespace < Remainder > ]
424
- : ParseJsonAccessor < EatWhitespace < Remainder > > extends [
425
- infer _PropertyName ,
426
- infer PropertyType ,
427
- `${infer Remainder } `
428
- ]
429
- ? // `renamed_field:field->json...`
430
- [ { name : Name ; type : PropertyType } , EatWhitespace < Remainder > ]
431
- : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
432
- ? ParseEmbeddedResource < EatWhitespace < Remainder > >
433
- : // `renamed_field:field`
434
- [ { name : Name ; original : OriginalName } , EatWhitespace < Remainder > ]
435
- : ParseIdentifier < EatWhitespace < Remainder > >
436
368
: ParseEmbeddedResource < EatWhitespace < Remainder > > extends [ infer Fields , `${infer Remainder } `]
437
369
? // `field(nodes)`
438
370
[ { name : Name ; original : Name ; children : Fields } , EatWhitespace < Remainder > ]
@@ -442,13 +374,48 @@ type ParseNode<Input extends string> = Input extends ''
442
374
`${infer Remainder } `
443
375
]
444
376
? // `field->json...`
445
- [ { name : PropertyName ; type : PropertyType } , EatWhitespace < Remainder > ]
377
+ [ { name : PropertyName ; original : PropertyName ; type : PropertyType } , EatWhitespace < Remainder > ]
446
378
: ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
447
379
? ParseEmbeddedResource < EatWhitespace < Remainder > >
380
+ : EatWhitespace < Remainder > extends `::${infer Remainder } `
381
+ ? ParseIdentifier < Remainder > extends [ `${infer CastType } `, `${infer Remainder } `]
382
+ ? // `field::type`
383
+ CastType extends PostgreSQLTypes
384
+ ? [ { name : Name ; type : TypeScriptTypes < CastType > } , EatWhitespace < Remainder > ]
385
+ : ParserError < `Invalid type for \`::\` operator \`$ { CastType } \``>
386
+ : ParserError < `Invalid type for \`::\` operator at \`$ { Remainder } \``>
448
387
: // `field`
449
388
[ { name : Name ; original : Name } , EatWhitespace < Remainder > ]
450
389
: ParserError < `Expected identifier at \`${Input } \``>
451
390
391
+ /**
392
+ * Parses a node.
393
+ * A node is one of the following:
394
+ * - `*`
395
+ * - a field, as defined above
396
+ * - a renamed field, `renamed_field:field`
397
+ */
398
+ type ParseNode < Input extends string > = Input extends ''
399
+ ? ParserError < 'Empty string' >
400
+ : // `*`
401
+ Input extends `*${infer Remainder } `
402
+ ? [ { star : true } , EatWhitespace < Remainder > ]
403
+ : ParseIdentifier < Input > extends [ infer Name , `${infer Remainder } `]
404
+ ? EatWhitespace < Remainder > extends `::${infer _Remainder } `
405
+ ? // `field::`
406
+ // Special case to detect type-casting before renaming.
407
+ ParseField < Input >
408
+ : EatWhitespace < Remainder > extends `:${infer Remainder } `
409
+ ? // `renamed_field:`
410
+ ParseField < EatWhitespace < Remainder > > extends [ infer Field , `${infer Remainder } `]
411
+ ? Field extends { name : string }
412
+ ? [ Prettify < Omit < Field , 'name' > & { name : Name } > , EatWhitespace < Remainder > ]
413
+ : ParserError < `Unable to parse renamed field`>
414
+ : ParserError < `Unable to parse renamed field`>
415
+ : // Otherwise, just parse it as a field without renaming.
416
+ ParseField < Input >
417
+ : ParserError < `Expected identifier at \`${Input } \``>
418
+
452
419
/**
453
420
* Parses a JSON property accessor of the shape `->a->b->c`. The last accessor in
454
421
* the series may convert to text by using the ->> operator instead of ->.
@@ -560,7 +527,9 @@ type GetResultHelper<
560
527
/**
561
528
* Constructs a type definition for an object based on a given PostgREST query.
562
529
*
563
- * @param Row Record<string, unknown>.
530
+ * @param Schema Database schema.
531
+ * @param Row Type of a row in the given table.
532
+ * @param Relationships Relationships between different tables in the database.
564
533
* @param Query Select query string literal to parse.
565
534
*/
566
535
export type GetResult <
0 commit comments