react-native-vision-camera/package/src/CameraError.ts
Marc Rousavy cc60ad296a
fix: Validate input props (fps, hdr, torch, ...) instead of silently crashing (#2354)
* fix: Fix Blackscreen by deterministically destroying session if `isActive=false`

* Re-open Camera if session died

* Simplify Camera

* Disconnect is optional, block when resetting state

* fix: Log in `configure { ... }`

* fix: Make concurrent configure safe

* fix: Don't resize preview

* fix: Use current `CameraConfiguration`

* Don't start if no outputs are available

* Only mount with preview outputs

* Update CameraSession.kt

* Update PreviewView.kt

* Better logging

* Update CameraSession.kt

* Extract

* fix: Rebuild entire session if `isActive` changed

* isActive safe

* Start session at 1

* Create ActiveCameraDevice.kt

* interrupts

* chore: Freeze `frame` in `useFrameProcessor`

* Revert "chore: Freeze `frame` in `useFrameProcessor`"

This reverts commit dff93d506e29a791d8dea8842b880ab5c892211e.

* chore: Better logging

* fix: Move HDR to `video`/`photo` config

* fix: Fix hdr usage

* fix: Ignore any updates after destroying Camera

* fix: Fix video HDR

* chore: Format code

* fix: Check Camera permission

* Remove unneeded error

* Update CameraSession.kt

* Update CameraPage.tsx

* Delete OutputConfiguration.toDebugString.kt

* Update CameraSession.kt

* fix: Perform sanity checks to make sure props are valid

* format
2024-01-08 12:13:05 +01:00

209 lines
6.4 KiB
TypeScript

export type PermissionError = 'permission/microphone-permission-denied' | 'permission/camera-permission-denied'
export type ParameterError =
| 'parameter/invalid-parameter'
| 'parameter/unsupported-output'
| 'parameter/unsupported-input'
| 'parameter/invalid-combination'
export type DeviceError =
| 'device/configuration-error'
| 'device/no-device'
| 'device/invalid-device'
| 'device/microphone-unavailable'
| 'device/pixel-format-not-supported'
| 'device/low-light-boost-not-supported'
| 'device/focus-not-supported'
| 'device/camera-not-available-on-simulator'
export type FormatError =
| 'format/invalid-fps'
| 'format/invalid-video-hdr'
| 'format/invalid-video-stabilization-mode'
| 'format/incompatible-pixel-format-with-hdr-setting'
| 'format/invalid-format'
| 'format/format-required'
export type SessionError =
| 'session/camera-not-ready'
| 'session/camera-cannot-be-opened'
| 'session/camera-has-been-disconnected'
| 'session/audio-in-use-by-other-app'
| 'session/audio-session-failed-to-activate'
export type CodeScannerError =
| 'code-scanner/not-compatible-with-outputs'
| 'code-scanner/code-type-not-supported'
| 'code-scanner/cannot-load-model'
export type CaptureError =
| 'capture/recording-in-progress'
| 'capture/no-recording-in-progress'
| 'capture/file-io-error'
| 'capture/create-temp-file-error'
| 'capture/create-recorder-error'
| 'capture/recorder-error'
| 'capture/video-not-enabled'
| 'capture/photo-not-enabled'
| 'capture/aborted'
| 'capture/unknown'
export type SystemError =
| 'system/camera-module-not-found'
| 'system/no-camera-manager'
| 'system/frame-processors-unavailable'
| 'system/view-not-found'
export type UnknownError = 'unknown/unknown'
/**
* Represents a JSON-style error cause. This contains native `NSError`/`Throwable` information, and can have recursive {@linkcode ErrorWithCause.cause | .cause} properties until the ultimate cause has been found.
*/
export interface ErrorWithCause {
/**
* The native error's code.
*
* * iOS: `NSError.code`
* * Android: N/A
*/
code?: number
/**
* The native error's domain.
*
* * iOS: `NSError.domain`
* * Android: N/A
*/
domain?: string
/**
* The native error description
*
* * iOS: `NSError.message`
* * Android: `Throwable.message`
*/
message: string
/**
* Optional additional details
*
* * iOS: `NSError.userInfo`
* * Android: N/A
*/
details?: Record<string, unknown>
/**
* Optional Java stacktrace
*
* * iOS: N/A
* * Android: `Throwable.stacktrace.toString()`
*/
stacktrace?: string
/**
* Optional additional cause for nested errors
*
* * iOS: N/A
* * Android: `Throwable.cause`
*/
cause?: ErrorWithCause
}
type CameraErrorCode =
| PermissionError
| ParameterError
| DeviceError
| FormatError
| SessionError
| CaptureError
| SystemError
| UnknownError
/**
* Represents any kind of error that occured in the {@linkcode Camera} View Module.
*/
class CameraError<TCode extends CameraErrorCode> extends Error {
private readonly _code: TCode
private readonly _message: string
private readonly _cause?: ErrorWithCause
public get code(): TCode {
return this._code
}
public get message(): string {
return this._message
}
public get cause(): Error | undefined {
const c = this._cause
if (c == null) return undefined
return new Error(`[${c.code}]: ${c.message}`)
}
/**
* @internal
*/
constructor(code: TCode, message: string, cause?: ErrorWithCause) {
super(`[${code}]: ${message}${cause != null ? ` (Cause: ${cause.message})` : ''}`)
super.name = code
super.message = message
this._code = code
this._message = message
this._cause = cause
}
public toString(): string {
return `[${this.code}]: ${this.message}`
}
}
/**
* Represents any kind of error that occured while trying to capture a video or photo.
*
* See the ["Camera Errors" documentation](https://react-native-vision-camera.com/docs/guides/errors) for more information about Camera Errors.
*/
export class CameraCaptureError extends CameraError<CaptureError> {}
/**
* Represents any kind of error that occured in the Camera View Module.
*
* See the ["Camera Errors" documentation](https://react-native-vision-camera.com/docs/guides/errors) for more information about Camera Errors.
*/
export class CameraRuntimeError extends CameraError<
PermissionError | ParameterError | DeviceError | FormatError | SessionError | SystemError | UnknownError
> {}
/**
* Checks if the given `error` is of type {@linkcode ErrorWithCause}
* @param {unknown} error Any unknown object to validate
* @returns `true` if the given `error` is of type {@linkcode ErrorWithCause}
*/
export const isErrorWithCause = (error: unknown): error is ErrorWithCause =>
typeof error === 'object' &&
error != null &&
// @ts-expect-error error is still unknown
typeof error.message === 'string' &&
// @ts-expect-error error is still unknown
(typeof error.stacktrace === 'string' || error.stacktrace == null) &&
// @ts-expect-error error is still unknown
(isErrorWithCause(error.cause) || error.cause == null)
const isCameraErrorJson = (error: unknown): error is { code: string; message: string; cause?: ErrorWithCause } =>
typeof error === 'object' &&
error != null &&
// @ts-expect-error error is still unknown
typeof error.code === 'string' &&
// @ts-expect-error error is still unknown
typeof error.message === 'string' &&
// @ts-expect-error error is still unknown
(typeof error.cause === 'object' || error.cause == null)
/**
* Tries to parse an error coming from native to a typed JS camera error.
* @param {CameraError} nativeError The native error instance. This is a JSON in the legacy native module architecture.
* @returns A {@linkcode CameraRuntimeError} or {@linkcode CameraCaptureError}, or the `nativeError` itself if it's not parsable
* @method
*/
export const tryParseNativeCameraError = <T>(nativeError: T): (CameraRuntimeError | CameraCaptureError) | T => {
if (isCameraErrorJson(nativeError)) {
if (nativeError.code.startsWith('capture')) {
return new CameraCaptureError(nativeError.code as CaptureError, nativeError.message, nativeError.cause)
} else {
return new CameraRuntimeError(
// @ts-expect-error the code is string, we narrow it down to TS union.
nativeError.code,
nativeError.message,
nativeError.cause,
)
}
} else {
return nativeError
}
}