Chore/base configurations
This commit is contained in:
33
source/parser/ajv/ajv-types.ts
Normal file
33
source/parser/ajv/ajv-types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* This code is inspired by and adapted from:
|
||||
* https://github.com/bitauth/libauth
|
||||
*/
|
||||
export type AjvError<Keyword = string, Params = { [paramName: string]: number | string }> = {
|
||||
keyword: Keyword;
|
||||
instancePath: string;
|
||||
schemaPath: string;
|
||||
params: Params;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type XOAjvError =
|
||||
| AjvError<'additionalProperties', { additionalProperty: string }>
|
||||
| AjvError<'required', { missingProperty: string }>
|
||||
| AjvError<'type', { type: string }>;
|
||||
|
||||
/**
|
||||
* Note: these types cover only XO use cases; other `ajv` error types are
|
||||
* possible using other settings.
|
||||
*/
|
||||
export type AjvValidator<T = unknown> = {
|
||||
(
|
||||
data: unknown,
|
||||
dataCxt?: {
|
||||
instancePath?: string;
|
||||
parentData: unknown;
|
||||
parentDataProperty: unknown;
|
||||
rootData?: unknown;
|
||||
},
|
||||
): data is T;
|
||||
errors?: XOAjvError[] | null;
|
||||
};
|
||||
60
source/parser/ajv/ajv-utils.ts
Normal file
60
source/parser/ajv/ajv-utils.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* This code is inspired from https://github.com/bitauth/libauth
|
||||
*/
|
||||
|
||||
import { lossyNormalize } from '@bitauth/libauth';
|
||||
import type { AjvValidator, XOAjvError } from './ajv-types.ts';
|
||||
import { FailedToParseTemplateError } from '../../errors.ts';
|
||||
|
||||
const errorsToDescription = (errors: XOAjvError[]): string => {
|
||||
// TODO: translate instancePath and make errors more descriptive.
|
||||
|
||||
const descriptions = errors.map((error) => `${error.instancePath}: ${error.message}`).join(',');
|
||||
|
||||
return descriptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an untrusted JSON string or object, verify that
|
||||
* the untrusted value is of the expected shape. Note, this method first
|
||||
* normalizes all characters in the input using `Normalization Form KC`
|
||||
* (Compatibility Decomposition, followed by Canonical Composition).
|
||||
*
|
||||
* @param {unknown} jsonOrObject - The JSON string or object to parse
|
||||
* @returns {T} The parsed object
|
||||
* @throws {FailedToParseTemplateError} If JSON parsing fails
|
||||
*/
|
||||
export const parseJson = <T>(jsonOrObject: unknown): T => {
|
||||
try {
|
||||
// If the JSON object is a string, use it directly.
|
||||
const stringified = typeof jsonOrObject === 'string' ? jsonOrObject : JSON.stringify(jsonOrObject);
|
||||
|
||||
// Normalize the JSON string using `Normalization Form KC` (Compatibility Decomposition, followed by Canonical Composition).
|
||||
const normalized = lossyNormalize(stringified);
|
||||
|
||||
// Parse the normalized JSON string into the expected type.
|
||||
const parsed = JSON.parse(normalized);
|
||||
|
||||
// Return the parsed object.
|
||||
return parsed;
|
||||
} catch (error) {
|
||||
// Error wrapping. If the JSON parsing fails, throw an error.
|
||||
throw new FailedToParseTemplateError(error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates a parsed object against an AJV schema.
|
||||
*
|
||||
* @param parsed - The parsed object to validate
|
||||
* @param validator - The AJV validator function
|
||||
* @returns The validated object if valid, or an error message string if invalid
|
||||
*/
|
||||
export const validateSchema = <T>(parsed: unknown, validator: AjvValidator<T>): T | string => {
|
||||
if (validator(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// If the object is invalid, return the error message.
|
||||
return errorsToDescription(validator.errors ?? []);
|
||||
};
|
||||
Reference in New Issue
Block a user