@hey-api/json-schema-ref-parser
Version:
Parse, Resolve, and Dereference JSON Schema $ref pointers
156 lines (130 loc) • 4.1 kB
text/typescript
import { Ono } from '@jsdevtools/ono';
import type { $RefParser } from '..';
import type $Ref from '../ref';
import type { JSONSchema } from '../types';
import { getHash, stripHash, toFileSystemPath } from './url';
export type JSONParserErrorType =
| 'EUNKNOWN'
| 'EPARSER'
| 'EUNMATCHEDPARSER'
| 'ETIMEOUT'
| 'ERESOLVER'
| 'EUNMATCHEDRESOLVER'
| 'EMISSINGPOINTER'
| 'EINVALIDPOINTER';
export class JSONParserError extends Error {
public readonly name: string;
public readonly message: string;
public source: string | undefined;
public path: Array<string | number> | null;
public readonly code: JSONParserErrorType;
public constructor(message: string, source?: string) {
super();
this.code = 'EUNKNOWN';
this.name = 'JSONParserError';
this.message = message;
this.source = source;
this.path = null;
Ono.extend(this);
}
get footprint() {
return `${this.path}+${this.source}+${this.code}+${this.message}`;
}
}
export class JSONParserErrorGroup<S extends object = JSONSchema> extends Error {
files: $RefParser;
constructor(parser: $RefParser) {
super();
this.files = parser;
this.name = 'JSONParserErrorGroup';
this.message = `${this.errors.length} error${
this.errors.length > 1 ? 's' : ''
} occurred while reading '${toFileSystemPath(parser.$refs._root$Ref!.path)}'`;
Ono.extend(this);
}
static getParserErrors<S extends object = JSONSchema>(parser: $RefParser) {
const errors = [];
for (const $ref of Object.values(parser.$refs._$refs) as $Ref<S>[]) {
if ($ref.errors) {
errors.push(...$ref.errors);
}
}
return errors;
}
get errors(): Array<
| JSONParserError
| InvalidPointerError
| ResolverError
| ParserError
| MissingPointerError
| UnmatchedParserError
| UnmatchedResolverError
> {
return JSONParserErrorGroup.getParserErrors<S>(this.files);
}
}
export class ParserError extends JSONParserError {
code = 'EPARSER' as JSONParserErrorType;
name = 'ParserError';
constructor(message: any, source: any) {
super(`Error parsing ${source}: ${message}`, source);
}
}
export class UnmatchedParserError extends JSONParserError {
code = 'EUNMATCHEDPARSER' as JSONParserErrorType;
name = 'UnmatchedParserError';
constructor(source: string) {
super(`Could not find parser for "${source}"`, source);
}
}
export class ResolverError extends JSONParserError {
code = 'ERESOLVER' as JSONParserErrorType;
name = 'ResolverError';
ioErrorCode?: string;
constructor(ex: Error | any, source?: string) {
super(ex.message || `Error reading file "${source}"`, source);
if ('code' in ex) {
this.ioErrorCode = String(ex.code);
}
}
}
export class UnmatchedResolverError extends JSONParserError {
code = 'EUNMATCHEDRESOLVER' as JSONParserErrorType;
name = 'UnmatchedResolverError';
constructor(source: any) {
super(`Could not find resolver for "${source}"`, source);
}
}
export class MissingPointerError extends JSONParserError {
code = 'EMISSINGPOINTER' as JSONParserErrorType;
name = 'MissingPointerError';
constructor(token: string, path: string) {
super(
`Missing $ref pointer "${getHash(path)}". Token "${token}" does not exist.`,
stripHash(path),
);
}
}
export class TimeoutError extends JSONParserError {
code = 'ETIMEOUT' as JSONParserErrorType;
name = 'TimeoutError';
constructor(timeout: number) {
super(`Dereferencing timeout reached: ${timeout}ms`);
}
}
export class InvalidPointerError extends JSONParserError {
code = 'EUNMATCHEDRESOLVER' as JSONParserErrorType;
name = 'InvalidPointerError';
constructor(pointer: string, path: string) {
super(`Invalid $ref pointer "${pointer}". Pointers must begin with "#/"`, stripHash(path));
}
}
export function isHandledError(err: any): err is JSONParserError {
return err instanceof JSONParserError || err instanceof JSONParserErrorGroup;
}
export function normalizeError(err: any) {
if (err.path === null) {
err.path = [];
}
return err;
}