@@ -2,6 +2,7 @@ import prettier from 'prettier'
2
2
import type {
3
3
PostgresColumn ,
4
4
PostgresFunction ,
5
+ PostgresRelationship ,
5
6
PostgresSchema ,
6
7
PostgresTable ,
7
8
PostgresType ,
@@ -20,7 +21,9 @@ export const apply = async ({
20
21
materializedViews,
21
22
columns,
22
23
types,
24
+ relationships,
23
25
} : GeneratorMetadata ) : Promise < string > => {
26
+ // return `/* START GENERATED TYPES */\n` + JSON.stringify(relationships, undefined, 2)
24
27
// Index columns by relation id
25
28
const columnsByTableId = Object . fromEntries < PostgresColumn [ ] > (
26
29
[ ...tables , ...foreignTables , ...views , ...materializedViews ] . map ( ( t ) => [ t . id , [ ] ] )
@@ -69,6 +72,11 @@ export const apply = async ({
69
72
return `${ JSON . stringify ( col . name ) } : ${ z } `
70
73
}
71
74
75
+ const makeRelationshipShapeLine = ( relation : PostgresRelationship , ctx : PgTypeCtx ) => {
76
+ const typeVal = `supabaseZodSchemas.${ relation . referenced_relation } .list`
77
+ return `get ${ relation . referenced_relation } () { return ${ typeVal } .optional() }`
78
+ }
79
+
72
80
const makeUpdateShapeLine = ( col : PostgresColumn , ctx : PgTypeCtx ) => {
73
81
// Update: everything optional; identity ALWAYS -> forbid value if provided
74
82
if ( col . identity_generation === 'ALWAYS' ) {
@@ -90,14 +98,32 @@ export const supabaseZodSchemas = {
90
98
. map ( ( table ) => {
91
99
const ctx : PgTypeCtx = { types, schemas, tables, views }
92
100
const cols = columnsByTableId [ table . id ] ?? [ ]
101
+ const relevantRels = relationships
102
+ . filter (
103
+ ( relationship ) =>
104
+ relationship . schema === table . schema &&
105
+ relationship . referenced_schema === table . schema &&
106
+ relationship . relation === table . name
107
+ )
108
+ . sort (
109
+ ( a , b ) =>
110
+ a . foreign_key_name . localeCompare ( b . foreign_key_name ) ||
111
+ a . referenced_relation . localeCompare ( b . referenced_relation ) ||
112
+ JSON . stringify ( a . referenced_columns ) . localeCompare ( JSON . stringify ( b . referenced_columns ) )
113
+ )
93
114
94
115
const listShape = cols . map ( ( c ) => makeListShapeLine ( c , ctx ) ) . join ( ',\n ' )
116
+ const relationshipShape = relevantRels
117
+ . map ( ( rel ) => makeRelationshipShapeLine ( rel , ctx ) )
118
+ . join ( ',\n ' )
119
+
95
120
const insertShape = cols . map ( ( c ) => makeInsertShapeLine ( c , ctx ) ) . join ( ',\n ' )
96
121
const updateShape = cols . map ( ( c ) => makeUpdateShapeLine ( c , ctx ) ) . join ( ',\n ' )
97
122
98
123
return `${ JSON . stringify ( table . name ) } : {
99
124
list: z.object({
100
- ${ listShape }
125
+ ${ listShape } ,
126
+ ${ relationshipShape }
101
127
}),
102
128
insert: z.object({
103
129
${ insertShape }
@@ -116,6 +142,10 @@ export const supabaseZodSchemas = {
116
142
return out
117
143
}
118
144
145
+ const wrapAsArray = ( zodSchema : string ) : string => {
146
+ return `z.array(${ zodSchema } )`
147
+ }
148
+
119
149
type PgTypeCtx = {
120
150
types : PostgresType [ ]
121
151
schemas : PostgresSchema [ ]
@@ -161,17 +191,19 @@ const pgTypeToZodSchema = (
161
191
162
192
// Arrays: leading underscore means array of the inner type
163
193
if ( pgType . startsWith ( '_' ) ) {
164
- return `z.array(${ pgTypeToZodSchema (
165
- schema ,
166
- pgType . slice ( 1 ) ,
167
- {
168
- types,
169
- schemas,
170
- tables,
171
- views,
172
- } ,
173
- mode
174
- ) } )`
194
+ return wrapAsArray (
195
+ pgTypeToZodSchema (
196
+ schema ,
197
+ pgType . slice ( 1 ) ,
198
+ {
199
+ types,
200
+ schemas,
201
+ tables,
202
+ views,
203
+ } ,
204
+ mode
205
+ )
206
+ )
175
207
}
176
208
177
209
// Enums inline
0 commit comments