@@ -3,8 +3,7 @@ import { fromZodError } from "zod-validation-error";
3
3
import { ApiConnectionError , ApiError } from "./errors" ;
4
4
import { RetryOptions } from "../schemas" ;
5
5
import { calculateNextRetryDelay } from "../utils/retries" ;
6
- import { FormDataEncoder } from "form-data-encoder" ;
7
- import { Readable } from "node:stream" ;
6
+
8
7
import {
9
8
CursorPage ,
10
9
CursorPageParams ,
@@ -114,59 +113,6 @@ export function zodfetchOffsetLimitPage<TItemSchema extends z.ZodTypeAny>(
114
113
return new OffsetLimitPagePromise ( fetchResult , schema , url , params , requestInit , options ) ;
115
114
}
116
115
117
- export function zodupload <
118
- TResponseBodySchema extends z . ZodTypeAny ,
119
- TBody = Record < string , unknown > ,
120
- > (
121
- schema : TResponseBodySchema ,
122
- url : string ,
123
- body : TBody ,
124
- requestInit ?: RequestInit ,
125
- options ?: ZodFetchOptions
126
- ) : ApiPromise < z . output < TResponseBodySchema > > {
127
- const finalRequestInit = createMultipartFormRequestInit ( body , requestInit ) ;
128
-
129
- return new ApiPromise ( _doZodFetch ( schema , url , finalRequestInit , options ) ) ;
130
- }
131
-
132
- async function createMultipartFormRequestInit < TBody = Record < string , unknown > > (
133
- body : TBody ,
134
- requestInit ?: RequestInit
135
- ) : Promise < RequestInit > {
136
- const form = await createForm ( body ) ;
137
- const encoder = new FormDataEncoder ( form ) ;
138
-
139
- const finalHeaders : Record < string , string > = { } ;
140
-
141
- for ( const [ key , value ] of Object . entries ( requestInit ?. headers || { } ) ) {
142
- finalHeaders [ key ] = value as string ;
143
- }
144
-
145
- for ( const [ key , value ] of Object . entries ( encoder . headers ) ) {
146
- finalHeaders [ key ] = value ;
147
- }
148
-
149
- finalHeaders [ "Content-Length" ] = String ( encoder . contentLength ) ;
150
-
151
- const finalRequestInit : RequestInit = {
152
- ...requestInit ,
153
- headers : finalHeaders ,
154
- body : Readable . from ( encoder ) as any ,
155
- // @ts -expect-error
156
- duplex : "half" ,
157
- } ;
158
-
159
- return finalRequestInit ;
160
- }
161
-
162
- const createForm = async < T = Record < string , unknown > > ( body : T | undefined ) : Promise < FormData > => {
163
- const form = new FormData ( ) ;
164
- await Promise . all (
165
- Object . entries ( body || { } ) . map ( ( [ key , value ] ) => addFormValue ( form , key , value ) )
166
- ) ;
167
- return form ;
168
- } ;
169
-
170
116
type ZodFetchResult < T > = {
171
117
data : T ;
172
118
response : Response ;
@@ -324,214 +270,6 @@ function requestInitWithCache(requestInit?: RequestInit): RequestInit {
324
270
}
325
271
}
326
272
327
- const addFormValue = async ( form : FormData , key : string , value : unknown ) : Promise < void > => {
328
- if ( value === undefined ) return ;
329
- if ( value == null ) {
330
- throw new TypeError (
331
- `Received null for "${ key } "; to pass null in FormData, you must use the string 'null'`
332
- ) ;
333
- }
334
-
335
- // TODO: make nested formats configurable
336
- if ( typeof value === "string" || typeof value === "number" || typeof value === "boolean" ) {
337
- form . append ( key , String ( value ) ) ;
338
- } else if (
339
- isUploadable ( value ) ||
340
- isBlobLike ( value ) ||
341
- value instanceof Buffer ||
342
- value instanceof ArrayBuffer
343
- ) {
344
- const file = await toFile ( value ) ;
345
- form . append ( key , file as File ) ;
346
- } else if ( Array . isArray ( value ) ) {
347
- await Promise . all ( value . map ( ( entry ) => addFormValue ( form , key + "[]" , entry ) ) ) ;
348
- } else if ( typeof value === "object" ) {
349
- await Promise . all (
350
- Object . entries ( value ) . map ( ( [ name , prop ] ) => addFormValue ( form , `${ key } [${ name } ]` , prop ) )
351
- ) ;
352
- } else {
353
- throw new TypeError (
354
- `Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${ value } instead`
355
- ) ;
356
- }
357
- } ;
358
-
359
- export type ToFileInput = Uploadable | Exclude < BlobLikePart , string > | AsyncIterable < BlobLikePart > ;
360
-
361
- /**
362
- * Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats
363
- * @param value the raw content of the file. Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s
364
- * @param {string= } name the name of the file. If omitted, toFile will try to determine a file name from bits if possible
365
- * @param {Object= } options additional properties
366
- * @param {string= } options.type the MIME type of the content
367
- * @param {number= } options.lastModified the last modified timestamp
368
- * @returns a {@link File} with the given properties
369
- */
370
- export async function toFile (
371
- value : ToFileInput | PromiseLike < ToFileInput > ,
372
- name ?: string | null | undefined ,
373
- options ?: FilePropertyBag | undefined
374
- ) : Promise < FileLike > {
375
- // If it's a promise, resolve it.
376
- value = await value ;
377
-
378
- // Use the file's options if there isn't one provided
379
- options ??= isFileLike ( value ) ? { lastModified : value . lastModified , type : value . type } : { } ;
380
-
381
- if ( isResponseLike ( value ) ) {
382
- const blob = await value . blob ( ) ;
383
- name ||= new URL ( value . url ) . pathname . split ( / [ \\ / ] / ) . pop ( ) ?? "unknown_file" ;
384
-
385
- return new File ( [ blob as any ] , name , options ) ;
386
- }
387
-
388
- const bits = await getBytes ( value ) ;
389
-
390
- name ||= getName ( value ) ?? "unknown_file" ;
391
-
392
- if ( ! options . type ) {
393
- const type = ( bits [ 0 ] as any ) ?. type ;
394
- if ( typeof type === "string" ) {
395
- options = { ...options , type } ;
396
- }
397
- }
398
-
399
- return new File ( bits , name , options ) ;
400
- }
401
-
402
- function getName ( value : any ) : string | undefined {
403
- return (
404
- getStringFromMaybeBuffer ( value . name ) ||
405
- getStringFromMaybeBuffer ( value . filename ) ||
406
- // For fs.ReadStream
407
- getStringFromMaybeBuffer ( value . path ) ?. split ( / [ \\ / ] / ) . pop ( )
408
- ) ;
409
- }
410
-
411
- const getStringFromMaybeBuffer = ( x : string | Buffer | unknown ) : string | undefined => {
412
- if ( typeof x === "string" ) return x ;
413
- if ( typeof Buffer !== "undefined" && x instanceof Buffer ) return String ( x ) ;
414
- return undefined ;
415
- } ;
416
-
417
- async function getBytes ( value : ToFileInput ) : Promise < Array < BlobPart > > {
418
- let parts : Array < BlobPart > = [ ] ;
419
- if (
420
- typeof value === "string" ||
421
- ArrayBuffer . isView ( value ) || // includes Uint8Array, Buffer, etc.
422
- value instanceof ArrayBuffer
423
- ) {
424
- parts . push ( value ) ;
425
- } else if ( isBlobLike ( value ) ) {
426
- parts . push ( await value . arrayBuffer ( ) ) ;
427
- } else if (
428
- isAsyncIterableIterator ( value ) // includes Readable, ReadableStream, etc.
429
- ) {
430
- for await ( const chunk of value ) {
431
- parts . push ( chunk as BlobPart ) ; // TODO, consider validating?
432
- }
433
- } else {
434
- throw new Error (
435
- `Unexpected data type: ${ typeof value } ; constructor: ${ value ?. constructor
436
- ?. name } ; props: ${ propsForError ( value ) } `
437
- ) ;
438
- }
439
-
440
- return parts ;
441
- }
442
-
443
- function propsForError ( value : any ) : string {
444
- const props = Object . getOwnPropertyNames ( value ) ;
445
- return `[${ props . map ( ( p ) => `"${ p } "` ) . join ( ", " ) } ]` ;
446
- }
447
-
448
- const isAsyncIterableIterator = ( value : any ) : value is AsyncIterableIterator < unknown > =>
449
- value != null && typeof value === "object" && typeof value [ Symbol . asyncIterator ] === "function" ;
450
-
451
- /**
452
- * Intended to match web.Blob, node.Blob, node-fetch.Blob, etc.
453
- */
454
- export interface BlobLike {
455
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */
456
- readonly size : number ;
457
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */
458
- readonly type : string ;
459
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */
460
- text ( ) : Promise < string > ;
461
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */
462
- slice ( start ?: number , end ?: number ) : BlobLike ;
463
- // unfortunately @types /node-fetch@^2.6.4 doesn't type the arrayBuffer method
464
- }
465
-
466
- /**
467
- * Intended to match web.File, node.File, node-fetch.File, etc.
468
- */
469
- export interface FileLike extends BlobLike {
470
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */
471
- readonly lastModified : number ;
472
- /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */
473
- readonly name : string ;
474
- }
475
-
476
- /**
477
- * Intended to match web.Response, node.Response, node-fetch.Response, etc.
478
- */
479
- export interface ResponseLike {
480
- url : string ;
481
- blob ( ) : Promise < BlobLike > ;
482
- }
483
-
484
- export type Uploadable = FileLike | ResponseLike | Readable ;
485
-
486
- export const isResponseLike = ( value : any ) : value is ResponseLike =>
487
- value != null &&
488
- typeof value === "object" &&
489
- typeof value . url === "string" &&
490
- typeof value . blob === "function" ;
491
-
492
- export const isFileLike = ( value : any ) : value is FileLike =>
493
- value != null &&
494
- typeof value === "object" &&
495
- typeof value . name === "string" &&
496
- typeof value . lastModified === "number" &&
497
- isBlobLike ( value ) ;
498
-
499
- /**
500
- * The BlobLike type omits arrayBuffer() because @types/node-fetch@^2.6.4 lacks it; but this check
501
- * adds the arrayBuffer() method type because it is available and used at runtime
502
- */
503
- export const isBlobLike = (
504
- value : any
505
- ) : value is BlobLike & { arrayBuffer ( ) : Promise < ArrayBuffer > } =>
506
- value != null &&
507
- typeof value === "object" &&
508
- typeof value . size === "number" &&
509
- typeof value . type === "string" &&
510
- typeof value . text === "function" &&
511
- typeof value . slice === "function" &&
512
- typeof value . arrayBuffer === "function" ;
513
-
514
- export const isFsReadStream = ( value : any ) : value is Readable => value instanceof Readable ;
515
-
516
- export const isUploadable = ( value : any ) : value is Uploadable => {
517
- return isFileLike ( value ) || isResponseLike ( value ) || isFsReadStream ( value ) ;
518
- } ;
519
-
520
- export type BlobLikePart =
521
- | string
522
- | ArrayBuffer
523
- | ArrayBufferView
524
- | BlobLike
525
- | Uint8Array
526
- | DataView ;
527
-
528
- export const isRecordLike = ( value : any ) : value is Record < string , string > =>
529
- value != null &&
530
- typeof value === "object" &&
531
- ! Array . isArray ( value ) &&
532
- Object . keys ( value ) . length > 0 &&
533
- Object . keys ( value ) . every ( ( key ) => typeof key === "string" && typeof value [ key ] === "string" ) ;
534
-
535
273
/**
536
274
* A subclass of `Promise` providing additional helper methods
537
275
* for interacting with the SDK.
0 commit comments