Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,18 +339,18 @@ try {
- Use the [parseRejected][api-reference] option to throw a custom error instead.
- Use the [reject][api-reference] option to decide **when** to throw.

#### 👉 ValidationError
#### 👉 ResponseValidationError

Raised when schema validation fails. \
Use `isValidationError` to identify this error type.
Use `isResponseValidationError` to identify this error type.

```ts
import { isValidationError } from 'up-fetch'
import { isResponseValidationError } from 'up-fetch'

try {
await upfetch('/todos/1', { schema: todoSchema })
} catch (error) {
if (isValidationError(error)) {
if (isResponseValidationError(error)) {
console.log(error.issues)
}
}
Expand Down Expand Up @@ -553,7 +553,7 @@ const upfetch = up(fetch, () => ({
// custom error message
const message = `Request failed with status ${status}: ${JSON.stringify(data)}`
// you can return a custom error class as well
return new ResponseError({ message, data, request, response })
return new ResponseError({ message, status, data })
},
}))
```
Expand Down Expand Up @@ -725,9 +725,9 @@ Options:

Checks if the error is a `ResponseError`.

### <samp>isValidationError(error)</samp>
### <samp>isResponseValidationError(error)</samp>

Checks if the error is a `ValidationError`.
Checks if the error is a `ResponseValidationError`.

### <samp>isJsonifiable(value)</samp>

Expand Down
14 changes: 7 additions & 7 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,18 +335,18 @@ try {
- 使用 [parseRejected][api-reference] 选项抛出自定义错误。
- 使用 [reject][api-reference] 选项决定**何时**抛出错误。

#### 👉 <samp>ValidationError</samp>
#### 👉 <samp>ResponseValidationError</samp>

当模式验证失败时抛出。\
使用 `isValidationError` 识别此错误类型。
使用 `isResponseValidationError` 识别此错误类型。

```ts
import { isValidationError } from 'up-fetch'
import { isResponseValidationError } from 'up-fetch'

try {
await upfetch('/todos/1', { schema: todoSchema })
} catch (error) {
if (isValidationError(error)) {
if (isResponseValidationError(error)) {
console.log(error.issues)
}
}
Expand Down Expand Up @@ -549,7 +549,7 @@ const upfetch = up(fetch, () => ({
// 自定义错误消息
const message = `Request failed with status ${status}: ${JSON.stringify(data)}`
// 你也可以返回自定义错误类
return new ResponseError({ message, data, request, response })
return new ResponseError({ message, status, data })
},
}))
```
Expand Down Expand Up @@ -719,9 +719,9 @@ function upfetch(

检查错误是否为 `ResponseError`。

### <samp>isValidationError(error)</samp>
### <samp>isResponseValidationError(error)</samp>

检查错误是否为 `ValidationError`。
检查错误是否为 `ResponseValidationError`。

### <samp>isJsonifiable(value)</samp>

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions src/fallback-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ export const fallbackOptions: FallbackOptions = {
parseRejected: async (response, request) =>
new ResponseError({
message: `[${response.status}] ${response.statusText}`,
status: response.status,
data: await fallbackOptions.parseResponse(response, request),
response,
request,
}),

// TODO: find a lighter way to do this with about the same amount of code
Expand Down
13 changes: 12 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,15 @@ export type {
} from './types'
export { up } from './up'
export { isJsonifiable } from './utils'
export { isValidationError, ValidationError } from './validation-error'
export {
isResponseValidationError,
/**
* @deprecated Use `isResponseValidationError` instead.
*/
isResponseValidationError as isValidationError,
ResponseValidationError,
/**
* @deprecated Use `ResponseValidationError` instead.
*/
ResponseValidationError as ValidationError,
} from './validation-error'
20 changes: 6 additions & 14 deletions src/response-error.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
export class ResponseError<TData = any> extends Error {
response: Response
request: Request
data: TData
status: number
kind: 'response'

constructor(props: {
message: string
response: Response
data: TData
request: Request
}) {
// super(`[${response.status}] ${response.statusText}`)
constructor(props: { message: string; status: number; data: TData }) {
super(props.message)
this.name = 'ResponseError'
this.response = props.response
this.request = props.request
this.kind = 'response'
this.data = props.data
this.status = props.response.status
this.status = props.status
}
}

export const isResponseError = <TData = any>(
error: unknown,
): error is ResponseError<TData> => error instanceof ResponseError
): error is ResponseError<TData> =>
typeof error === 'object' && (error as ResponseError)?.kind === 'response'
21 changes: 19 additions & 2 deletions src/tests/dist.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { expectTypeOf, test } from 'vitest'
import { expect, expectTypeOf, test } from 'vitest'
import { z } from 'zod'
import { up } from '../../dist'
import {
isResponseValidationError,
isValidationError,
ResponseValidationError,
up,
ValidationError,
} from '../../dist'

/**
* testing the build output with `declaration: true` in tsconfig.json
Expand All @@ -26,3 +32,14 @@ test('type inference should still work after the build', () => {

noop(testCase)
})

test('legacy validation error exports should remain available after the build', () => {
expect(ValidationError).toBe(ResponseValidationError)
expect(isValidationError).toBe(isResponseValidationError)
expectTypeOf<typeof ValidationError>().toEqualTypeOf<
typeof ResponseValidationError
>()
expectTypeOf<typeof isValidationError>().toEqualTypeOf<
typeof isResponseValidationError
>()
})
5 changes: 2 additions & 3 deletions src/tests/fallback-options.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ describe('parseRejected', () => {
)
expect(responseError instanceof ResponseError).toBeTruthy()
expect(responseError.data).toStrictEqual(output)
expect(responseError.response).toStrictEqual(response)
expect(responseError.request).toBe(request)
expect(responseError.name).toStrictEqual('ResponseError')
expect(responseError.status).toStrictEqual(200)
expect(responseError.kind).toStrictEqual('response')
})
})
12 changes: 6 additions & 6 deletions src/tests/on-error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import {
import { z } from 'zod'
import {
isResponseError,
isValidationError,
isResponseValidationError,
type ResponseError,
type ResponseValidationError,
up,
type ValidationError,
} from '..'

const server = setupServer()
Expand All @@ -37,19 +37,19 @@ describe('onError', () => {
const upfetch = up(fetch, () => ({
baseUrl: baseUrl,
onError(error, request) {
if (isValidationError(error)) {
if (isResponseValidationError(error)) {
exec++
expectTypeOf(error).toEqualTypeOf<ValidationError>()
expectTypeOf(error).toEqualTypeOf<ResponseValidationError>()
}
},
}))

await upfetch('', {
schema: z.object({ hello: z.number() }),
onError(error, request) {
if (isValidationError(error)) {
if (isResponseValidationError(error)) {
exec++
expectTypeOf(error).toEqualTypeOf<ValidationError>()
expectTypeOf(error).toEqualTypeOf<ResponseValidationError>()
}
},
}).catch(() => {})
Expand Down
4 changes: 2 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
Params,
SerializeParams,
} from './types'
import { ValidationError } from './validation-error'
import { ResponseValidationError } from './validation-error'

export const mergeHeaders = (
headerInits: (HeadersInit | HeadersObject | undefined)[],
Expand Down Expand Up @@ -112,6 +112,6 @@ export async function validate<TSchema extends StandardSchemaV1>(
data: StandardSchemaV1.InferInput<TSchema>,
): Promise<StandardSchemaV1.InferOutput<TSchema>> {
const result = await schema['~standard'].validate(data)
if (result.issues) throw new ValidationError(result, data)
if (result.issues) throw new ResponseValidationError(result, data)
return result.value
}
13 changes: 8 additions & 5 deletions src/validation-error.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import type { StandardSchemaV1 } from '@standard-schema/spec'

export class ValidationError<TData = any> extends Error {
override name: 'ValidationError'
export class ResponseValidationError<TData = any> extends Error {
issues: readonly StandardSchemaV1.Issue[]
kind: 'validation'
data: TData

constructor(result: StandardSchemaV1.FailureResult, data: TData) {
super(JSON.stringify(result.issues))
this.name = 'ValidationError'
this.kind = 'validation'
this.issues = result.issues
this.data = data
}
}

export const isValidationError = (error: unknown): error is ValidationError =>
error instanceof ValidationError
export const isResponseValidationError = (
error: unknown,
): error is ResponseValidationError =>
typeof error === 'object' &&
(error as ResponseValidationError)?.kind === 'validation'