Skip to content

Commit

Permalink
feat: Accept more bodyInit types such as FormData, URLSearchParams, B…
Browse files Browse the repository at this point in the history
…lob, ArrayBuffer, ReadableStream, etc
  • Loading branch information
gustavoguichard committed May 8, 2023
1 parent 68f1387 commit 31dd708
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 9 deletions.
19 changes: 18 additions & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,28 @@ describe('ensureStringBody', () => {
})

it('should return the same if body was not defined', () => {
expect(subject.ensureStringBody()).toBe(undefined)
expect(subject.ensureStringBody()).toBeUndefined()
})

it('should stringify the body if it is a JSON-like value', () => {
expect(subject.ensureStringBody({ page: 2 })).toBe(`{"page":2}`)
expect(subject.ensureStringBody([1, 2])).toBe(`[1,2]`)
expect(subject.ensureStringBody(3)).toBe(`3`)
expect(subject.ensureStringBody(true)).toBe(`true`)
expect(subject.ensureStringBody({})).toBe(`{}`)
})

it('should not stringify other valid kinds of BodyInit', () => {
const ab = new ArrayBuffer(0)
expect(subject.ensureStringBody(ab)).toBe(ab)
const rs = new ReadableStream()
expect(subject.ensureStringBody(rs)).toBe(rs)
const fd = new FormData()
expect(subject.ensureStringBody(fd)).toBe(fd)
const usp = new URLSearchParams()
expect(subject.ensureStringBody(usp)).toBe(usp)
const blob = new Blob()
expect(subject.ensureStringBody(blob)).toBe(blob)
})
})

Expand Down
30 changes: 29 additions & 1 deletion src/internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,32 @@ function replaceUrlParams(
return url instanceof URL ? new URL(urlString) : urlString
}

export { getJson, getText, replaceUrlParams }
/**
* This is an enhanced version of the typeof operator to check the type of more complex values.
* @param t the value to be checked
* @returns the type of the value
*/
function typeOf(t: unknown) {
return Object.prototype.toString
.call(t)
.replace(/^\[object (.+)\]$/, '$1')
.toLowerCase() as
| 'array'
| 'arraybuffer'
| 'bigint'
| 'blob'
| 'boolean'
| 'formdata'
| 'function'
| 'null'
| 'number'
| 'object'
| 'readablestream'
| 'string'
| 'symbol'
| 'undefined'
| 'url'
| 'urlsearchparams'
}

export { getJson, getText, replaceUrlParams, typeOf }
19 changes: 13 additions & 6 deletions src/make-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HTTP_METHODS } from './constants'
import { getJson, getText, replaceUrlParams } from './internals'
import { getJson, getText, replaceUrlParams, typeOf } from './internals'
import {
EnhancedRequestInit,
HTTPMethod,
Expand Down Expand Up @@ -104,12 +104,19 @@ function typedResponse(response: Response): TypedResponse {

/**
* @param body the JSON-like body of the request
* @returns the body stringified if it is not a string
* @returns the body is stringified if it is not a string and it is a JSON-like object. It also accepts other types of BodyInit such as Blob, ReadableStream, etc.
*/
function ensureStringBody(body?: JSONValue): string | undefined {
if (typeof body === 'undefined') return
if (typeof body === 'string') return body
return JSON.stringify(body)
function ensureStringBody<B extends JSONValue | BodyInit | null>(
body?: B,
): B extends JSONValue ? string : B {
if (typeof body === 'undefined') return body as never
if (typeof body === 'string') return body as never

return (
['number', 'boolean', 'array', 'object'].includes(typeOf(body))
? JSON.stringify(body)
: body
) as never
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type TypedResponse = Omit<Response, 'json' | 'text'> & {
}

type EnhancedRequestInit = Omit<RequestInit, 'body'> & {
body?: JSONValue
body?: JSONValue | BodyInit | null
query?: SearchParams
params?: Record<string, string>
trace?: (...args: Parameters<typeof fetch>) => void
Expand Down

0 comments on commit 31dd708

Please sign in to comment.