Comparing version 0.0.15 to 0.0.16
@@ -6,4 +6,4 @@ function createFetchmap(fetch) { | ||
const validationErrorFailure = (validationError, status) => failure({ validationError, status }); | ||
const mapFun = async (response, map) => typeof map === 'function' | ||
? map(response) | ||
const mapFun = async (response, map) => 'noBody' in map | ||
? map.noBody(response) | ||
: 'json' in map | ||
@@ -25,3 +25,3 @@ ? map.json(await response.json(), response) | ||
try { | ||
const validated = await mapFun(response, (_b = (_a = map[status]) !== null && _a !== void 0 ? _a : (isOk ? map.ok : map.notOk)) !== null && _b !== void 0 ? _b : success); | ||
const validated = await mapFun(response, (_b = (_a = map[status]) !== null && _a !== void 0 ? _a : (isOk ? map.ok : map.notOk)) !== null && _b !== void 0 ? _b : { noBody: success }); | ||
return validated.tag === 'success' | ||
@@ -28,0 +28,0 @@ ? (isOk ? success : serverErrorFailure)(validated.success) |
{ | ||
"name": "fetchmap", | ||
"version": "0.0.15", | ||
"version": "0.0.16", | ||
"description": "Non-throwing fetch wrapper", | ||
@@ -5,0 +5,0 @@ "sideEffects": false, |
@@ -42,8 +42,18 @@ # fetchmap | ||
const success = <T>(value: T) => ({ tag: 'success', success: value } as const) | ||
const fetchmap = createFetchmap(fetch) | ||
const json_success = await fetchmap({ ok: 'json' }, 'https://localhost:5005/json') | ||
const mock_data_validator = (data: unknown) => success(data) | ||
const json_success = await fetchmap( | ||
{ ok: { json: mock_data_validator } }, | ||
'https://localhost:5005/json' | ||
) | ||
expect(json_success).toEqual({ tag: 'success', success: { some: 'data' } }) | ||
const json_success_only_200 = await fetchmap({ 200: 'json' }, 'https://localhost:5005/json') | ||
const json_success_only_200 = await fetchmap( | ||
{ 200: mock_data_validator }, | ||
'https://localhost:5005/json' | ||
) | ||
expect(json_success_only_200).toEqual({ tag: 'success', success: { some: 'data' } }) | ||
@@ -50,0 +60,0 @@ |
291
src/index.ts
@@ -111,123 +111,203 @@ type OkStatus = | ||
} | ||
type SuccessResult<Success> = { | ||
readonly tag: 'success' | ||
readonly success: Success | ||
} | ||
/** | ||
* Creates a `fetchmap` from third-party `fetch` function. | ||
* | ||
* @param fetch A `fetch`-like function. Should take two parameters and return a `Promise<BasicResponse>`. | ||
* `BasicResponse` requires only `status`, `arrayBuffer`, `blob`, `formData`, `json` and `text` | ||
* properties of standard `Response` type to be defined. | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any | ||
export function createFetchmap<Fetch extends (input: any, init?: any) => Promise<BasicResponse>>( | ||
fetch: Fetch | ||
) { | ||
type Input = Parameters<Fetch>[0] | ||
type Init = Parameters<Fetch>[1] | ||
type FailureResult<Failure> = { | ||
readonly tag: 'failure' | ||
readonly failure: Failure | ||
} | ||
type SuccessResult<Success> = { | ||
readonly tag: 'success' | ||
readonly success: Success | ||
} | ||
type SuccessOf<ResultLike> = ResultLike extends SuccessResult<infer S> ? S : never | ||
type FailureOf<ResultLike> = ResultLike extends FailureResult<infer F> ? F : never | ||
type FailureResult<Failure> = { | ||
readonly tag: 'failure' | ||
readonly failure: Failure | ||
} | ||
type Result<Success = unknown, Failure = unknown> = SuccessResult<Success> | FailureResult<Failure> | ||
type SuccessOf<ResultLike> = ResultLike extends SuccessResult<infer S> ? S : never | ||
type FailureOf<ResultLike> = ResultLike extends FailureResult<infer F> ? F : never | ||
type MapTextResponse<Resp extends BasicResponse> = { | ||
readonly text: (text: string, response: Resp) => Result | ||
} | ||
type Result<Success = unknown, Failure = unknown> = | ||
| SuccessResult<Success> | ||
| FailureResult<Failure> | ||
type MapJsonResponse<Resp extends BasicResponse> = { | ||
readonly json: (json: unknown, response: Resp) => Result | ||
} | ||
type MapResponseBody<Resp extends BasicResponse> = | ||
| { readonly text: (text: string, response: Resp) => Result } | ||
| { readonly json: (json: unknown, response: Resp) => Result } | ||
| { readonly blob: (blob: Blob, response: Resp) => Result } | ||
| { readonly arrayBuffer: (arrayBuffer: ArrayBuffer, response: Resp) => Result } | ||
| { readonly formData: (formData: FormData, response: Resp) => Result } | ||
type MapBlobResponse<Resp extends BasicResponse> = { | ||
readonly blob: (blob: Blob, response: Resp) => Result | ||
} | ||
type MapResponse<Resp extends BasicResponse> = | ||
| MapResponseBody<Resp> | ||
| ((response: Resp) => Result) | ||
type MapArrayBufferResponse<Resp extends BasicResponse> = { | ||
readonly arrayBuffer: (arrayBuffer: ArrayBuffer, response: Resp) => Result | ||
} | ||
type MultiMapResponse<Resp extends BasicResponse> = { | ||
readonly [code: number]: MapResponse<Resp> | ||
readonly ok: MapResponse<Resp> | ||
readonly notOk: MapResponse<Resp> | ||
} | ||
type MapFormDataResponse<Resp extends BasicResponse> = { | ||
readonly formData: (formData: FormData, response: Resp) => Result | ||
} | ||
type MapResultOf<Resp extends BasicResponse, Map extends MapResponse<Resp>> = Map extends ( | ||
r: Resp | ||
) => infer T | ||
type MapNoBodyResponse<Resp extends BasicResponse> = { readonly noBody: (response: Resp) => Result } | ||
type MapResponse<Resp extends BasicResponse> = | ||
| MapTextResponse<Resp> | ||
| MapJsonResponse<Resp> | ||
| MapBlobResponse<Resp> | ||
| MapArrayBufferResponse<Resp> | ||
| MapFormDataResponse<Resp> | ||
| MapNoBodyResponse<Resp> | ||
type MultiMapResponse<Resp extends BasicResponse> = { | ||
readonly [code: number]: MapResponse<Resp> | ||
readonly ok: MapResponse<Resp> | ||
readonly notOk: MapResponse<Resp> | ||
} | ||
type MapResultOf<Resp extends BasicResponse, Map extends MapResponse<Resp>> = Map extends { | ||
readonly noBody: (r: Resp) => infer T | ||
} | ||
? T | ||
: Map extends { | ||
readonly text: (b: string, r: Resp) => infer T | ||
} | ||
? ('json' | 'blob' | 'arrayBuffer' | 'formData') & keyof Map extends never | ||
? T | ||
: Map extends { readonly text: (b: string, r: Resp) => infer T } | ||
: never | ||
: Map extends { readonly json: (b: unknown, r: Resp) => infer T } | ||
? ('text' | 'blob' | 'arrayBuffer' | 'formData') & keyof Map extends never | ||
? T | ||
: Map extends { readonly json: (b: unknown, r: Resp) => infer T } | ||
: never | ||
: Map extends { readonly blob: (b: Blob, r: Resp) => infer T } | ||
? ('json' | 'text' | 'arrayBuffer' | 'formData') & keyof Map extends never | ||
? T | ||
: Map extends { readonly blob: (b: Blob, r: Resp) => infer T } | ||
: never | ||
: Map extends { readonly arrayBuffer: (b: ArrayBuffer, r: Resp) => infer T } | ||
? ('json' | 'blob' | 'text' | 'formData') & keyof Map extends never | ||
? T | ||
: Map extends { readonly arrayBuffer: (b: ArrayBuffer, r: Resp) => infer T } | ||
: never | ||
: Map extends { readonly formData: (b: FormData, r: Resp) => infer T } | ||
? ('json' | 'blob' | 'arrayBuffer' | 'text') & keyof Map extends never | ||
? T | ||
: Map extends { readonly formData: (b: FormData, r: Resp) => infer T } | ||
? T | ||
: never | ||
: never | ||
type OkKeys<Resp extends BasicResponse, Map extends MultiMapResponse<Resp>> = (OkStatus | 'ok') & | ||
keyof Map | ||
type OkKeys<Resp extends BasicResponse, Map extends MultiMapResponse<Resp>> = (OkStatus | 'ok') & | ||
keyof Map | ||
type NotOkKeys< | ||
Resp extends BasicResponse, | ||
Map extends MultiMapResponse<Resp>, | ||
Keys = keyof Map | ||
> = Keys extends OkStatus | 'ok' ? never : Keys | ||
type NotOkKeys< | ||
Resp extends BasicResponse, | ||
Map extends MultiMapResponse<Resp>, | ||
Keys = keyof Map | ||
> = Keys extends OkStatus | 'ok' ? never : Keys | ||
type FetchSuccessResult< | ||
Resp extends BasicResponse, | ||
Map extends MultiMapResponse<Resp>, | ||
SuccessKeys extends keyof Map = OkKeys<Resp, Map> | ||
> = SuccessResult< | ||
Map[SuccessKeys] extends MapResponse<Resp> | ||
? SuccessOf<MapResultOf<Resp, Map[SuccessKeys]>> | ||
type FetchFailureResult< | ||
Resp extends BasicResponse, | ||
Map extends MultiMapResponse<Resp>, | ||
FailureKeys extends keyof Map = NotOkKeys<Resp, Map>, | ||
SuccessKeys extends keyof Map = OkKeys<Resp, Map> | ||
> = FailureResult< | ||
| { | ||
readonly status: number | ||
readonly validationError: FailureOf< | ||
| (Map[SuccessKeys] extends MapJsonResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['json']> | ||
: Map[SuccessKeys] extends MapBlobResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['blob']> | ||
: Map[SuccessKeys] extends MapTextResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['text']> | ||
: Map[SuccessKeys] extends MapFormDataResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['formData']> | ||
: Map[SuccessKeys] extends MapArrayBufferResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['arrayBuffer']> | ||
: Map[SuccessKeys] extends MapNoBodyResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['noBody']> | ||
: Map[SuccessKeys] extends MapResponse<Resp> | ||
? MapResultOf<Resp, Map[SuccessKeys]> | ||
: never) | ||
| (Map[FailureKeys] extends MapJsonResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['json']> | ||
: Map[FailureKeys] extends MapBlobResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['blob']> | ||
: Map[FailureKeys] extends MapTextResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['text']> | ||
: Map[FailureKeys] extends MapFormDataResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['formData']> | ||
: Map[FailureKeys] extends MapArrayBufferResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['arrayBuffer']> | ||
: Map[FailureKeys] extends MapNoBodyResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['noBody']> | ||
: Map[FailureKeys] extends MapResponse<Resp> | ||
? MapResultOf<Resp, Map[FailureKeys]> | ||
: never) | ||
> | ||
} | ||
| { | ||
readonly serverError: SuccessOf< | ||
Map[FailureKeys] extends MapJsonResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['json']> | ||
: Map[FailureKeys] extends MapBlobResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['blob']> | ||
: Map[FailureKeys] extends MapTextResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['text']> | ||
: Map[FailureKeys] extends MapFormDataResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['formData']> | ||
: Map[FailureKeys] extends MapArrayBufferResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['arrayBuffer']> | ||
: Map[FailureKeys] extends MapNoBodyResponse<Resp> | ||
? ReturnType<Map[FailureKeys]['noBody']> | ||
: Map[FailureKeys] extends MapResponse<Resp> | ||
? MapResultOf<Resp, Map[FailureKeys]> | ||
: never | ||
> | ||
} | ||
| { readonly clientError: unknown } | ||
| { readonly mapError: unknown } | ||
> | ||
type FetchSuccessResult< | ||
Resp extends BasicResponse, | ||
Map extends MultiMapResponse<Resp>, | ||
SuccessKeys extends keyof Map = OkKeys<Resp, Map> | ||
> = SuccessResult< | ||
SuccessOf< | ||
Map[SuccessKeys] extends MapJsonResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['json']> | ||
: Map[SuccessKeys] extends MapBlobResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['blob']> | ||
: Map[SuccessKeys] extends MapTextResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['text']> | ||
: Map[SuccessKeys] extends MapFormDataResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['formData']> | ||
: Map[SuccessKeys] extends MapArrayBufferResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['arrayBuffer']> | ||
: Map[SuccessKeys] extends MapNoBodyResponse<Resp> | ||
? ReturnType<Map[SuccessKeys]['noBody']> | ||
: Map[SuccessKeys] extends MapResponse<Resp> | ||
? MapResultOf<Resp, Map[SuccessKeys]> | ||
: never | ||
> | ||
> | ||
type FetchFailureResult< | ||
Resp extends BasicResponse, | ||
Map extends MultiMapResponse<Resp>, | ||
FailureKeys extends keyof Map = NotOkKeys<Resp, Map> | ||
> = FailureResult< | ||
| (( | ||
Map[keyof Map] extends MapResponse<Resp> | ||
? FailureOf<MapResultOf<Resp, Map[keyof Map]>> | ||
: never | ||
) extends never | ||
? never | ||
: { | ||
readonly status: number | ||
readonly validationError: Map[keyof Map] extends MapResponse<Resp> | ||
? FailureOf<MapResultOf<Resp, Map[keyof Map]>> | ||
: never | ||
}) | ||
| { | ||
readonly serverError: Map[FailureKeys] extends MapResponse<Resp> | ||
? SuccessOf<MapResultOf<Resp, Map[FailureKeys]>> | ||
: never | ||
} | ||
| { readonly clientError: unknown } | ||
| { readonly mapError: unknown } | ||
> | ||
type FetchResult< | ||
Resp extends BasicResponse, | ||
PartialMap extends Partial<MultiMapResponse<Resp>>, | ||
Map extends MultiMapResponse<Resp> = { | ||
readonly [Key in keyof PartialMap | 'ok' | 'notOk']: PartialMap[Key] extends MapResponse<Resp> | ||
? PartialMap[Key] | ||
: { readonly noBody: (response: Resp) => SuccessResult<Resp> } | ||
} | ||
> = FetchSuccessResult<Resp, Map> | FetchFailureResult<Resp, Map> | ||
type FetchResult< | ||
Resp extends BasicResponse, | ||
PartialMap extends Partial<MultiMapResponse<Resp>>, | ||
Map extends MultiMapResponse<Resp> = { | ||
readonly [Key in keyof PartialMap | 'ok' | 'notOk']: PartialMap[Key] extends MapResponse<Resp> | ||
? PartialMap[Key] | ||
: (response: Resp) => SuccessResult<Resp> | ||
} | ||
> = FetchSuccessResult<Resp, Map> | FetchFailureResult<Resp, Map> | ||
type PrettyType<V> = Extract<{ [K in keyof V]: V[K] }, unknown> | ||
type PrettyType<V> = Extract<{ [K in keyof V]: V[K] }, unknown> | ||
/** | ||
* Creates a `fetchmap` from third-party `fetch` function. | ||
* | ||
* @param fetch A `fetch`-like function. Should take two parameters and return a `Promise<BasicResponse>`. | ||
* `BasicResponse` requires only `status`, `arrayBuffer`, `blob`, `formData`, `json` and `text` | ||
* properties of standard `Response` type to be defined. | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any | ||
export function createFetchmap<Fetch extends (input: any, init?: any) => Promise<BasicResponse>>( | ||
fetch: Fetch | ||
) { | ||
type Input = Parameters<Fetch>[0] | ||
type Init = Parameters<Fetch>[1] | ||
@@ -242,4 +322,4 @@ const success = <T>(value: T) => ({ tag: 'success', success: value } as const) | ||
const mapFun = async <Resp extends BasicResponse>(response: Resp, map: MapResponse<Resp>) => | ||
typeof map === 'function' | ||
? map(response) | ||
'noBody' in map | ||
? map.noBody(response) | ||
: 'json' in map | ||
@@ -286,7 +366,6 @@ ? map.json(await response.json(), response) | ||
*/ | ||
function fetchmap<Map extends Partial<MultiMapResponse<Awaited<ReturnType<Fetch>>>>>( | ||
map: Map, | ||
input: Input, | ||
init?: Init | ||
): Promise<PrettyType<FetchResult<Awaited<ReturnType<Fetch>>, Map>>> | ||
function fetchmap< | ||
Response extends Awaited<ReturnType<Fetch>>, | ||
Map extends Partial<MultiMapResponse<Response>> | ||
>(map: Map, input: Input, init?: Init): Promise<PrettyType<FetchResult<Response, Map>>> | ||
@@ -306,3 +385,3 @@ function fetchmap( | ||
response, | ||
map[status] ?? (isOk ? map.ok : map.notOk) ?? success | ||
map[status] ?? (isOk ? map.ok : map.notOk) ?? { noBody: success } | ||
) | ||
@@ -309,0 +388,0 @@ |
@@ -72,3 +72,3 @@ import { spawn } from 'child_process' | ||
test('ok, ok is function', async () => { | ||
const result = await fetchmap({ ok: (r) => success(r.status) }, url('json')) | ||
const result = await fetchmap({ ok: { noBody: (r) => success(r.status) } }, url('json')) | ||
expect(result).toEqual<typeof result>(success(200)) | ||
@@ -117,7 +117,7 @@ }) | ||
test('fail, mapError, ok is function', async () => { | ||
const ok = () => { | ||
const noBody = () => { | ||
throw new Error('what?') | ||
} | ||
const result = await fetchmap({ ok }, url('text')) | ||
const result = await fetchmap({ ok: { noBody } }, url('text')) | ||
expect(result).toEqual<typeof result>(failure({ mapError: new Error('what?') })) | ||
@@ -157,3 +157,6 @@ }) | ||
test('fail, serverError, notOk is function', async () => { | ||
const result = await fetchmap({ notOk: (r) => success(r.status) }, url('server-error')) | ||
const result = await fetchmap( | ||
{ notOk: { noBody: (r) => success(r.status) } }, | ||
url('server-error') | ||
) | ||
expect(result).toEqual<typeof result>(failure({ serverError: 500 })) | ||
@@ -160,0 +163,0 @@ }) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
81
77434
1007