@@ -109,6 +109,13 @@ type ParserError<Message extends string> = { error: true } & Message
109109type GenericStringError = ParserError < 'Received a generic string' >
110110export type SelectQueryError < Message extends string > = { error : true } & Message
111111
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+
112119/**
113120 * Trims whitespace from the left of the input.
114121 */
@@ -118,6 +125,9 @@ type EatWhitespace<Input extends string> = string extends Input
118125 ? EatWhitespace < Remainder >
119126 : Input
120127
128+ /**
129+ * Returns a boolean representing whether there is a foreign key with the given name.
130+ */
121131type HasFKey < FKeyName , Relationships > = Relationships extends [ infer R ]
122132 ? R extends { foreignKeyName : FKeyName }
123133 ? true
@@ -128,6 +138,9 @@ type HasFKey<FKeyName, Relationships> = Relationships extends [infer R]
128138 : HasFKey < FKeyName , Rest >
129139 : false
130140
141+ /**
142+ * Returns a boolean representing whether there the foreign key has a unique constraint.
143+ */
131144type HasUniqueFKey < FKeyName , Relationships > = Relationships extends [ infer R ]
132145 ? R extends { foreignKeyName : FKeyName ; isOneToOne : true }
133146 ? true
@@ -138,6 +151,10 @@ type HasUniqueFKey<FKeyName, Relationships> = Relationships extends [infer R]
138151 : HasUniqueFKey < FKeyName , Rest >
139152 : false
140153
154+ /**
155+ * Returns a boolean representing whether there is a foreign key referencing
156+ * a given relation.
157+ */
141158type HasFKeyToFRel < FRelName , Relationships > = Relationships extends [ infer R ]
142159 ? R extends { referencedRelation : FRelName }
143160 ? true
@@ -161,8 +178,9 @@ type HasUniqueFKeyToFRel<FRelName, Relationships> = Relationships extends [infer
161178/**
162179 * Constructs a type definition for a single field of an object.
163180 *
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.
166184 * @param Field Single field parsed by `ParseQuery`.
167185 */
168186type ConstructFieldDefinition <
@@ -231,12 +249,12 @@ type ConstructFieldDefinition<
231249 : Child [ ]
232250 : never
233251 }
252+ : Field extends { name : string ; type : infer T }
253+ ? { [ K in Field [ 'name' ] ] : T }
234254 : Field extends { name : string ; original : string }
235255 ? Field [ 'original' ] extends keyof Row
236256 ? { [ K in Field [ 'name' ] ] : Row [ Field [ 'original' ] ] }
237257 : SelectQueryError < `Referencing missing column \`${Field [ 'original' ] } \``>
238- : Field extends { name : string ; type : infer T }
239- ? { [ K in Field [ 'name' ] ] : T }
240258 : Record < string , unknown >
241259
242260/**
@@ -246,8 +264,7 @@ type ConstructFieldDefinition<
246264 */
247265
248266/**
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_]`.
251268 */
252269type ReadLetters < Input extends string > = string extends Input
253270 ? GenericStringError
@@ -266,7 +283,7 @@ type ReadLettersHelper<Input extends string, Acc extends string> = string extend
266283 : [ Acc , '' ]
267284
268285/**
269- * Reads a consecutive sequence of more than 1 double-quoted letters,
286+ * Reads a consecutive sequence of 1 or more double-quoted letters,
270287 * where letters are `[^"]`.
271288 */
272289type ReadQuotedLetters < Input extends string > = string extends Input
@@ -289,7 +306,7 @@ type ReadQuotedLettersHelper<Input extends string, Acc extends string> = string
289306
290307/**
291308 * 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 .
293310 */
294311type ParseIdentifier < Input extends string > = ReadLetters < Input > extends [
295312 infer Name ,
@@ -301,46 +318,29 @@ type ParseIdentifier<Input extends string> = ReadLetters<Input> extends [
301318 : ParserError < `No (possibly double-quoted) identifier at \`${Input } \``>
302319
303320/**
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:
307323 * - `field`
308324 * - `field::type`
309325 * - `field->json...`
310326 * - `field(nodes)`
311327 * - `field!hint(nodes)`
312328 * - `field!inner(nodes)`
313329 * - `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)`
321330 *
322- * TODO: more support for JSON operators `-> `, `->>`.
331+ * TODO: support type casting of JSON operators `a->b::type `, `a ->>b::type `.
323332 */
324- type ParseNode < Input extends string > = Input extends ''
333+ type ParseField < Input extends string > = Input extends ''
325334 ? ParserError < 'Empty string' >
326- : // `*`
327- Input extends `*${infer Remainder } `
328- ? [ { star : true } , EatWhitespace < Remainder > ]
329335 : 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 } `
338337 ? ParseEmbeddedResource < EatWhitespace < Remainder > > extends [ infer Fields , `${infer Remainder } `]
339338 ? // `field!inner(nodes)`
340339 [ { 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+ >
344344 : EatWhitespace < Remainder > extends `!${infer Remainder } `
345345 ? ParseIdentifier < EatWhitespace < Remainder > > extends [ infer Hint , `${infer Remainder } `]
346346 ? EatWhitespace < Remainder > extends `!inner${infer Remainder } `
@@ -350,89 +350,21 @@ type ParseNode<Input extends string> = Input extends ''
350350 ]
351351 ? // `field!hint!inner(nodes)`
352352 [ { 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+ >
356357 : ParseEmbeddedResource < EatWhitespace < Remainder > > extends [
357358 infer Fields ,
358359 `${infer Remainder } `
359360 ]
360361 ? // `field!hint(nodes)`
361362 [ { 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+ >
365367 : 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 > >
436368 : ParseEmbeddedResource < EatWhitespace < Remainder > > extends [ infer Fields , `${infer Remainder } `]
437369 ? // `field(nodes)`
438370 [ { name : Name ; original : Name ; children : Fields } , EatWhitespace < Remainder > ]
@@ -442,13 +374,48 @@ type ParseNode<Input extends string> = Input extends ''
442374 `${infer Remainder } `
443375 ]
444376 ? // `field->json...`
445- [ { name : PropertyName ; type : PropertyType } , EatWhitespace < Remainder > ]
377+ [ { name : PropertyName ; original : PropertyName ; type : PropertyType } , EatWhitespace < Remainder > ]
446378 : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
447379 ? 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 } \``>
448387 : // `field`
449388 [ { name : Name ; original : Name } , EatWhitespace < Remainder > ]
450389 : ParserError < `Expected identifier at \`${Input } \``>
451390
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+
452419/**
453420 * Parses a JSON property accessor of the shape `->a->b->c`. The last accessor in
454421 * the series may convert to text by using the ->> operator instead of ->.
@@ -560,7 +527,9 @@ type GetResultHelper<
560527/**
561528 * Constructs a type definition for an object based on a given PostgREST query.
562529 *
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.
564533 * @param Query Select query string literal to parse.
565534 */
566535export type GetResult <
0 commit comments