UNPKG

@expo/schemer

Version:

Centralized scheme validation library for Expo

266 lines (264 loc) • 9.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { ErrorCodes: null, SchemerError: null, ValidationError: null, default: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: Object.getOwnPropertyDescriptor(all, name).get }); } _export(exports, { get ErrorCodes () { return _Error.ErrorCodes; }, get SchemerError () { return _Error.SchemerError; }, get ValidationError () { return _Error.ValidationError; }, get default () { return Schemer; } }); function _schemautils() { const data = require("@expo/schema-utils"); _schemautils = function() { return data; }; return data; } function _fs() { const data = /*#__PURE__*/ _interop_require_default(require("fs")); _fs = function() { return data; }; return data; } function _path() { const data = /*#__PURE__*/ _interop_require_default(require("path")); _path = function() { return data; }; return data; } function _probeimagesize() { const data = /*#__PURE__*/ _interop_require_default(require("probe-image-size")); _probeimagesize = function() { return data; }; return data; } const _Error = require("./Error"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const ERROR_CODES = { additionalProperties: 'SCHEMA_ADDITIONAL_PROPERTY', required: 'SCHEMA_MISSING_REQUIRED_PROPERTY', pattern: 'SCHEMA_INVALID_PATTERN', not: 'SCHEMA_INVALID_NOT' }; const toFieldPath = (path)=>path[0] === '.' ? path.slice(1) : path; class Schemer { options; schema; rootDir; manualValidationErrors; schemaValidationErrors; constructor(schema, options = {}){ this.options = { ...options }; this.schema = schema; this.rootDir = this.options.rootDir || __dirname; this.manualValidationErrors = []; this.schemaValidationErrors = []; } _formatValidationError({ keyword, path, value, message }) { return new _Error.ValidationError({ errorCode: ERROR_CODES[keyword] ?? 'SCHEMA_VALIDATION_ERROR', fieldPath: toFieldPath(path), message, data: value, meta: undefined }); } getErrors() { return [ ...this.schemaValidationErrors, ...this.manualValidationErrors ]; } _throwOnErrors() { // Clean error state after each validation const errors = this.getErrors(); if (errors.length > 0) { this.manualValidationErrors = []; this.schemaValidationErrors = []; throw new _Error.SchemerError(errors); } } async validateAll(data) { this._validateSchema(data); await this._validateAssetsAsync(data); this._throwOnErrors(); } async validateAssetsAsync(data) { await this._validateAssetsAsync(data); this._throwOnErrors(); } async validateSchemaAsync(data) { this._validateSchema(data); this._throwOnErrors(); } _validateSchema(data) { try { (0, _schemautils().validate)(this.schema, data); } catch (error) { if (error instanceof _schemautils().ValidationError) { for (const validationError of error.errors){ this.schemaValidationErrors.push(this._formatValidationError(validationError)); } } else { throw error; } } } async _validateAssetsAsync(data) { const assets = []; (0, _schemautils().visit)(this.schema, data, (subSchema, value, path)=>{ const meta = subSchema.meta; if (path && meta?.asset) { assets.push({ fieldPath: toFieldPath(path), data: value, meta }); } }); await Promise.all(assets.map(this._validateAssetAsync.bind(this))); } async _validateImageAsync({ fieldPath, data, meta }) { if (meta && meta.asset && data) { const { dimensions, square, contentTypePattern } = meta; // filePath could be an URL const filePath = _path().default.resolve(this.rootDir, data); try { // This cases on whether filePath is a remote URL or located on the machine const isLocalFile = _fs().default.existsSync(filePath); const probeResult = isLocalFile ? await (0, _probeimagesize().default)(require('fs').createReadStream(filePath)) : await (0, _probeimagesize().default)(data); if (!probeResult) { return; } const { width, height, type, mime } = probeResult; const fileExtension = filePath.split('.').pop(); if (isLocalFile && mime !== `image/${fileExtension}`) { this.manualValidationErrors.push(new _Error.ValidationError({ errorCode: 'FILE_EXTENSION_MISMATCH', fieldPath, message: `the file extension should match the content, but the file extension is .${fileExtension} while the file content at '${data}' is of type ${type}`, data, meta })); } if (contentTypePattern && !mime.match(new RegExp(contentTypePattern))) { this.manualValidationErrors.push(new _Error.ValidationError({ errorCode: 'INVALID_CONTENT_TYPE', fieldPath, message: `field '${fieldPath}' should point to ${meta.contentTypeHuman} but the file at '${data}' has type ${type}`, data, meta })); } if (dimensions && (dimensions.height !== height || dimensions.width !== width)) { this.manualValidationErrors.push(new _Error.ValidationError({ errorCode: 'INVALID_DIMENSIONS', fieldPath, message: `'${fieldPath}' should have dimensions ${dimensions.width}x${dimensions.height}, but the file at '${data}' has dimensions ${width}x${height}`, data, meta })); } if (square && width !== height) { this.manualValidationErrors.push(new _Error.ValidationError({ errorCode: 'NOT_SQUARE', fieldPath, message: `image should be square, but the file at '${data}' has dimensions ${width}x${height}`, data, meta })); } } catch { this.manualValidationErrors.push(new _Error.ValidationError({ errorCode: 'INVALID_ASSET_URI', fieldPath, message: `cannot access file at '${data}'`, data, meta })); } } } async _validateDirectoryAsync({ fieldPath, data, meta }) { if (meta && meta.asset && data) { const filePath = _path().default.resolve(this.rootDir, data); try { if (!_fs().default.existsSync(filePath)) { this.manualValidationErrors.push(new _Error.ValidationError({ errorCode: 'INVALID_ASSET_URI', fieldPath, message: `directory does not exist at '${data}'`, data, meta })); return; } if (!_fs().default.lstatSync(filePath).isDirectory()) { this.manualValidationErrors.push(new _Error.ValidationError({ errorCode: 'INVALID_CONTENT_TYPE', fieldPath, message: `field '${fieldPath}' should point to ${meta.contentTypeHuman} but the path at '${data}' is not a directory`, data, meta })); } } catch { this.manualValidationErrors.push(new _Error.ValidationError({ errorCode: 'INVALID_ASSET_URI', fieldPath, message: `cannot access directory at '${data}'`, data, meta })); } } } async _validateAssetAsync({ fieldPath, data, meta }) { if (meta && meta.asset && data) { if (meta.contentTypePattern && meta.contentTypePattern.startsWith('^image')) { await this._validateImageAsync({ fieldPath, data, meta }); } else if (meta.contentTypePattern === 'directory') { await this._validateDirectoryAsync({ fieldPath, data, meta }); } } } } //# sourceMappingURL=index.js.map