Comprehensive validation utilities for ensuring JSON Patch operations and sequences are correctly formatted and can be safely applied to target documents.
Validates an array of JSON Patch operations, optionally against a target document.
/**
* Validates a sequence of operations. If document parameter is provided,
* the sequence is additionally validated against the object document.
* @param sequence Array of operations to validate
* @param document Optional document to validate operations against
* @param externalValidator Optional custom validator function
* @returns JsonPatchError if validation fails, undefined if valid
*/
function validate<T>(
sequence: ReadonlyArray<Operation>,
document?: T,
externalValidator?: Validator<T>
): JsonPatchError | undefined;Usage Examples:
import { validate } from "fast-json-patch";
// Basic structure validation
const patch = [
{ op: "add", path: "/name", value: "Alice" },
{ op: "remove", path: "/age" }
];
const error = validate(patch);
if (error) {
console.error("Invalid patch:", error.message);
} else {
console.log("Patch is valid");
}
// Validate against target document
const document = { name: "John", age: 30 };
const patchWithError = [
{ op: "remove", path: "/nonexistent" } // This path doesn't exist
];
const docError = validate(patchWithError, document);
if (docError) {
console.error("Cannot apply patch:", docError.name); // "OPERATION_PATH_UNRESOLVABLE"
}
// Using custom validator
const customValidator = (op, index, doc, path) => {
if (op.op === "add" && typeof op.value === "string" && op.value.length > 100) {
throw new Error("String values cannot exceed 100 characters");
}
};
const longStringPatch = [
{ op: "add", path: "/description", value: "a".repeat(150) }
];
const customError = validate(longStringPatch, document, customValidator);Validates a single JSON Patch operation, with detailed error reporting.
/**
* Validates a single operation. Called from validate. Throws JsonPatchError in case of an error.
* @param operation Operation object to validate
* @param index Index of operation in the sequence
* @param document Optional object where the operation is supposed to be applied
* @param existingPathFragment Optional existing path fragment for context
*/
function validator(
operation: Operation,
index: number,
document?: any,
existingPathFragment?: string
): void;Usage Examples:
import { validator } from "fast-json-patch";
try {
// Validate operation structure
validator({ op: "add", path: "/name", value: "Alice" }, 0);
console.log("Operation is valid");
// This will throw an error
validator({ op: "invalid", path: "/name" }, 0);
} catch (error) {
console.error("Validation failed:", error.message);
console.error("Error code:", error.name); // "OPERATION_OP_INVALID"
}
// Validate against document context
const doc = { users: ["Alice"] };
try {
validator(
{ op: "add", path: "/users/5", value: "Bob" },
0,
doc,
"/users"
);
} catch (error) {
console.error("Invalid array index:", error.name); // "OPERATION_VALUE_OUT_OF_BOUNDS"
}Custom validator function interface for extending validation logic:
interface Validator<T> {
/**
* Custom validation function
* @param operation The operation being validated
* @param index The operation's index in the sequence
* @param document The target document
* @param existingPathFragment The existing path fragment for context
*/
(
operation: Operation,
index: number,
document: T,
existingPathFragment: string
): void;
}Comprehensive error class for JSON Patch validation failures:
class JsonPatchError extends Error {
constructor(
message: string,
name: JsonPatchErrorName,
index?: number,
operation?: any,
tree?: any
);
/** Descriptive error name */
name: JsonPatchErrorName;
/** Index of the failing operation */
index?: number;
/** The operation that caused the error */
operation?: any;
/** The document being operated on */
tree?: any;
}All possible validation error types:
type JsonPatchErrorName =
| 'SEQUENCE_NOT_AN_ARRAY' // Patch sequence must be an array
| 'OPERATION_NOT_AN_OBJECT' // Operation must be an object
| 'OPERATION_OP_INVALID' // Invalid operation type
| 'OPERATION_PATH_INVALID' // Invalid path format
| 'OPERATION_FROM_REQUIRED' // Missing 'from' property for move/copy
| 'OPERATION_VALUE_REQUIRED' // Missing 'value' property for add/replace/test
| 'OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED' // Value cannot contain undefined
| 'OPERATION_PATH_CANNOT_ADD' // Cannot add at the specified path
| 'OPERATION_PATH_UNRESOLVABLE' // Path does not exist in document
| 'OPERATION_FROM_UNRESOLVABLE' // From path does not exist in document
| 'OPERATION_PATH_ILLEGAL_ARRAY_INDEX' // Invalid array index format
| 'OPERATION_VALUE_OUT_OF_BOUNDS' // Array index out of bounds
| 'TEST_OPERATION_FAILED'; // Test operation comparison failedAdd Operations:
value propertyundefinedRemove Operations:
Replace Operations:
value propertyundefinedMove Operations:
from property (string)path and from must be valid pathsfrom path must exist in the target documentCopy Operations:
from property (string)from path must exist in the target documentTest Operations:
value propertyTEST_OPERATION_FAILED if values don't matchWhen validating against a document:
__proto__ and constructor.prototypeExtend validation with custom logic:
import { validate, Validator } from "fast-json-patch";
const businessValidator: Validator<any> = (operation, index, document, path) => {
// Business logic validation
if (operation.op === "add" && operation.path.startsWith("/admin/")) {
throw new Error("Admin modifications not allowed");
}
// Type-specific validation
if (operation.op === "replace" && operation.path === "/email") {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(operation.value)) {
throw new Error("Invalid email format");
}
}
};
// Use with validation
const error = validate(patch, document, businessValidator);This validation system ensures patch operations are safe, correctly formatted, and can be successfully applied to target documents while providing detailed error information for debugging.