{"version":3,"file":"JsonSchema.js","sourceRoot":"","sources":["../src/JsonSchema.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport * as os from 'os';\nimport * as path from 'path';\n\nimport { JsonFile, type JsonObject } from './JsonFile';\nimport { FileSystem } from './FileSystem';\n\nimport type ValidatorType from 'z-schema';\nconst Validator: typeof import('z-schema') = require('z-schema/dist/ZSchema-browser-min');\n\ninterface ISchemaWithId {\n id: string | undefined;\n}\n\n/**\n * Callback function arguments for JsonSchema.validateObjectWithCallback();\n * @public\n */\nexport interface IJsonSchemaErrorInfo {\n /**\n * The z-schema error tree, formatted as an indented text string.\n */\n details: string;\n}\n\n/**\n * Options for JsonSchema.validateObject()\n * @public\n */\nexport interface IJsonSchemaValidateOptions {\n /**\n * A custom header that will be used to report schema errors.\n * @remarks\n * If omitted, the default header is \"JSON validation failed:\". The error message starts with\n * the header, followed by the full input filename, followed by the z-schema error tree.\n * If you wish to customize all aspects of the error message, use JsonFile.loadAndValidateWithCallback()\n * or JsonSchema.validateObjectWithCallback().\n */\n customErrorHeader?: string;\n}\n\n/**\n * Options for JsonSchema.fromFile()\n * @public\n */\nexport interface IJsonSchemaFromFileOptions {\n /**\n * Other schemas that this schema references, e.g. via the \"$ref\" directive.\n * @remarks\n * The tree of dependent schemas may reference the same schema more than once.\n * However, if the same schema \"id\" is used by two different JsonSchema instances,\n * an error will be reported. This means you cannot load the same filename twice\n * and use them both together, and you cannot have diamond dependencies on different\n * versions of the same schema. Although technically this would be possible to support,\n * it normally indicates an error or design problem.\n *\n * JsonSchema also does not allow circular references between schema dependencies.\n */\n dependentSchemas?: JsonSchema[];\n}\n\n/**\n * Represents a JSON schema that can be used to validate JSON data files loaded by the JsonFile class.\n * @remarks\n * The schema itself is normally loaded and compiled later, only if it is actually required to validate\n * an input. To avoid schema errors at runtime, it's recommended to create a unit test that calls\n * JsonSchema.ensureCompiled() for each of your schema objects.\n *\n * @public\n */\nexport class JsonSchema {\n private _dependentSchemas: JsonSchema[] = [];\n private _filename: string = '';\n private _validator: ValidatorType | undefined = undefined;\n private _schemaObject: JsonObject | undefined = undefined;\n\n private constructor() {}\n\n /**\n * Registers a JsonSchema that will be loaded from a file on disk.\n * @remarks\n * NOTE: An error occurs if the file does not exist; however, the file itself is not loaded or validated\n * until it the schema is actually used.\n */\n public static fromFile(filename: string, options?: IJsonSchemaFromFileOptions): JsonSchema {\n // This is a quick and inexpensive test to avoid the catch the most common errors early.\n // Full validation will happen later in JsonSchema.compile().\n if (!FileSystem.exists(filename)) {\n throw new Error('Schema file not found: ' + filename);\n }\n\n const schema: JsonSchema = new JsonSchema();\n schema._filename = filename;\n\n if (options) {\n schema._dependentSchemas = options.dependentSchemas || [];\n }\n\n return schema;\n }\n\n /**\n * Registers a JsonSchema that will be loaded from a file on disk.\n * @remarks\n * NOTE: An error occurs if the file does not exist; however, the file itself is not loaded or validated\n * until it the schema is actually used.\n */\n public static fromLoadedObject(schemaObject: JsonObject): JsonSchema {\n const schema: JsonSchema = new JsonSchema();\n schema._schemaObject = schemaObject;\n return schema;\n }\n\n private static _collectDependentSchemas(\n collectedSchemas: JsonSchema[],\n dependentSchemas: JsonSchema[],\n seenObjects: Set,\n seenIds: Set\n ): void {\n for (const dependentSchema of dependentSchemas) {\n // It's okay for the same schema to appear multiple times in the tree, but we only process it once\n if (seenObjects.has(dependentSchema)) {\n continue;\n }\n seenObjects.add(dependentSchema);\n\n const schemaId: string = dependentSchema._ensureLoaded();\n if (schemaId === '') {\n throw new Error(\n `This schema ${dependentSchema.shortName} cannot be referenced` +\n ' because is missing the \"id\" field'\n );\n }\n if (seenIds.has(schemaId)) {\n throw new Error(\n `This schema ${dependentSchema.shortName} has the same \"id\" as another schema in this set`\n );\n }\n\n seenIds.add(schemaId);\n\n collectedSchemas.push(dependentSchema);\n\n JsonSchema._collectDependentSchemas(\n collectedSchemas,\n dependentSchema._dependentSchemas,\n seenObjects,\n seenIds\n );\n }\n }\n\n /**\n * Used to nicely format the ZSchema error tree.\n */\n private static _formatErrorDetails(errorDetails: ValidatorType.SchemaErrorDetail[]): string {\n return JsonSchema._formatErrorDetailsHelper(errorDetails, '', '');\n }\n\n /**\n * Used by _formatErrorDetails.\n */\n private static _formatErrorDetailsHelper(\n errorDetails: ValidatorType.SchemaErrorDetail[],\n indent: string,\n buffer: string\n ): string {\n for (const errorDetail of errorDetails) {\n buffer += os.EOL + indent + `Error: ${errorDetail.path}`;\n\n if (errorDetail.description) {\n const MAX_LENGTH: number = 40;\n let truncatedDescription: string = errorDetail.description.trim();\n if (truncatedDescription.length > MAX_LENGTH) {\n truncatedDescription = truncatedDescription.substr(0, MAX_LENGTH - 3) + '...';\n }\n\n buffer += ` (${truncatedDescription})`;\n }\n\n buffer += os.EOL + indent + ` ${errorDetail.message}`;\n\n if (errorDetail.inner) {\n buffer = JsonSchema._formatErrorDetailsHelper(errorDetail.inner, indent + ' ', buffer);\n }\n }\n\n return buffer;\n }\n\n /**\n * Returns a short name for this schema, for use in error messages.\n * @remarks\n * If the schema was loaded from a file, then the base filename is used. Otherwise, the \"id\"\n * field is used if available.\n */\n public get shortName(): string {\n if (!this._filename) {\n if (this._schemaObject) {\n const schemaWithId: ISchemaWithId = this._schemaObject as ISchemaWithId;\n if (schemaWithId.id) {\n return schemaWithId.id;\n }\n }\n return '(anonymous schema)';\n } else {\n return path.basename(this._filename);\n }\n }\n\n /**\n * If not already done, this loads the schema from disk and compiles it.\n * @remarks\n * Any dependencies will be compiled as well.\n */\n public ensureCompiled(): void {\n this._ensureLoaded();\n\n if (!this._validator) {\n // Don't assign this to _validator until we're sure everything was successful\n const newValidator: ValidatorType = new Validator({\n breakOnFirstError: false,\n noTypeless: true,\n noExtraKeywords: true\n });\n\n const anythingSchema: JsonObject = {\n type: ['array', 'boolean', 'integer', 'number', 'object', 'string']\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (newValidator as any).setRemoteReference('http://json-schema.org/draft-04/schema', anythingSchema);\n\n const collectedSchemas: JsonSchema[] = [];\n const seenObjects: Set = new Set();\n const seenIds: Set = new Set();\n\n JsonSchema._collectDependentSchemas(collectedSchemas, this._dependentSchemas, seenObjects, seenIds);\n\n // Validate each schema in order. We specifically do not supply them all together, because we want\n // to make sure that circular references will fail to validate.\n for (const collectedSchema of collectedSchemas) {\n if (!newValidator.validateSchema(collectedSchema._schemaObject)) {\n throw new Error(\n `Failed to validate schema \"${collectedSchema.shortName}\":` +\n os.EOL +\n JsonSchema._formatErrorDetails(newValidator.getLastErrors())\n );\n }\n }\n\n this._validator = newValidator;\n }\n }\n\n /**\n * Validates the specified JSON object against this JSON schema. If the validation fails,\n * an exception will be thrown.\n * @param jsonObject - The JSON data to be validated\n * @param filenameForErrors - The filename that the JSON data was available, or an empty string\n * if not applicable\n * @param options - Other options that control the validation\n */\n public validateObject(\n jsonObject: JsonObject,\n filenameForErrors: string,\n options?: IJsonSchemaValidateOptions\n ): void {\n this.validateObjectWithCallback(jsonObject, (errorInfo: IJsonSchemaErrorInfo) => {\n const prefix: string =\n options && options.customErrorHeader ? options.customErrorHeader : 'JSON validation failed:';\n\n throw new Error(prefix + os.EOL + filenameForErrors + os.EOL + errorInfo.details);\n });\n }\n\n /**\n * Validates the specified JSON object against this JSON schema. If the validation fails,\n * a callback is called for each validation error.\n */\n public validateObjectWithCallback(\n jsonObject: JsonObject,\n errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void\n ): void {\n this.ensureCompiled();\n\n if (!this._validator!.validate(jsonObject, this._schemaObject)) {\n const errorDetails: string = JsonSchema._formatErrorDetails(this._validator!.getLastErrors());\n\n const args: IJsonSchemaErrorInfo = {\n details: errorDetails\n };\n errorCallback(args);\n }\n }\n\n private _ensureLoaded(): string {\n if (!this._schemaObject) {\n this._schemaObject = JsonFile.load(this._filename);\n }\n return (this._schemaObject as ISchemaWithId).id || '';\n }\n}\n"]}