“use strict”;

Object.defineProperty(exports, “__esModule”, {

value: true

}); exports.validate = validate; exports.typeIs = typeIs; exports.validateType = validateType; exports.validateOptional = validateOptional; exports.validateOptionalType = validateOptionalType; exports.arrayOf = arrayOf; exports.arrayOfType = arrayOfType; exports.validateArrayOfType = validateArrayOfType; exports.assertEach = assertEach; exports.assertOneOf = assertOneOf; exports.assertNodeType = assertNodeType; exports.assertNodeOrValueType = assertNodeOrValueType; exports.assertValueType = assertValueType; exports.assertShape = assertShape; exports.chain = chain; exports.default = defineType; exports.DEPRECATED_KEYS = exports.BUILDER_KEYS = exports.NODE_FIELDS = exports.FLIPPED_ALIAS_KEYS = exports.ALIAS_KEYS = exports.VISITOR_KEYS = void 0;

var _is = _interopRequireDefault(require(“../validators/is”));

var _validate = require(“../validators/validate”);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const VISITOR_KEYS = {}; exports.VISITOR_KEYS = VISITOR_KEYS; const ALIAS_KEYS = {}; exports.ALIAS_KEYS = ALIAS_KEYS; const FLIPPED_ALIAS_KEYS = {}; exports.FLIPPED_ALIAS_KEYS = FLIPPED_ALIAS_KEYS; const NODE_FIELDS = {}; exports.NODE_FIELDS = NODE_FIELDS; const BUILDER_KEYS = {}; exports.BUILDER_KEYS = BUILDER_KEYS; const DEPRECATED_KEYS = {}; exports.DEPRECATED_KEYS = DEPRECATED_KEYS;

function getType(val) {

if (Array.isArray(val)) {
  return "array";
} else if (val === null) {
  return "null";
} else if (val === undefined) {
  return "undefined";
} else {
  return typeof val;
}

}

function validate(validate) {

return {
  validate
};

}

function typeIs(typeName) {

return typeof typeName === "string" ? assertNodeType(typeName) : assertNodeType(...typeName);

}

function validateType(typeName) {

return validate(typeIs(typeName));

}

function validateOptional(validate) {

return {
  validate,
  optional: true
};

}

function validateOptionalType(typeName) {

return {
  validate: typeIs(typeName),
  optional: true
};

}

function arrayOf(elementType) {

return chain(assertValueType("array"), assertEach(elementType));

}

function arrayOfType(typeName) {

return arrayOf(typeIs(typeName));

}

function validateArrayOfType(typeName) {

return validate(arrayOfType(typeName));

}

function assertEach(callback) {

function validator(node, key, val) {
  if (!Array.isArray(val)) return;

  for (let i = 0; i < val.length; i++) {
    callback(node, `${key}[${i}]`, val[i]);
  }
}

validator.each = callback;
return validator;

}

function assertOneOf(…values) {

function validate(node, key, val) {
  if (values.indexOf(val) < 0) {
    throw new TypeError(`Property ${key} expected value to be one of ${JSON.stringify(values)} but got ${JSON.stringify(val)}`);
  }
}

validate.oneOf = values;
return validate;

}

function assertNodeType(…types) {

function validate(node, key, val) {
  let valid = false;

  for (const type of types) {
    if ((0, _is.default)(type, val)) {
      valid = true;
      break;
    }
  }

  if (!valid) {
    throw new TypeError(`Property ${key} of ${node.type} expected node to be of a type ${JSON.stringify(types)} ` + `but instead got ${JSON.stringify(val && val.type)}`);
  }
}

validate.oneOfNodeTypes = types;
return validate;

}

function assertNodeOrValueType(…types) {

function validate(node, key, val) {
  let valid = false;

  for (const type of types) {
    if (getType(val) === type || (0, _is.default)(type, val)) {
      valid = true;
      break;
    }
  }

  if (!valid) {
    throw new TypeError(`Property ${key} of ${node.type} expected node to be of a type ${JSON.stringify(types)} ` + `but instead got ${JSON.stringify(val && val.type)}`);
  }
}

validate.oneOfNodeOrValueTypes = types;
return validate;

}

function assertValueType(type) {

function validate(node, key, val) {
  const valid = getType(val) === type;

  if (!valid) {
    throw new TypeError(`Property ${key} expected type of ${type} but got ${getType(val)}`);
  }
}

validate.type = type;
return validate;

}

function assertShape(shape) {

function validate(node, key, val) {
  const errors = [];

  for (const property of Object.keys(shape)) {
    try {
      (0, _validate.validateField)(node, property, val[property], shape[property]);
    } catch (error) {
      if (error instanceof TypeError) {
        errors.push(error.message);
        continue;
      }

      throw error;
    }
  }

  if (errors.length) {
    throw new TypeError(`Property ${key} of ${node.type} expected to have the following:\n${errors.join("\n")}`);
  }
}

validate.shapeOf = shape;
return validate;

}

function chain(…fns) {

function validate(...args) {
  for (const fn of fns) {
    fn(...args);
  }
}

validate.chainOf = fns;
return validate;

}

function defineType(type, opts = {}) {

const inherits = opts.inherits && store[opts.inherits] || {};
const fields = opts.fields || inherits.fields || {};
const visitor = opts.visitor || inherits.visitor || [];
const aliases = opts.aliases || inherits.aliases || [];
const builder = opts.builder || inherits.builder || opts.visitor || [];

if (opts.deprecatedAlias) {
  DEPRECATED_KEYS[opts.deprecatedAlias] = type;
}

for (const key of visitor.concat(builder)) {
  fields[key] = fields[key] || {};
}

for (const key of Object.keys(fields)) {
  const field = fields[key];

  if (builder.indexOf(key) === -1) {
    field.optional = true;
  }

  if (field.default === undefined) {
    field.default = null;
  } else if (!field.validate) {
    field.validate = assertValueType(getType(field.default));
  }
}

VISITOR_KEYS[type] = opts.visitor = visitor;
BUILDER_KEYS[type] = opts.builder = builder;
NODE_FIELDS[type] = opts.fields = fields;
ALIAS_KEYS[type] = opts.aliases = aliases;
aliases.forEach(alias => {
  FLIPPED_ALIAS_KEYS[alias] = FLIPPED_ALIAS_KEYS[alias] || [];
  FLIPPED_ALIAS_KEYS[alias].push(type);
});
store[type] = opts;

}

const store = {};