1
1
import { Models } from './models' ;
2
- import { Service } from './service' ;
3
2
4
3
/**
5
4
* Payload type representing a key-value pair with string keys and any values.
@@ -48,7 +47,7 @@ type RealtimeRequest = {
48
47
/**
49
48
* Realtime event response structure with generic payload type.
50
49
*/
51
- export type RealtimeResponseEvent < T extends unknown > = {
50
+ type RealtimeResponseEvent < T extends unknown > = {
52
51
/**
53
52
* List of event names associated with the response.
54
53
*/
@@ -215,7 +214,7 @@ type Realtime = {
215
214
/**
216
215
* Type representing upload progress information.
217
216
*/
218
- export type UploadProgress = {
217
+ type UploadProgress = {
219
218
/**
220
219
* Identifier for the upload progress.
221
220
*/
@@ -284,6 +283,8 @@ class AppwriteException extends Error {
284
283
* Client that handles requests to Appwrite
285
284
*/
286
285
class Client {
286
+ static CHUNK_SIZE = 1024 * 1024 * 5 ;
287
+
287
288
/**
288
289
* Holds configuration such as project.
289
290
*/
@@ -295,15 +296,14 @@ class Client {
295
296
locale : '' ,
296
297
session : '' ,
297
298
} ;
298
-
299
299
/**
300
300
* Custom headers for API requests.
301
301
*/
302
302
headers : Headers = {
303
303
'x-sdk-name' : 'Web' ,
304
304
'x-sdk-platform' : 'client' ,
305
305
'x-sdk-language' : 'web' ,
306
- 'x-sdk-version' : '16.0.0-rc.2 ' ,
306
+ 'x-sdk-version' : '16.0.0-rc.3 ' ,
307
307
'X-Appwrite-Response-Format' : '1.6.0' ,
308
308
} ;
309
309
@@ -350,7 +350,6 @@ class Client {
350
350
this . config . project = value ;
351
351
return this ;
352
352
}
353
-
354
353
/**
355
354
* Set JWT
356
355
*
@@ -365,7 +364,6 @@ class Client {
365
364
this . config . jwt = value ;
366
365
return this ;
367
366
}
368
-
369
367
/**
370
368
* Set Locale
371
369
*
@@ -378,7 +376,6 @@ class Client {
378
376
this . config . locale = value ;
379
377
return this ;
380
378
}
381
-
382
379
/**
383
380
* Set Session
384
381
*
@@ -394,7 +391,6 @@ class Client {
394
391
return this ;
395
392
}
396
393
397
-
398
394
private realtime : Realtime = {
399
395
socket : undefined ,
400
396
timeout : undefined ,
@@ -578,40 +574,18 @@ class Client {
578
574
}
579
575
}
580
576
581
- /**
582
- * Call API endpoint with the specified method, URL, headers, and parameters.
583
- *
584
- * @param {string } method - HTTP method (e.g., 'GET', 'POST', 'PUT', 'DELETE').
585
- * @param {URL } url - The URL of the API endpoint.
586
- * @param {Headers } headers - Custom headers for the API request.
587
- * @param {Payload } params - Request parameters.
588
- * @returns {Promise<any> } - A promise that resolves with the response data.
589
- *
590
- * @typedef {Object } Payload - Request payload data.
591
- * @property {string } key - The key.
592
- * @property {string } value - The value.
593
- */
594
- async call ( method : string , url : URL , headers : Headers = { } , params : Payload = { } ) : Promise < any > {
577
+ prepareRequest ( method : string , url : URL , headers : Headers = { } , params : Payload = { } ) : { uri : string , options : RequestInit } {
595
578
method = method . toUpperCase ( ) ;
596
579
597
-
598
580
headers = Object . assign ( { } , this . headers , headers ) ;
599
581
600
582
let options : RequestInit = {
601
583
method,
602
584
headers,
603
- credentials : 'include'
604
585
} ;
605
586
606
- if ( typeof window !== 'undefined' && window . localStorage ) {
607
- const cookieFallback = window . localStorage . getItem ( 'cookieFallback' ) ;
608
- if ( cookieFallback ) {
609
- headers [ 'X-Fallback-Cookies' ] = cookieFallback ;
610
- }
611
- }
612
-
613
587
if ( method === 'GET' ) {
614
- for ( const [ key , value ] of Object . entries ( Service . flatten ( params ) ) ) {
588
+ for ( const [ key , value ] of Object . entries ( Client . flatten ( params ) ) ) {
615
589
url . searchParams . append ( key , value ) ;
616
590
}
617
591
} else {
@@ -621,15 +595,17 @@ class Client {
621
595
break ;
622
596
623
597
case 'multipart/form-data' :
624
- let formData = new FormData ( ) ;
625
-
626
- for ( const key in params ) {
627
- if ( Array . isArray ( params [ key ] ) ) {
628
- params [ key ] . forEach ( ( value : any ) => {
629
- formData . append ( key + '[]' , value ) ;
630
- } )
598
+ const formData = new FormData ( ) ;
599
+
600
+ for ( const [ key , value ] of Object . entries ( params ) ) {
601
+ if ( value instanceof File ) {
602
+ formData . append ( key , value , value . name ) ;
603
+ } else if ( Array . isArray ( value ) ) {
604
+ for ( const nestedValue of value ) {
605
+ formData . append ( `${ key } []` , nestedValue ) ;
606
+ }
631
607
} else {
632
- formData . append ( key , params [ key ] ) ;
608
+ formData . append ( key , value ) ;
633
609
}
634
610
}
635
611
@@ -639,45 +615,121 @@ class Client {
639
615
}
640
616
}
641
617
642
- try {
643
- let data = null ;
644
- const response = await fetch ( url . toString ( ) , options ) ;
618
+ return { uri : url . toString ( ) , options } ;
619
+ }
620
+
621
+ async chunkedUpload ( method : string , url : URL , headers : Headers = { } , originalPayload : Payload = { } , onProgress : ( progress : UploadProgress ) => void ) {
622
+ const file = Object . values ( originalPayload ) . find ( ( value ) => value instanceof File ) ;
623
+
624
+ if ( file . size <= Client . CHUNK_SIZE ) {
625
+ return await this . call ( method , url , headers , originalPayload ) ;
626
+ }
627
+
628
+ let start = 0 ;
629
+ let response = null ;
645
630
646
- const warnings = response . headers . get ( 'x-appwrite-warning' ) ;
647
- if ( warnings ) {
648
- warnings . split ( ';' ) . forEach ( ( warning : string ) => console . warn ( 'Warning: ' + warning ) ) ;
631
+ while ( start < file . size ) {
632
+ let end = start + Client . CHUNK_SIZE ; // Prepare end for the next chunk
633
+ if ( end >= file . size ) {
634
+ end = file . size ; // Adjust for the last chunk to include the last byte
649
635
}
650
636
651
- if ( response . headers . get ( 'content-type' ) ?. includes ( 'application/json' ) ) {
652
- data = await response . json ( ) ;
653
- } else {
654
- data = {
655
- message : await response . text ( )
656
- } ;
637
+ headers [ 'content-range' ] = `bytes ${ start } -${ end - 1 } /${ file . size } ` ;
638
+ const chunk = file . slice ( start , end ) ;
639
+
640
+ let payload = { ...originalPayload , file : new File ( [ chunk ] , file . name ) } ;
641
+
642
+ response = await this . call ( method , url , headers , payload ) ;
643
+
644
+ if ( onProgress && typeof onProgress === 'function' ) {
645
+ onProgress ( {
646
+ $id : response . $id ,
647
+ progress : Math . round ( ( end / file . size ) * 100 ) ,
648
+ sizeUploaded : end ,
649
+ chunksTotal : Math . ceil ( file . size / Client . CHUNK_SIZE ) ,
650
+ chunksUploaded : Math . ceil ( end / Client . CHUNK_SIZE )
651
+ } ) ;
657
652
}
658
653
659
- if ( 400 <= response . status ) {
660
- throw new AppwriteException ( data ?. message , response . status , data ?. type , data ) ;
654
+ if ( response && response . $id ) {
655
+ headers [ 'x-appwrite-id' ] = response . $id ;
661
656
}
662
657
663
- const cookieFallback = response . headers . get ( 'X-Fallback-Cookies' ) ;
658
+ start = end ;
659
+ }
664
660
665
- if ( typeof window !== 'undefined' && window . localStorage && cookieFallback ) {
666
- window . console . warn ( 'Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.' ) ;
667
- window . localStorage . setItem ( 'cookieFallback' , cookieFallback ) ;
668
- }
661
+ return response ;
662
+ }
663
+
664
+ async redirect ( method : string , url : URL , headers : Headers = { } , params : Payload = { } ) : Promise < string > {
665
+ const { uri, options } = this . prepareRequest ( method , url , headers , params ) ;
666
+
667
+ const response = await fetch ( uri , {
668
+ ...options ,
669
+ redirect : 'manual'
670
+ } ) ;
671
+
672
+ if ( response . status !== 301 && response . status !== 302 ) {
673
+ throw new AppwriteException ( 'Invalid redirect' , response . status ) ;
674
+ }
675
+
676
+ return response . headers . get ( 'location' ) || '' ;
677
+ }
678
+
679
+ async call ( method : string , url : URL , headers : Headers = { } , params : Payload = { } , responseType = 'json' ) : Promise < any > {
680
+ const { uri, options } = this . prepareRequest ( method , url , headers , params ) ;
681
+
682
+ let data : any = null ;
683
+
684
+ const response = await fetch ( uri , options ) ;
685
+
686
+ const warnings = response . headers . get ( 'x-appwrite-warning' ) ;
687
+ if ( warnings ) {
688
+ warnings . split ( ';' ) . forEach ( ( warning : string ) => console . warn ( 'Warning: ' + warning ) ) ;
689
+ }
669
690
670
- return data ;
671
- } catch ( e ) {
672
- if ( e instanceof AppwriteException ) {
673
- throw e ;
691
+ if ( response . headers . get ( 'content-type' ) ?. includes ( 'application/json' ) ) {
692
+ data = await response . json ( ) ;
693
+ } else if ( responseType === 'arrayBuffer' ) {
694
+ data = await response . arrayBuffer ( ) ;
695
+ } else {
696
+ data = {
697
+ message : await response . text ( )
698
+ } ;
699
+ }
700
+
701
+ if ( 400 <= response . status ) {
702
+ throw new AppwriteException ( data ?. message , response . status , data ?. type , data ) ;
703
+ }
704
+
705
+ const cookieFallback = response . headers . get ( 'X-Fallback-Cookies' ) ;
706
+
707
+ if ( typeof window !== 'undefined' && window . localStorage && cookieFallback ) {
708
+ window . console . warn ( 'Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.' ) ;
709
+ window . localStorage . setItem ( 'cookieFallback' , cookieFallback ) ;
710
+ }
711
+
712
+ return data ;
713
+ }
714
+
715
+ static flatten ( data : Payload , prefix = '' ) : Payload {
716
+ let output : Payload = { } ;
717
+
718
+ for ( const [ key , value ] of Object . entries ( data ) ) {
719
+ let finalKey = prefix ? prefix + '[' + key + ']' : key ;
720
+ if ( Array . isArray ( value ) ) {
721
+ output = { ...output , ...Client . flatten ( value , finalKey ) } ;
722
+ } else {
723
+ output [ finalKey ] = value ;
674
724
}
675
- throw new AppwriteException ( ( < Error > e ) . message ) ;
676
725
}
726
+
727
+ return output ;
677
728
}
678
729
}
679
730
680
731
export { Client , AppwriteException } ;
681
732
export { Query } from './query' ;
682
- export type { Models , Payload } ;
733
+ export type { Models , Payload , UploadProgress } ;
734
+ export type { RealtimeResponseEvent } ;
683
735
export type { QueryTypes , QueryTypesList } from './query' ;
0 commit comments