@@ -8,7 +8,7 @@ import type {
8
8
PostgresView ,
9
9
} from '../../lib/index.js'
10
10
import type { GeneratorMetadata } from '../../lib/generators.js'
11
- import { GENERATE_TYPES_DEFAULT_SCHEMA } from '../constants.js'
11
+ import { GENERATE_TYPES_DEFAULT_SCHEMA , VALID_FUNCTION_ARGS_MODE } from '../constants.js'
12
12
13
13
export const apply = async ( {
14
14
schemas,
@@ -26,15 +26,99 @@ export const apply = async ({
26
26
detectOneToOneRelationships : boolean
27
27
postgrestVersion ?: string
28
28
} ) : Promise < string > => {
29
+ schemas . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
30
+
29
31
const columnsByTableId = Object . fromEntries < PostgresColumn [ ] > (
30
32
[ ...tables , ...foreignTables , ...views , ...materializedViews ] . map ( ( t ) => [ t . id , [ ] ] )
31
33
)
32
- columns
33
- . filter ( ( c ) => c . table_id in columnsByTableId )
34
- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
35
- . forEach ( ( c ) => {
36
- columnsByTableId [ c . table_id ] . push ( c )
37
- } )
34
+ for ( const column of columns ) {
35
+ if ( column . table_id in columnsByTableId ) {
36
+ columnsByTableId [ column . table_id ] . push ( column )
37
+ }
38
+ }
39
+ for ( const tableId in columnsByTableId ) {
40
+ columnsByTableId [ tableId ] . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
41
+ }
42
+
43
+ const introspectionBySchema = Object . fromEntries < {
44
+ tables : Pick < PostgresTable , 'id' | 'name' | 'schema' | 'columns' > [ ]
45
+ views : PostgresView [ ]
46
+ functions : { fn : PostgresFunction ; inArgs : PostgresFunction [ 'args' ] } [ ]
47
+ enums : PostgresType [ ]
48
+ compositeTypes : PostgresType [ ]
49
+ } > (
50
+ schemas . map ( ( s ) => [
51
+ s . name ,
52
+ { tables : [ ] , views : [ ] , functions : [ ] , enums : [ ] , compositeTypes : [ ] } ,
53
+ ] )
54
+ )
55
+ for ( const table of tables ) {
56
+ if ( table . schema in introspectionBySchema ) {
57
+ introspectionBySchema [ table . schema ] . tables . push ( table )
58
+ }
59
+ }
60
+ for ( const table of foreignTables ) {
61
+ if ( table . schema in introspectionBySchema ) {
62
+ introspectionBySchema [ table . schema ] . tables . push ( table )
63
+ }
64
+ }
65
+ for ( const view of views ) {
66
+ if ( view . schema in introspectionBySchema ) {
67
+ introspectionBySchema [ view . schema ] . views . push ( view )
68
+ }
69
+ }
70
+ for ( const materializedView of materializedViews ) {
71
+ if ( materializedView . schema in introspectionBySchema ) {
72
+ introspectionBySchema [ materializedView . schema ] . views . push ( {
73
+ ...materializedView ,
74
+ is_updatable : false ,
75
+ } )
76
+ }
77
+ }
78
+ for ( const func of functions ) {
79
+ if ( func . schema in introspectionBySchema ) {
80
+ func . args . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
81
+ // Either:
82
+ // 1. All input args are be named, or
83
+ // 2. There is only one input arg which is unnamed
84
+ const inArgs = func . args . filter ( ( { mode } ) => VALID_FUNCTION_ARGS_MODE . has ( mode ) )
85
+
86
+ if (
87
+ // Case 1: Function has a single parameter
88
+ inArgs . length === 1 ||
89
+ // Case 2: All input args are named
90
+ ! inArgs . some ( ( { name } ) => name === '' )
91
+ ) {
92
+ introspectionBySchema [ func . schema ] . functions . push ( { fn : func , inArgs } )
93
+ }
94
+ }
95
+ }
96
+ for ( const type of types ) {
97
+ if ( type . schema in introspectionBySchema ) {
98
+ if ( type . enums . length > 0 ) {
99
+ introspectionBySchema [ type . schema ] . enums . push ( type )
100
+ }
101
+ if ( type . attributes . length > 0 ) {
102
+ introspectionBySchema [ type . schema ] . compositeTypes . push ( type )
103
+ }
104
+ }
105
+ }
106
+ for ( const schema in introspectionBySchema ) {
107
+ introspectionBySchema [ schema ] . tables . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
108
+ introspectionBySchema [ schema ] . views . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
109
+ introspectionBySchema [ schema ] . functions . sort ( ( a , b ) => a . fn . name . localeCompare ( b . fn . name ) )
110
+ introspectionBySchema [ schema ] . enums . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
111
+ introspectionBySchema [ schema ] . compositeTypes . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
112
+ }
113
+
114
+ // group types by id for quicker lookup
115
+ const typesById = types . reduce (
116
+ ( acc , type ) => {
117
+ acc [ type . id ] = type
118
+ return acc
119
+ } ,
120
+ { } as Record < number , ( typeof types ) [ number ] >
121
+ )
38
122
39
123
const internal_supabase_schema = postgrestVersion
40
124
? `// Allows to automatically instantiate createClient with right options
@@ -49,44 +133,15 @@ export type Json = string | number | boolean | null | { [key: string]: Json | un
49
133
50
134
export type Database = {
51
135
${ internal_supabase_schema }
52
- ${ schemas
53
- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
54
- . map ( ( schema ) => {
55
- const schemaTables = [ ...tables , ...foreignTables ]
56
- . filter ( ( table ) => table . schema === schema . name )
57
- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
58
- const schemaViews = [ ...views , ...materializedViews ]
59
- . filter ( ( view ) => view . schema === schema . name )
60
- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
61
- const schemaFunctions = functions
62
- . filter ( ( func ) => {
63
- if ( func . schema !== schema . name ) {
64
- return false
65
- }
66
-
67
- // Either:
68
- // 1. All input args are be named, or
69
- // 2. There is only one input arg which is unnamed
70
- const inArgs = func . args . filter ( ( { mode } ) => [ 'in' , 'inout' , 'variadic' ] . includes ( mode ) )
71
-
72
- if ( ! inArgs . some ( ( { name } ) => name === '' ) ) {
73
- return true
74
- }
75
-
76
- if ( inArgs . length === 1 ) {
77
- return true
78
- }
79
-
80
- return false
81
- } )
82
- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
83
- const schemaEnums = types
84
- . filter ( ( type ) => type . schema === schema . name && type . enums . length > 0 )
85
- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
86
- const schemaCompositeTypes = types
87
- . filter ( ( type ) => type . schema === schema . name && type . attributes . length > 0 )
88
- . sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
89
- return `${ JSON . stringify ( schema . name ) } : {
136
+ ${ schemas . map ( ( schema ) => {
137
+ const {
138
+ tables : schemaTables ,
139
+ views : schemaViews ,
140
+ functions : schemaFunctions ,
141
+ enums : schemaEnums ,
142
+ compositeTypes : schemaCompositeTypes ,
143
+ } = introspectionBySchema [ schema . name ]
144
+ return `${ JSON . stringify ( schema . name ) } : {
90
145
Tables: {
91
146
${
92
147
schemaTables . length === 0
@@ -105,9 +160,9 @@ export type Database = {
105
160
} ) } ${ column . is_nullable ? '| null' : '' } `
106
161
) ,
107
162
...schemaFunctions
108
- . filter ( ( fn ) => fn . argument_types === table . name )
109
- . map ( ( fn ) => {
110
- const type = types . find ( ( { id } ) => id === fn . return_type_id )
163
+ . filter ( ( { fn } ) => fn . argument_types === table . name )
164
+ . map ( ( { fn } ) => {
165
+ const type = typesById [ fn . return_type_id ]
111
166
let tsType = 'unknown'
112
167
if ( type ) {
113
168
tsType = pgTypeToTsType ( schema , type . name , {
@@ -226,7 +281,7 @@ export type Database = {
226
281
) }
227
282
}
228
283
${
229
- 'is_updatable' in view && view . is_updatable
284
+ view . is_updatable
230
285
? `Insert: {
231
286
${ columnsByTableId [ view . id ] . map ( ( column ) => {
232
287
let output = JSON . stringify ( column . name )
@@ -306,28 +361,29 @@ export type Database = {
306
361
307
362
const schemaFunctionsGroupedByName = schemaFunctions . reduce (
308
363
( acc , curr ) => {
309
- acc [ curr . name ] ??= [ ]
310
- acc [ curr . name ] . push ( curr )
364
+ acc [ curr . fn . name ] ??= [ ]
365
+ acc [ curr . fn . name ] . push ( curr )
311
366
return acc
312
367
} ,
313
- { } as Record < string , PostgresFunction [ ] >
368
+ { } as Record < string , typeof schemaFunctions >
314
369
)
370
+ for ( const fnName in schemaFunctionsGroupedByName ) {
371
+ schemaFunctionsGroupedByName [ fnName ] . sort ( ( a , b ) =>
372
+ b . fn . definition . localeCompare ( a . fn . definition )
373
+ )
374
+ }
315
375
316
376
return Object . entries ( schemaFunctionsGroupedByName ) . map (
317
377
( [ fnName , fns ] ) =>
318
378
`${ JSON . stringify ( fnName ) } : {
319
379
Args: ${ fns
320
- . map ( ( { args } ) => {
321
- const inArgs = args
322
- . toSorted ( ( a , b ) => a . name . localeCompare ( b . name ) )
323
- . filter ( ( { mode } ) => mode === 'in' )
324
-
380
+ . map ( ( { inArgs } ) => {
325
381
if ( inArgs . length === 0 ) {
326
382
return 'Record<PropertyKey, never>'
327
383
}
328
384
329
385
const argsNameAndType = inArgs . map ( ( { name, type_id, has_default } ) => {
330
- const type = types . find ( ( { id } ) => id === type_id )
386
+ const type = typesById [ type_id ]
331
387
let tsType = 'unknown'
332
388
if ( type ) {
333
389
tsType = pgTypeToTsType ( schema , type . name , {
@@ -346,10 +402,10 @@ export type Database = {
346
402
. join ( ' | ' ) }
347
403
Returns: ${ ( ( ) => {
348
404
// Case 1: `returns table`.
349
- const tableArgs = fns [ 0 ] . args . filter ( ( { mode } ) => mode === 'table' )
405
+ const tableArgs = fns [ 0 ] . fn . args . filter ( ( { mode } ) => mode === 'table' )
350
406
if ( tableArgs . length > 0 ) {
351
407
const argsNameAndType = tableArgs . map ( ( { name, type_id } ) => {
352
- const type = types . find ( ( { id } ) => id === type_id )
408
+ const type = typesById [ type_id ]
353
409
let tsType = 'unknown'
354
410
if ( type ) {
355
411
tsType = pgTypeToTsType ( schema , type . name , {
@@ -371,7 +427,7 @@ export type Database = {
371
427
372
428
// Case 2: returns a relation's row type.
373
429
const relation = [ ...tables , ...views ] . find (
374
- ( { id } ) => id === fns [ 0 ] . return_type_relation_id
430
+ ( { id } ) => id === fns [ 0 ] . fn . return_type_relation_id
375
431
)
376
432
if ( relation ) {
377
433
return `{
@@ -394,7 +450,7 @@ export type Database = {
394
450
}
395
451
396
452
// Case 3: returns base/array/composite/enum type.
397
- const type = types . find ( ( { id } ) => id === fns [ 0 ] . return_type_id )
453
+ const type = typesById [ fns [ 0 ] . fn . return_type_id ]
398
454
if ( type ) {
399
455
return pgTypeToTsType ( schema , type . name , {
400
456
types,
@@ -405,7 +461,7 @@ export type Database = {
405
461
}
406
462
407
463
return 'unknown'
408
- } ) ( ) } ${ fns [ 0 ] . is_set_returning_function ? '[]' : '' }
464
+ } ) ( ) } ${ fns [ 0 ] . fn . is_set_returning_function ? '[]' : '' }
409
465
}`
410
466
)
411
467
} ) ( ) }
@@ -430,7 +486,7 @@ export type Database = {
430
486
( { name, attributes } ) =>
431
487
`${ JSON . stringify ( name ) } : {
432
488
${ attributes . map ( ( { name, type_id } ) => {
433
- const type = types . find ( ( { id } ) => id === type_id )
489
+ const type = typesById [ type_id ]
434
490
let tsType = 'unknown'
435
491
if ( type ) {
436
492
tsType = `${ pgTypeToTsType ( schema , type . name , {
@@ -447,7 +503,7 @@ export type Database = {
447
503
}
448
504
}
449
505
}`
450
- } ) }
506
+ } ) }
451
507
}
452
508
453
509
type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>
0 commit comments