src/validation/property-validator-schema.js
function typeSpecificConstraintSchemas() {
  return {
    any: { },
    string: {
      mustNotBeEmpty: dynamicConstraintSchema(joi.boolean()),
      mustBeTrimmed: dynamicConstraintSchema(joi.boolean()),
      regexPattern: dynamicConstraintSchema(regexSchema),
      minimumLength: dynamicConstraintSchema(integerSchema.min(0)),
      maximumLength: maximumSizeConstraintSchema('minimumLength'),
      minimumValue: dynamicConstraintSchema(joi.string()),
      minimumValueExclusive: dynamicConstraintSchema(joi.string()),
      maximumValue: dynamicConstraintSchema(joi.string()),
      maximumValueExclusive: dynamicConstraintSchema(joi.string()),
      mustEqualIgnoreCase: dynamicConstraintSchema(joi.string())
    },
    integer: {
      minimumValue: dynamicConstraintSchema(integerSchema),
      minimumValueExclusive: dynamicConstraintSchema(integerSchema),
      maximumValue: maximumValueInclusiveNumberConstraintSchema(integerSchema),
      maximumValueExclusive: maximumValueExclusiveNumberConstraintSchema(integerSchema)
    },
    float: {
      minimumValue: dynamicConstraintSchema(joi.number()),
      minimumValueExclusive: dynamicConstraintSchema(joi.number()),
      maximumValue: maximumValueInclusiveNumberConstraintSchema(joi.number()),
      maximumValueExclusive: maximumValueExclusiveNumberConstraintSchema(joi.number())
    },
    boolean: { },
    datetime: {
      minimumValue: dynamicConstraintSchema(datetimeSchema),
      minimumValueExclusive: dynamicConstraintSchema(datetimeSchema),
      maximumValue: dynamicConstraintSchema(datetimeSchema),
      maximumValueExclusive: dynamicConstraintSchema(datetimeSchema)
    },
    date: {
      minimumValue: dynamicConstraintSchema(dateOnlySchema),
      minimumValueExclusive: dynamicConstraintSchema(dateOnlySchema),
      maximumValue: dynamicConstraintSchema(dateOnlySchema),
      maximumValueExclusive: dynamicConstraintSchema(dateOnlySchema)
    },
    time: {
      minimumValue: dynamicConstraintSchema(timeOnlySchema),
      minimumValueExclusive: dynamicConstraintSchema(timeOnlySchema),
      maximumValue: dynamicConstraintSchema(timeOnlySchema),
      maximumValueExclusive: dynamicConstraintSchema(timeOnlySchema)
    },
    timezone: {
      minimumValue: dynamicConstraintSchema(timezoneSchema),
      minimumValueExclusive: dynamicConstraintSchema(timezoneSchema),
      maximumValue: dynamicConstraintSchema(timezoneSchema),
      maximumValueExclusive: dynamicConstraintSchema(timezoneSchema)
    },
    enum: {
      predefinedValues: dynamicConstraintSchema(joi.array().required().min(1).items([ integerSchema, joi.string() ]))
    },
    uuid: {
      minimumValue: dynamicConstraintSchema(uuidSchema),
      minimumValueExclusive: dynamicConstraintSchema(uuidSchema),
      maximumValue: dynamicConstraintSchema(uuidSchema),
      maximumValueExclusive: dynamicConstraintSchema(uuidSchema)
    },
    attachmentReference: {
      maximumSize: dynamicConstraintSchema(integerSchema.min(1).max(20971520)),
      supportedExtensions: dynamicConstraintSchema(joi.array().min(1).items(joi.string())),
      supportedContentTypes: dynamicConstraintSchema(joi.array().min(1).items(joi.string().min(1))),
      regexPattern: dynamicConstraintSchema(regexSchema)
    },
    array: {
      mustNotBeEmpty: dynamicConstraintSchema(joi.boolean()),
      minimumLength: dynamicConstraintSchema(integerSchema.min(0)),
      maximumLength: maximumSizeConstraintSchema('minimumLength'),
      arrayElementsValidator: dynamicConstraintSchema(joi.lazy(() => schema))
    },
    object: {
      allowUnknownProperties: dynamicConstraintSchema(joi.boolean()),
      propertyValidators: dynamicConstraintSchema(joi.object().min(1).pattern(/^.+$/, joi.lazy(() => schema)))
    },
    hashtable: {
      minimumSize: dynamicConstraintSchema(integerSchema.min(0)),
      maximumSize: maximumSizeConstraintSchema('minimumSize'),
      hashtableKeysValidator: dynamicConstraintSchema(joi.object().keys({
        mustNotBeEmpty: dynamicConstraintSchema(joi.boolean()),
        regexPattern: dynamicConstraintSchema(regexSchema)
      })),
      hashtableValuesValidator: dynamicConstraintSchema(joi.lazy(() => schema))
    },
    conditional: {
      validationCandidates: dynamicConstraintSchema(conditionalValidationCandidatesSchema()).required()
    }
  };
}
function typeSpecificConstraintSchemas() {
  return {
    any: { },
    string: {
      mustNotBeEmpty: dynamicConstraintSchema(joi.boolean()),
      mustBeTrimmed: dynamicConstraintSchema(joi.boolean()),
      regexPattern: dynamicConstraintSchema(regexSchema),
      minimumLength: dynamicConstraintSchema(integerSchema.min(0)),
      maximumLength: maximumSizeConstraintSchema('minimumLength'),
      minimumValue: dynamicConstraintSchema(joi.string()),
      minimumValueExclusive: dynamicConstraintSchema(joi.string()),
      maximumValue: dynamicConstraintSchema(joi.string()),
      maximumValueExclusive: dynamicConstraintSchema(joi.string()),
      mustEqualIgnoreCase: dynamicConstraintSchema(joi.string())
    },
    integer: {
      minimumValue: dynamicConstraintSchema(integerSchema),
      minimumValueExclusive: dynamicConstraintSchema(integerSchema),
      maximumValue: maximumValueInclusiveNumberConstraintSchema(integerSchema),
      maximumValueExclusive: maximumValueExclusiveNumberConstraintSchema(integerSchema)
    },
    float: {
      minimumValue: dynamicConstraintSchema(joi.number()),
      minimumValueExclusive: dynamicConstraintSchema(joi.number()),
      maximumValue: maximumValueInclusiveNumberConstraintSchema(joi.number()),
      maximumValueExclusive: maximumValueExclusiveNumberConstraintSchema(joi.number())
    },
    boolean: { },
    datetime: {
      minimumValue: dynamicConstraintSchema(datetimeSchema),
      minimumValueExclusive: dynamicConstraintSchema(datetimeSchema),
      maximumValue: dynamicConstraintSchema(datetimeSchema),
      maximumValueExclusive: dynamicConstraintSchema(datetimeSchema)
    },
    date: {
      minimumValue: dynamicConstraintSchema(dateOnlySchema),
      minimumValueExclusive: dynamicConstraintSchema(dateOnlySchema),
      maximumValue: dynamicConstraintSchema(dateOnlySchema),
      maximumValueExclusive: dynamicConstraintSchema(dateOnlySchema)
    },
    time: {
      minimumValue: dynamicConstraintSchema(timeOnlySchema),
      minimumValueExclusive: dynamicConstraintSchema(timeOnlySchema),
      maximumValue: dynamicConstraintSchema(timeOnlySchema),
      maximumValueExclusive: dynamicConstraintSchema(timeOnlySchema)
    },
    timezone: {
      minimumValue: dynamicConstraintSchema(timezoneSchema),
      minimumValueExclusive: dynamicConstraintSchema(timezoneSchema),
      maximumValue: dynamicConstraintSchema(timezoneSchema),
      maximumValueExclusive: dynamicConstraintSchema(timezoneSchema)
    },
    enum: {
      predefinedValues: dynamicConstraintSchema(joi.array().required().min(1).items([ integerSchema, joi.string() ]))
    },
    uuid: {
      minimumValue: dynamicConstraintSchema(uuidSchema),
      minimumValueExclusive: dynamicConstraintSchema(uuidSchema),
      maximumValue: dynamicConstraintSchema(uuidSchema),
      maximumValueExclusive: dynamicConstraintSchema(uuidSchema)
    },
    attachmentReference: {
      maximumSize: dynamicConstraintSchema(integerSchema.min(1).max(20971520)),
      supportedExtensions: dynamicConstraintSchema(joi.array().min(1).items(joi.string())),
      supportedContentTypes: dynamicConstraintSchema(joi.array().min(1).items(joi.string().min(1))),
      regexPattern: dynamicConstraintSchema(regexSchema)
    },
    array: {
      mustNotBeEmpty: dynamicConstraintSchema(joi.boolean()),
      minimumLength: dynamicConstraintSchema(integerSchema.min(0)),
      maximumLength: maximumSizeConstraintSchema('minimumLength'),
      arrayElementsValidator: dynamicConstraintSchema(joi.lazy(() => schema))
    },
    object: {
      allowUnknownProperties: dynamicConstraintSchema(joi.boolean()),
      propertyValidators: dynamicConstraintSchema(joi.object().min(1).pattern(/^.+$/, joi.lazy(() => schema)))
    },
    hashtable: {
      minimumSize: dynamicConstraintSchema(integerSchema.min(0)),
      maximumSize: maximumSizeConstraintSchema('minimumSize'),
      hashtableKeysValidator: dynamicConstraintSchema(joi.object().keys({
        mustNotBeEmpty: dynamicConstraintSchema(joi.boolean()),
        regexPattern: dynamicConstraintSchema(regexSchema)
      })),
      hashtableValuesValidator: dynamicConstraintSchema(joi.lazy(() => schema))
    },
    conditional: {
      validationCandidates: dynamicConstraintSchema(conditionalValidationCandidatesSchema()).required()
    }
  };
}
Defined as a function rather than a plain object because it contains lazy references that result in recursive references between the complex types (e.g. "array", "object", "hashtable") and the main "propertyValidators" schema