Skip to content

Commit

Permalink
refactor: deep clone elements before assigning to structured errrors (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
char0n authored Oct 26, 2023
1 parent 15ab4b7 commit 659befc
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 90 deletions.
10 changes: 7 additions & 3 deletions packages/apidom-ast/src/traversal/visitor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApiDOMError } from '@swagger-api/apidom-error';
import { ApiDOMStructuredError } from '@swagger-api/apidom-error';

/**
* SPDX-FileCopyrightText: Copyright (c) GraphQL Contributors
Expand Down Expand Up @@ -281,7 +281,9 @@ export const visit = (
let result;
if (!Array.isArray(node)) {
if (!nodePredicate(node)) {
throw new ApiDOMError(`Invalid AST Node: ${JSON.stringify(node)}`);
throw new ApiDOMStructuredError(`Invalid AST Node: ${String(node)}`, {
node,
});
}

// cycle detected; skipping over a sub-tree to avoid recursion
Expand Down Expand Up @@ -436,7 +438,9 @@ visit[Symbol.for('nodejs.util.promisify.custom')] = async (
let result;
if (!Array.isArray(node)) {
if (!nodePredicate(node)) {
throw new ApiDOMError(`Invalid AST Node: ${JSON.stringify(node)}`);
throw new ApiDOMStructuredError(`Invalid AST Node: ${String(node)}`, {
node,
});
}

// cycle detected; skipping over a sub-tree to avoid recursion
Expand Down
7 changes: 6 additions & 1 deletion packages/apidom-ast/src/yaml/errors/YamlTagError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { ApiDOMErrorOptions } from '@swagger-api/apidom-error';

import YamlSchemaError from './YamlSchemaError';
import Position from '../../Position';
import Node from '../../Node';

export interface YamlTagErrorOptions extends ApiDOMErrorOptions {
export interface YamlTagErrorOptions<T extends Node = Node> extends ApiDOMErrorOptions {
readonly specificTagName: string;
readonly explicitTagName: string;
readonly tagKind: string;
readonly tagPosition?: Position;
readonly nodeCanonicalContent?: string;
readonly node?: T;
}

class YamlTagError extends YamlSchemaError {
Expand All @@ -22,6 +24,8 @@ class YamlTagError extends YamlSchemaError {

public readonly nodeCanonicalContent?: string;

public readonly node?: unknown;

constructor(message?: string, structuredOptions?: YamlTagErrorOptions) {
super(message, structuredOptions);

Expand All @@ -31,6 +35,7 @@ class YamlTagError extends YamlSchemaError {
this.tagKind = structuredOptions.tagKind;
this.tagPosition = structuredOptions.tagPosition;
this.nodeCanonicalContent = structuredOptions.nodeCanonicalContent;
this.node = structuredOptions.node;
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/apidom-ast/src/yaml/schemas/failsafe/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const FailsafeSchema = stampit({
explicitTagName: node.tag.explicitName,
tagKind: node.tag.kind,
tagPosition: clone(node.tag.position),
node: node.clone(),
});
}

Expand All @@ -105,6 +106,7 @@ const FailsafeSchema = stampit({
tagKind: node.tag.kind,
tagPosition: clone(node.tag.position),
nodeCanonicalContent: canonicalNode.content,
node: node.clone(),
});
}

Expand Down
17 changes: 16 additions & 1 deletion packages/apidom-core/src/clone/errors/CloneError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { ApiDOMStructuredError } from '@swagger-api/apidom-error';
import type { ApiDOMErrorOptions } from '@swagger-api/apidom-error';

class CloneError extends ApiDOMStructuredError {}
interface CloneErrorOptions extends ApiDOMErrorOptions {
readonly value: unknown;
}

class CloneError extends ApiDOMStructuredError {
public readonly value: unknown;

constructor(message?: string, structuredOptions?: CloneErrorOptions) {
super(message, structuredOptions);

if (typeof structuredOptions !== 'undefined') {
this.value = structuredOptions.source;
}
}
}

export default CloneError;
2 changes: 1 addition & 1 deletion packages/apidom-core/src/deepmerge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export default function deepmerge(

deepmerge.all = (list: ObjectOrArrayElement[], options?: DeepMergeOptions) => {
if (!Array.isArray(list)) {
throw new TypeError('First argument should be an array.');
throw new TypeError('First argument of deepmerge should be an array.');
}
if (list.length === 0) {
return new ObjectElement();
Expand Down
11 changes: 3 additions & 8 deletions packages/apidom-json-path/src/errors/EvaluationJsonPathError.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Element, hasElementSourceMap, toValue } from '@swagger-api/apidom-core';
import { Element } from '@swagger-api/apidom-core';
import { ApiDOMErrorOptions } from '@swagger-api/apidom-error';

import JsonPathError from './JsonPathError';
Expand All @@ -11,19 +11,14 @@ export interface EvaluationJsonPathErrorOptions<T extends Element> extends ApiDO
class EvaluationJsonPathError<T extends Element> extends JsonPathError {
public readonly path!: string | string[];

public readonly element!: string;

public readonly elementSourceMap?: [[number, number, number], [number, number, number]];
public readonly element!: T;

constructor(message?: string, structuredOptions?: EvaluationJsonPathErrorOptions<T>) {
super(message, structuredOptions);

if (typeof structuredOptions !== 'undefined') {
this.path = structuredOptions.path;
this.element = structuredOptions.element.element;
if (hasElementSourceMap(structuredOptions.element)) {
this.elementSourceMap = toValue(structuredOptions.element.getMetaProperty('sourceMap'));
}
this.element = structuredOptions.element;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Element, hasElementSourceMap, toValue } from '@swagger-api/apidom-core';
import { Element } from '@swagger-api/apidom-core';
import { ApiDOMErrorOptions } from '@swagger-api/apidom-error';

import JsonPathError from './JsonPathError';
Expand All @@ -11,19 +11,14 @@ export interface MultiEvaluationJsonPathErrorOptions<T extends Element> extends
class MultiEvaluationJsonPathError<T extends Element> extends JsonPathError {
public readonly paths!: string[] | string[][];

public readonly element!: string;

public readonly elementSourceMap?: [[number, number, number], [number, number, number]];
public readonly element!: T;

constructor(message?: string, structuredOptions?: MultiEvaluationJsonPathErrorOptions<T>) {
super(message, structuredOptions);

if (typeof structuredOptions !== 'undefined') {
this.paths = structuredOptions.paths;
this.element = structuredOptions.element.element;
if (hasElementSourceMap(structuredOptions.element)) {
this.elementSourceMap = toValue(structuredOptions.element.getMetaProperty('sourceMap'));
}
this.element = structuredOptions.element;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/apidom-json-path/src/evaluate-multi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { JSONPath } from 'jsonpath-plus';
import { Element, toValue } from '@swagger-api/apidom-core';
import { Element, toValue, cloneDeep } from '@swagger-api/apidom-core';
import { evaluate as jsonPointerEvaluate } from '@swagger-api/apidom-json-pointer';

import MultiEvaluationJsonPathError from './errors/MultiEvaluationJsonPathError';
Expand Down Expand Up @@ -45,7 +45,7 @@ const evaluateMulti: EvaluateMulti = (paths, element) => {
`JSON Path evaluation failed while multi-evaluating "${String(paths)}".`,
{
paths,
element,
element: cloneDeep(element),
cause: error,
},
);
Expand Down
4 changes: 2 additions & 2 deletions packages/apidom-json-path/src/evaluate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { JSONPath } from 'jsonpath-plus';
import { Element, toValue } from '@swagger-api/apidom-core';
import { Element, toValue, cloneDeep } from '@swagger-api/apidom-core';
import { evaluate as jsonPointerEvaluate } from '@swagger-api/apidom-json-pointer';

import EvaluationJsonPathError from './errors/EvaluationJsonPathError';
Expand All @@ -25,7 +25,7 @@ const evaluate: Evaluate = (path, element) => {
} catch (error: unknown) {
throw new EvaluationJsonPathError(
`JSON Path evaluation failed while evaluating "${String(path)}".`,
{ path, element, cause: error },
{ path, element: cloneDeep(element), cause: error },
);
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Element, hasElementSourceMap, toValue } from '@swagger-api/apidom-core';
import { Element } from '@swagger-api/apidom-core';
import { ApiDOMErrorOptions } from '@swagger-api/apidom-error';

import RelativeJsonPointerError from './RelativeJsonPointerError';
Expand All @@ -21,17 +21,11 @@ class EvaluationRelativeJsonPointerError<
> extends RelativeJsonPointerError {
public readonly relativePointer!: string;

public readonly currentElement!: string;
public readonly currentElement!: T;

public readonly currentElementSourceMap?: [[number, number, number], [number, number, number]];
public readonly rootElement!: U;

public readonly rootElement!: string;

public readonly rootElementSourceMap?: [[number, number, number], [number, number, number]];

public readonly cursorElement?: string;

public readonly cursorElementSourceMap?: [[number, number, number], [number, number, number]];
public readonly cursorElement?: V;

constructor(
message?: string,
Expand All @@ -41,29 +35,9 @@ class EvaluationRelativeJsonPointerError<

if (typeof structuredOptions !== 'undefined') {
this.relativePointer = structuredOptions.relativePointer;

this.currentElement = structuredOptions.currentElement.element;
if (hasElementSourceMap(structuredOptions.currentElement)) {
this.currentElementSourceMap = toValue(
structuredOptions.currentElement.getMetaProperty('sourceMap'),
);
}

this.rootElement = structuredOptions.rootElement.element;
if (hasElementSourceMap(structuredOptions.rootElement)) {
this.rootElementSourceMap = toValue(
structuredOptions.rootElement.getMetaProperty('sourceMap'),
);
}

if (typeof structuredOptions.cursorElement !== 'undefined') {
this.cursorElement = structuredOptions.cursorElement.element;
if (hasElementSourceMap(structuredOptions.cursorElement)) {
this.cursorElementSourceMap = toValue(
structuredOptions.cursorElement.getMetaProperty('sourceMap'),
);
}
}
this.currentElement = structuredOptions.currentElement;
this.rootElement = structuredOptions.rootElement;
this.cursorElement = structuredOptions.cursorElement;
}
}
}
Expand Down
37 changes: 19 additions & 18 deletions packages/apidom-json-pointer-relative/src/evaluate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Element,
visit,
cloneDeep,
BREAK,
isElement,
isMemberElement,
Expand Down Expand Up @@ -43,9 +44,9 @@ const evaluate = <T extends Element, U extends Element>(
'Relative JSON Pointer evaluation failed. Current element not found inside the root element',
{
relativePointer,
currentElement,
rootElement,
cursorElement: cursor,
currentElement: cloneDeep(currentElement),
rootElement: cloneDeep(rootElement),
cursorElement: cloneDeep.safe(cursor),
},
);
}
Expand All @@ -70,9 +71,9 @@ const evaluate = <T extends Element, U extends Element>(
'Relative JSON Pointer evaluation failed while parsing the pointer.',
{
relativePointer,
currentElement,
rootElement,
cursorElement: cursor,
currentElement: cloneDeep(currentElement),
rootElement: cloneDeep(currentElement),
cursorElement: cloneDeep.safe(cursor),
cause: error,
},
);
Expand All @@ -98,9 +99,9 @@ const evaluate = <T extends Element, U extends Element>(
`Relative JSON Pointer evaluation failed on non-negative-integer prefix of "${relativeJsonPointer.nonNegativeIntegerPrefix}"`,
{
relativePointer,
currentElement,
rootElement,
cursorElement: cursor,
currentElement: cloneDeep(currentElement),
rootElement: cloneDeep(rootElement),
cursorElement: cloneDeep.safe(cursor),
},
);
}
Expand All @@ -117,9 +118,9 @@ const evaluate = <T extends Element, U extends Element>(
`Relative JSON Pointer evaluation failed failed on index-manipulation "${relativeJsonPointer.indexManipulation}"`,
{
relativePointer,
currentElement,
rootElement,
cursorElement: cursor,
currentElement: cloneDeep(currentElement),
rootElement: cloneDeep(rootElement),
cursorElement: cloneDeep.safe(cursor),
},
);
}
Expand All @@ -133,9 +134,9 @@ const evaluate = <T extends Element, U extends Element>(
`Relative JSON Pointer evaluation failed on index-manipulation "${relativeJsonPointer.indexManipulation}"`,
{
relativePointer,
currentElement,
rootElement,
cursorElement: cursor,
currentElement: cloneDeep(currentElement),
rootElement: cloneDeep(rootElement),
cursorElement: cloneDeep.safe(cursor),
},
);
}
Expand All @@ -152,9 +153,9 @@ const evaluate = <T extends Element, U extends Element>(
'Relative JSON Pointer evaluation failed. Current element cannot be the root element to apply "#"',
{
relativePointer,
currentElement,
rootElement,
cursorElement: cursor,
currentElement: cloneDeep(currentElement),
rootElement: cloneDeep(rootElement),
cursorElement: cloneDeep.safe(cursor),
},
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiDOMErrorOptions } from '@swagger-api/apidom-error';
import { Element, hasElementSourceMap, toValue } from '@swagger-api/apidom-core';
import { Element } from '@swagger-api/apidom-core';

import JsonPointerError from './JsonPointerError';

Expand All @@ -20,9 +20,7 @@ class EvaluationJsonPointerError<T extends Element> extends JsonPointerError {

public readonly failedTokenPosition?: number;

public readonly element!: string;

public readonly elementSourceMap?: [[number, number, number], [number, number, number]];
public readonly element!: T;

constructor(message?: string, structuredOptions?: EvaluationJsonPointerErrorOptions<T>) {
super(message, structuredOptions);
Expand All @@ -34,10 +32,7 @@ class EvaluationJsonPointerError<T extends Element> extends JsonPointerError {
}
this.failedToken = structuredOptions.failedToken;
this.failedTokenPosition = structuredOptions.failedTokenPosition;
this.element = structuredOptions.element.element;
if (hasElementSourceMap(structuredOptions.element)) {
this.elementSourceMap = toValue(structuredOptions.element.getMetaProperty('sourceMap'));
}
this.element = structuredOptions.element;
}
}
}
Expand Down
Loading

0 comments on commit 659befc

Please sign in to comment.