@@ -26,6 +26,36 @@ const validatePositiveInteger = (name: string, n: unknown): number => {
26
26
return n ;
27
27
} ;
28
28
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ const appendBodyToFormData = ( formData : FormData , body : Record < string , any > ) : void => {
31
+ for ( const [ key , value ] of Object . entries ( body ) ) {
32
+ if ( Array . isArray ( value ) ) {
33
+ value . forEach ( item => formData . append ( key , item ) ) ;
34
+ } else {
35
+ formData . append ( key , value ) ;
36
+ }
37
+ }
38
+ }
39
+
40
+ export type FilePathOrFileObject =
41
+ | string
42
+ | File ;
43
+
44
+ function makeFormDataFromFilePath ( filePath : string ) : FormData {
45
+ const formData = new FormData ( ) ;
46
+ const fileStream = createReadStream ( filePath ) ;
47
+ const fileName = getBasename ( filePath ) ;
48
+
49
+ formData . append ( 'file' , fileStream , fileName ) ;
50
+ return formData ;
51
+ }
52
+
53
+ function makeFormDataFromFileObject ( file : File ) : FormData {
54
+ const formData = new FormData ( ) ;
55
+ formData . append ( 'file' , file ) ;
56
+ return formData ;
57
+ }
58
+
29
59
export abstract class APIClient {
30
60
protected baseURL : string ;
31
61
protected maxRetries : number ;
@@ -64,10 +94,41 @@ export abstract class APIClient {
64
94
return this . makeRequest ( 'delete' , path , opts ) ;
65
95
}
66
96
67
- async upload < Req , Rsp > ( path : string , filePath : string , opts ?: RequestOptions < Req > ) : Promise < Rsp > {
68
- const formDataRequest = this . makeFormDataRequest ( path , filePath , opts ) ;
69
- const response = await this . performRequest ( formDataRequest ) ;
70
- return this . fetch . handleResponse < Rsp > ( response ) as Rsp ;
97
+ upload < Req , Rsp > ( path : string , file : string , opts ?: RequestOptions < Req > ) : Promise < Rsp > ;
98
+ upload < Req , Rsp > ( path : string , file : File , opts ?: RequestOptions < Req > ) : Promise < Rsp > ;
99
+
100
+
101
+ upload < Req , Rsp > ( path : string , file : FilePathOrFileObject , opts ?: RequestOptions < Req > ) : Promise < Rsp > {
102
+ let formData : FormData ;
103
+
104
+ if ( typeof file === 'string' ) {
105
+ formData = makeFormDataFromFilePath ( file ) ;
106
+ } else if ( file instanceof File ) {
107
+ formData = makeFormDataFromFileObject ( file ) ;
108
+ } else {
109
+ throw new AI21Error ( 'Invalid file type for upload' ) ;
110
+ }
111
+
112
+ if ( opts ?. body ) {
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
+ appendBodyToFormData ( formData , opts . body as Record < string , any > ) ;
115
+ }
116
+
117
+ const headers = {
118
+ ...opts ?. headers ,
119
+ 'Content-Type' : `multipart/form-data; boundary=${ formData . getBoundary ( ) } `
120
+ } ;
121
+
122
+ const options : FinalRequestOptions = {
123
+ method : 'post' ,
124
+ path : path ,
125
+ body : formData ,
126
+ headers,
127
+ } ;
128
+
129
+ return this . performRequest ( options ) . then (
130
+ ( response ) => this . fetch . handleResponse < Rsp > ( response ) as Rsp ,
131
+ ) ;
71
132
}
72
133
73
134
protected makeFormDataRequest < Req > (
@@ -133,6 +194,7 @@ export abstract class APIClient {
133
194
const options = {
134
195
method,
135
196
path,
197
+
136
198
...opts ,
137
199
} ;
138
200
@@ -145,7 +207,8 @@ export abstract class APIClient {
145
207
let url = `${ this . baseURL } ${ options . path } ` ;
146
208
147
209
if ( options . query ) {
148
- const queryString = new URLSearchParams ( options . query as Record < string , string > ) . toString ( ) ;
210
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
211
+ const queryString = new URLSearchParams ( options . query as Record < string , any > ) . toString ( ) ;
149
212
url += `?${ queryString } ` ;
150
213
}
151
214
0 commit comments