validate.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.validate = validate;
  4. const deep_compare_strict_js_1 = require("./deep-compare-strict.js");
  5. const dereference_js_1 = require("./dereference.js");
  6. const format_js_1 = require("./format.js");
  7. const pointer_js_1 = require("./pointer.js");
  8. const ucs2_length_js_1 = require("./ucs2-length.js");
  9. function validate(instance, schema, draft = '2019-09', lookup = (0, dereference_js_1.dereference)(schema), shortCircuit = true, recursiveAnchor = null, instanceLocation = '#', schemaLocation = '#', evaluated = Object.create(null)) {
  10. if (schema === true) {
  11. return { valid: true, errors: [] };
  12. }
  13. if (schema === false) {
  14. return {
  15. valid: false,
  16. errors: [
  17. {
  18. instanceLocation,
  19. keyword: 'false',
  20. keywordLocation: instanceLocation,
  21. error: 'False boolean schema.'
  22. }
  23. ]
  24. };
  25. }
  26. const rawInstanceType = typeof instance;
  27. let instanceType;
  28. switch (rawInstanceType) {
  29. case 'boolean':
  30. case 'number':
  31. case 'string':
  32. instanceType = rawInstanceType;
  33. break;
  34. case 'object':
  35. if (instance === null) {
  36. instanceType = 'null';
  37. }
  38. else if (Array.isArray(instance)) {
  39. instanceType = 'array';
  40. }
  41. else {
  42. instanceType = 'object';
  43. }
  44. break;
  45. default:
  46. throw new Error(`Instances of "${rawInstanceType}" type are not supported.`);
  47. }
  48. const { $ref, $recursiveRef, $recursiveAnchor, type: $type, const: $const, enum: $enum, required: $required, not: $not, anyOf: $anyOf, allOf: $allOf, oneOf: $oneOf, if: $if, then: $then, else: $else, format: $format, properties: $properties, patternProperties: $patternProperties, additionalProperties: $additionalProperties, unevaluatedProperties: $unevaluatedProperties, minProperties: $minProperties, maxProperties: $maxProperties, propertyNames: $propertyNames, dependentRequired: $dependentRequired, dependentSchemas: $dependentSchemas, dependencies: $dependencies, prefixItems: $prefixItems, items: $items, additionalItems: $additionalItems, unevaluatedItems: $unevaluatedItems, contains: $contains, minContains: $minContains, maxContains: $maxContains, minItems: $minItems, maxItems: $maxItems, uniqueItems: $uniqueItems, minimum: $minimum, maximum: $maximum, exclusiveMinimum: $exclusiveMinimum, exclusiveMaximum: $exclusiveMaximum, multipleOf: $multipleOf, minLength: $minLength, maxLength: $maxLength, pattern: $pattern, __absolute_ref__, __absolute_recursive_ref__ } = schema;
  49. const errors = [];
  50. if ($recursiveAnchor === true && recursiveAnchor === null) {
  51. recursiveAnchor = schema;
  52. }
  53. if ($recursiveRef === '#') {
  54. const refSchema = recursiveAnchor === null
  55. ? lookup[__absolute_recursive_ref__]
  56. : recursiveAnchor;
  57. const keywordLocation = `${schemaLocation}/$recursiveRef`;
  58. const result = validate(instance, recursiveAnchor === null ? schema : recursiveAnchor, draft, lookup, shortCircuit, refSchema, instanceLocation, keywordLocation, evaluated);
  59. if (!result.valid) {
  60. errors.push({
  61. instanceLocation,
  62. keyword: '$recursiveRef',
  63. keywordLocation,
  64. error: 'A subschema had errors.'
  65. }, ...result.errors);
  66. }
  67. }
  68. if ($ref !== undefined) {
  69. const uri = __absolute_ref__ || $ref;
  70. const refSchema = lookup[uri];
  71. if (refSchema === undefined) {
  72. let message = `Unresolved $ref "${$ref}".`;
  73. if (__absolute_ref__ && __absolute_ref__ !== $ref) {
  74. message += ` Absolute URI "${__absolute_ref__}".`;
  75. }
  76. message += `\nKnown schemas:\n- ${Object.keys(lookup).join('\n- ')}`;
  77. throw new Error(message);
  78. }
  79. const keywordLocation = `${schemaLocation}/$ref`;
  80. const result = validate(instance, refSchema, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, keywordLocation, evaluated);
  81. if (!result.valid) {
  82. errors.push({
  83. instanceLocation,
  84. keyword: '$ref',
  85. keywordLocation,
  86. error: 'A subschema had errors.'
  87. }, ...result.errors);
  88. }
  89. if (draft === '4' || draft === '7') {
  90. return { valid: errors.length === 0, errors };
  91. }
  92. }
  93. if (Array.isArray($type)) {
  94. let length = $type.length;
  95. let valid = false;
  96. for (let i = 0; i < length; i++) {
  97. if (instanceType === $type[i] ||
  98. ($type[i] === 'integer' &&
  99. instanceType === 'number' &&
  100. instance % 1 === 0 &&
  101. instance === instance)) {
  102. valid = true;
  103. break;
  104. }
  105. }
  106. if (!valid) {
  107. errors.push({
  108. instanceLocation,
  109. keyword: 'type',
  110. keywordLocation: `${schemaLocation}/type`,
  111. error: `Instance type "${instanceType}" is invalid. Expected "${$type.join('", "')}".`
  112. });
  113. }
  114. }
  115. else if ($type === 'integer') {
  116. if (instanceType !== 'number' || instance % 1 || instance !== instance) {
  117. errors.push({
  118. instanceLocation,
  119. keyword: 'type',
  120. keywordLocation: `${schemaLocation}/type`,
  121. error: `Instance type "${instanceType}" is invalid. Expected "${$type}".`
  122. });
  123. }
  124. }
  125. else if ($type !== undefined && instanceType !== $type) {
  126. errors.push({
  127. instanceLocation,
  128. keyword: 'type',
  129. keywordLocation: `${schemaLocation}/type`,
  130. error: `Instance type "${instanceType}" is invalid. Expected "${$type}".`
  131. });
  132. }
  133. if ($const !== undefined) {
  134. if (instanceType === 'object' || instanceType === 'array') {
  135. if (!(0, deep_compare_strict_js_1.deepCompareStrict)(instance, $const)) {
  136. errors.push({
  137. instanceLocation,
  138. keyword: 'const',
  139. keywordLocation: `${schemaLocation}/const`,
  140. error: `Instance does not match ${JSON.stringify($const)}.`
  141. });
  142. }
  143. }
  144. else if (instance !== $const) {
  145. errors.push({
  146. instanceLocation,
  147. keyword: 'const',
  148. keywordLocation: `${schemaLocation}/const`,
  149. error: `Instance does not match ${JSON.stringify($const)}.`
  150. });
  151. }
  152. }
  153. if ($enum !== undefined) {
  154. if (instanceType === 'object' || instanceType === 'array') {
  155. if (!$enum.some(value => (0, deep_compare_strict_js_1.deepCompareStrict)(instance, value))) {
  156. errors.push({
  157. instanceLocation,
  158. keyword: 'enum',
  159. keywordLocation: `${schemaLocation}/enum`,
  160. error: `Instance does not match any of ${JSON.stringify($enum)}.`
  161. });
  162. }
  163. }
  164. else if (!$enum.some(value => instance === value)) {
  165. errors.push({
  166. instanceLocation,
  167. keyword: 'enum',
  168. keywordLocation: `${schemaLocation}/enum`,
  169. error: `Instance does not match any of ${JSON.stringify($enum)}.`
  170. });
  171. }
  172. }
  173. if ($not !== undefined) {
  174. const keywordLocation = `${schemaLocation}/not`;
  175. const result = validate(instance, $not, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, keywordLocation);
  176. if (result.valid) {
  177. errors.push({
  178. instanceLocation,
  179. keyword: 'not',
  180. keywordLocation,
  181. error: 'Instance matched "not" schema.'
  182. });
  183. }
  184. }
  185. let subEvaluateds = [];
  186. if ($anyOf !== undefined) {
  187. const keywordLocation = `${schemaLocation}/anyOf`;
  188. const errorsLength = errors.length;
  189. let anyValid = false;
  190. for (let i = 0; i < $anyOf.length; i++) {
  191. const subSchema = $anyOf[i];
  192. const subEvaluated = Object.create(evaluated);
  193. const result = validate(instance, subSchema, draft, lookup, shortCircuit, $recursiveAnchor === true ? recursiveAnchor : null, instanceLocation, `${keywordLocation}/${i}`, subEvaluated);
  194. errors.push(...result.errors);
  195. anyValid = anyValid || result.valid;
  196. if (result.valid) {
  197. subEvaluateds.push(subEvaluated);
  198. }
  199. }
  200. if (anyValid) {
  201. errors.length = errorsLength;
  202. }
  203. else {
  204. errors.splice(errorsLength, 0, {
  205. instanceLocation,
  206. keyword: 'anyOf',
  207. keywordLocation,
  208. error: 'Instance does not match any subschemas.'
  209. });
  210. }
  211. }
  212. if ($allOf !== undefined) {
  213. const keywordLocation = `${schemaLocation}/allOf`;
  214. const errorsLength = errors.length;
  215. let allValid = true;
  216. for (let i = 0; i < $allOf.length; i++) {
  217. const subSchema = $allOf[i];
  218. const subEvaluated = Object.create(evaluated);
  219. const result = validate(instance, subSchema, draft, lookup, shortCircuit, $recursiveAnchor === true ? recursiveAnchor : null, instanceLocation, `${keywordLocation}/${i}`, subEvaluated);
  220. errors.push(...result.errors);
  221. allValid = allValid && result.valid;
  222. if (result.valid) {
  223. subEvaluateds.push(subEvaluated);
  224. }
  225. }
  226. if (allValid) {
  227. errors.length = errorsLength;
  228. }
  229. else {
  230. errors.splice(errorsLength, 0, {
  231. instanceLocation,
  232. keyword: 'allOf',
  233. keywordLocation,
  234. error: `Instance does not match every subschema.`
  235. });
  236. }
  237. }
  238. if ($oneOf !== undefined) {
  239. const keywordLocation = `${schemaLocation}/oneOf`;
  240. const errorsLength = errors.length;
  241. const matches = $oneOf.filter((subSchema, i) => {
  242. const subEvaluated = Object.create(evaluated);
  243. const result = validate(instance, subSchema, draft, lookup, shortCircuit, $recursiveAnchor === true ? recursiveAnchor : null, instanceLocation, `${keywordLocation}/${i}`, subEvaluated);
  244. errors.push(...result.errors);
  245. if (result.valid) {
  246. subEvaluateds.push(subEvaluated);
  247. }
  248. return result.valid;
  249. }).length;
  250. if (matches === 1) {
  251. errors.length = errorsLength;
  252. }
  253. else {
  254. errors.splice(errorsLength, 0, {
  255. instanceLocation,
  256. keyword: 'oneOf',
  257. keywordLocation,
  258. error: `Instance does not match exactly one subschema (${matches} matches).`
  259. });
  260. }
  261. }
  262. if (instanceType === 'object' || instanceType === 'array') {
  263. Object.assign(evaluated, ...subEvaluateds);
  264. }
  265. if ($if !== undefined) {
  266. const keywordLocation = `${schemaLocation}/if`;
  267. const conditionResult = validate(instance, $if, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, keywordLocation, evaluated).valid;
  268. if (conditionResult) {
  269. if ($then !== undefined) {
  270. const thenResult = validate(instance, $then, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, `${schemaLocation}/then`, evaluated);
  271. if (!thenResult.valid) {
  272. errors.push({
  273. instanceLocation,
  274. keyword: 'if',
  275. keywordLocation,
  276. error: `Instance does not match "then" schema.`
  277. }, ...thenResult.errors);
  278. }
  279. }
  280. }
  281. else if ($else !== undefined) {
  282. const elseResult = validate(instance, $else, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, `${schemaLocation}/else`, evaluated);
  283. if (!elseResult.valid) {
  284. errors.push({
  285. instanceLocation,
  286. keyword: 'if',
  287. keywordLocation,
  288. error: `Instance does not match "else" schema.`
  289. }, ...elseResult.errors);
  290. }
  291. }
  292. }
  293. if (instanceType === 'object') {
  294. if ($required !== undefined) {
  295. for (const key of $required) {
  296. if (!(key in instance)) {
  297. errors.push({
  298. instanceLocation,
  299. keyword: 'required',
  300. keywordLocation: `${schemaLocation}/required`,
  301. error: `Instance does not have required property "${key}".`
  302. });
  303. }
  304. }
  305. }
  306. const keys = Object.keys(instance);
  307. if ($minProperties !== undefined && keys.length < $minProperties) {
  308. errors.push({
  309. instanceLocation,
  310. keyword: 'minProperties',
  311. keywordLocation: `${schemaLocation}/minProperties`,
  312. error: `Instance does not have at least ${$minProperties} properties.`
  313. });
  314. }
  315. if ($maxProperties !== undefined && keys.length > $maxProperties) {
  316. errors.push({
  317. instanceLocation,
  318. keyword: 'maxProperties',
  319. keywordLocation: `${schemaLocation}/maxProperties`,
  320. error: `Instance does not have at least ${$maxProperties} properties.`
  321. });
  322. }
  323. if ($propertyNames !== undefined) {
  324. const keywordLocation = `${schemaLocation}/propertyNames`;
  325. for (const key in instance) {
  326. const subInstancePointer = `${instanceLocation}/${(0, pointer_js_1.encodePointer)(key)}`;
  327. const result = validate(key, $propertyNames, draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, keywordLocation);
  328. if (!result.valid) {
  329. errors.push({
  330. instanceLocation,
  331. keyword: 'propertyNames',
  332. keywordLocation,
  333. error: `Property name "${key}" does not match schema.`
  334. }, ...result.errors);
  335. }
  336. }
  337. }
  338. if ($dependentRequired !== undefined) {
  339. const keywordLocation = `${schemaLocation}/dependantRequired`;
  340. for (const key in $dependentRequired) {
  341. if (key in instance) {
  342. const required = $dependentRequired[key];
  343. for (const dependantKey of required) {
  344. if (!(dependantKey in instance)) {
  345. errors.push({
  346. instanceLocation,
  347. keyword: 'dependentRequired',
  348. keywordLocation,
  349. error: `Instance has "${key}" but does not have "${dependantKey}".`
  350. });
  351. }
  352. }
  353. }
  354. }
  355. }
  356. if ($dependentSchemas !== undefined) {
  357. for (const key in $dependentSchemas) {
  358. const keywordLocation = `${schemaLocation}/dependentSchemas`;
  359. if (key in instance) {
  360. const result = validate(instance, $dependentSchemas[key], draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, `${keywordLocation}/${(0, pointer_js_1.encodePointer)(key)}`, evaluated);
  361. if (!result.valid) {
  362. errors.push({
  363. instanceLocation,
  364. keyword: 'dependentSchemas',
  365. keywordLocation,
  366. error: `Instance has "${key}" but does not match dependant schema.`
  367. }, ...result.errors);
  368. }
  369. }
  370. }
  371. }
  372. if ($dependencies !== undefined) {
  373. const keywordLocation = `${schemaLocation}/dependencies`;
  374. for (const key in $dependencies) {
  375. if (key in instance) {
  376. const propsOrSchema = $dependencies[key];
  377. if (Array.isArray(propsOrSchema)) {
  378. for (const dependantKey of propsOrSchema) {
  379. if (!(dependantKey in instance)) {
  380. errors.push({
  381. instanceLocation,
  382. keyword: 'dependencies',
  383. keywordLocation,
  384. error: `Instance has "${key}" but does not have "${dependantKey}".`
  385. });
  386. }
  387. }
  388. }
  389. else {
  390. const result = validate(instance, propsOrSchema, draft, lookup, shortCircuit, recursiveAnchor, instanceLocation, `${keywordLocation}/${(0, pointer_js_1.encodePointer)(key)}`);
  391. if (!result.valid) {
  392. errors.push({
  393. instanceLocation,
  394. keyword: 'dependencies',
  395. keywordLocation,
  396. error: `Instance has "${key}" but does not match dependant schema.`
  397. }, ...result.errors);
  398. }
  399. }
  400. }
  401. }
  402. }
  403. const thisEvaluated = Object.create(null);
  404. let stop = false;
  405. if ($properties !== undefined) {
  406. const keywordLocation = `${schemaLocation}/properties`;
  407. for (const key in $properties) {
  408. if (!(key in instance)) {
  409. continue;
  410. }
  411. const subInstancePointer = `${instanceLocation}/${(0, pointer_js_1.encodePointer)(key)}`;
  412. const result = validate(instance[key], $properties[key], draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, `${keywordLocation}/${(0, pointer_js_1.encodePointer)(key)}`);
  413. if (result.valid) {
  414. evaluated[key] = thisEvaluated[key] = true;
  415. }
  416. else {
  417. stop = shortCircuit;
  418. errors.push({
  419. instanceLocation,
  420. keyword: 'properties',
  421. keywordLocation,
  422. error: `Property "${key}" does not match schema.`
  423. }, ...result.errors);
  424. if (stop)
  425. break;
  426. }
  427. }
  428. }
  429. if (!stop && $patternProperties !== undefined) {
  430. const keywordLocation = `${schemaLocation}/patternProperties`;
  431. for (const pattern in $patternProperties) {
  432. const regex = new RegExp(pattern, 'u');
  433. const subSchema = $patternProperties[pattern];
  434. for (const key in instance) {
  435. if (!regex.test(key)) {
  436. continue;
  437. }
  438. const subInstancePointer = `${instanceLocation}/${(0, pointer_js_1.encodePointer)(key)}`;
  439. const result = validate(instance[key], subSchema, draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, `${keywordLocation}/${(0, pointer_js_1.encodePointer)(pattern)}`);
  440. if (result.valid) {
  441. evaluated[key] = thisEvaluated[key] = true;
  442. }
  443. else {
  444. stop = shortCircuit;
  445. errors.push({
  446. instanceLocation,
  447. keyword: 'patternProperties',
  448. keywordLocation,
  449. error: `Property "${key}" matches pattern "${pattern}" but does not match associated schema.`
  450. }, ...result.errors);
  451. }
  452. }
  453. }
  454. }
  455. if (!stop && $additionalProperties !== undefined) {
  456. const keywordLocation = `${schemaLocation}/additionalProperties`;
  457. for (const key in instance) {
  458. if (thisEvaluated[key]) {
  459. continue;
  460. }
  461. const subInstancePointer = `${instanceLocation}/${(0, pointer_js_1.encodePointer)(key)}`;
  462. const result = validate(instance[key], $additionalProperties, draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, keywordLocation);
  463. if (result.valid) {
  464. evaluated[key] = true;
  465. }
  466. else {
  467. stop = shortCircuit;
  468. errors.push({
  469. instanceLocation,
  470. keyword: 'additionalProperties',
  471. keywordLocation,
  472. error: `Property "${key}" does not match additional properties schema.`
  473. }, ...result.errors);
  474. }
  475. }
  476. }
  477. else if (!stop && $unevaluatedProperties !== undefined) {
  478. const keywordLocation = `${schemaLocation}/unevaluatedProperties`;
  479. for (const key in instance) {
  480. if (!evaluated[key]) {
  481. const subInstancePointer = `${instanceLocation}/${(0, pointer_js_1.encodePointer)(key)}`;
  482. const result = validate(instance[key], $unevaluatedProperties, draft, lookup, shortCircuit, recursiveAnchor, subInstancePointer, keywordLocation);
  483. if (result.valid) {
  484. evaluated[key] = true;
  485. }
  486. else {
  487. errors.push({
  488. instanceLocation,
  489. keyword: 'unevaluatedProperties',
  490. keywordLocation,
  491. error: `Property "${key}" does not match unevaluated properties schema.`
  492. }, ...result.errors);
  493. }
  494. }
  495. }
  496. }
  497. }
  498. else if (instanceType === 'array') {
  499. if ($maxItems !== undefined && instance.length > $maxItems) {
  500. errors.push({
  501. instanceLocation,
  502. keyword: 'maxItems',
  503. keywordLocation: `${schemaLocation}/maxItems`,
  504. error: `Array has too many items (${instance.length} > ${$maxItems}).`
  505. });
  506. }
  507. if ($minItems !== undefined && instance.length < $minItems) {
  508. errors.push({
  509. instanceLocation,
  510. keyword: 'minItems',
  511. keywordLocation: `${schemaLocation}/minItems`,
  512. error: `Array has too few items (${instance.length} < ${$minItems}).`
  513. });
  514. }
  515. const length = instance.length;
  516. let i = 0;
  517. let stop = false;
  518. if ($prefixItems !== undefined) {
  519. const keywordLocation = `${schemaLocation}/prefixItems`;
  520. const length2 = Math.min($prefixItems.length, length);
  521. for (; i < length2; i++) {
  522. const result = validate(instance[i], $prefixItems[i], draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, `${keywordLocation}/${i}`);
  523. evaluated[i] = true;
  524. if (!result.valid) {
  525. stop = shortCircuit;
  526. errors.push({
  527. instanceLocation,
  528. keyword: 'prefixItems',
  529. keywordLocation,
  530. error: `Items did not match schema.`
  531. }, ...result.errors);
  532. if (stop)
  533. break;
  534. }
  535. }
  536. }
  537. if ($items !== undefined) {
  538. const keywordLocation = `${schemaLocation}/items`;
  539. if (Array.isArray($items)) {
  540. const length2 = Math.min($items.length, length);
  541. for (; i < length2; i++) {
  542. const result = validate(instance[i], $items[i], draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, `${keywordLocation}/${i}`);
  543. evaluated[i] = true;
  544. if (!result.valid) {
  545. stop = shortCircuit;
  546. errors.push({
  547. instanceLocation,
  548. keyword: 'items',
  549. keywordLocation,
  550. error: `Items did not match schema.`
  551. }, ...result.errors);
  552. if (stop)
  553. break;
  554. }
  555. }
  556. }
  557. else {
  558. for (; i < length; i++) {
  559. const result = validate(instance[i], $items, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
  560. evaluated[i] = true;
  561. if (!result.valid) {
  562. stop = shortCircuit;
  563. errors.push({
  564. instanceLocation,
  565. keyword: 'items',
  566. keywordLocation,
  567. error: `Items did not match schema.`
  568. }, ...result.errors);
  569. if (stop)
  570. break;
  571. }
  572. }
  573. }
  574. if (!stop && $additionalItems !== undefined) {
  575. const keywordLocation = `${schemaLocation}/additionalItems`;
  576. for (; i < length; i++) {
  577. const result = validate(instance[i], $additionalItems, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
  578. evaluated[i] = true;
  579. if (!result.valid) {
  580. stop = shortCircuit;
  581. errors.push({
  582. instanceLocation,
  583. keyword: 'additionalItems',
  584. keywordLocation,
  585. error: `Items did not match additional items schema.`
  586. }, ...result.errors);
  587. }
  588. }
  589. }
  590. }
  591. if ($contains !== undefined) {
  592. if (length === 0 && $minContains === undefined) {
  593. errors.push({
  594. instanceLocation,
  595. keyword: 'contains',
  596. keywordLocation: `${schemaLocation}/contains`,
  597. error: `Array is empty. It must contain at least one item matching the schema.`
  598. });
  599. }
  600. else if ($minContains !== undefined && length < $minContains) {
  601. errors.push({
  602. instanceLocation,
  603. keyword: 'minContains',
  604. keywordLocation: `${schemaLocation}/minContains`,
  605. error: `Array has less items (${length}) than minContains (${$minContains}).`
  606. });
  607. }
  608. else {
  609. const keywordLocation = `${schemaLocation}/contains`;
  610. const errorsLength = errors.length;
  611. let contained = 0;
  612. for (let j = 0; j < length; j++) {
  613. const result = validate(instance[j], $contains, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${j}`, keywordLocation);
  614. if (result.valid) {
  615. evaluated[j] = true;
  616. contained++;
  617. }
  618. else {
  619. errors.push(...result.errors);
  620. }
  621. }
  622. if (contained >= ($minContains || 0)) {
  623. errors.length = errorsLength;
  624. }
  625. if ($minContains === undefined &&
  626. $maxContains === undefined &&
  627. contained === 0) {
  628. errors.splice(errorsLength, 0, {
  629. instanceLocation,
  630. keyword: 'contains',
  631. keywordLocation,
  632. error: `Array does not contain item matching schema.`
  633. });
  634. }
  635. else if ($minContains !== undefined && contained < $minContains) {
  636. errors.push({
  637. instanceLocation,
  638. keyword: 'minContains',
  639. keywordLocation: `${schemaLocation}/minContains`,
  640. error: `Array must contain at least ${$minContains} items matching schema. Only ${contained} items were found.`
  641. });
  642. }
  643. else if ($maxContains !== undefined && contained > $maxContains) {
  644. errors.push({
  645. instanceLocation,
  646. keyword: 'maxContains',
  647. keywordLocation: `${schemaLocation}/maxContains`,
  648. error: `Array may contain at most ${$maxContains} items matching schema. ${contained} items were found.`
  649. });
  650. }
  651. }
  652. }
  653. if (!stop && $unevaluatedItems !== undefined) {
  654. const keywordLocation = `${schemaLocation}/unevaluatedItems`;
  655. for (i; i < length; i++) {
  656. if (evaluated[i]) {
  657. continue;
  658. }
  659. const result = validate(instance[i], $unevaluatedItems, draft, lookup, shortCircuit, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);
  660. evaluated[i] = true;
  661. if (!result.valid) {
  662. errors.push({
  663. instanceLocation,
  664. keyword: 'unevaluatedItems',
  665. keywordLocation,
  666. error: `Items did not match unevaluated items schema.`
  667. }, ...result.errors);
  668. }
  669. }
  670. }
  671. if ($uniqueItems) {
  672. for (let j = 0; j < length; j++) {
  673. const a = instance[j];
  674. const ao = typeof a === 'object' && a !== null;
  675. for (let k = 0; k < length; k++) {
  676. if (j === k) {
  677. continue;
  678. }
  679. const b = instance[k];
  680. const bo = typeof b === 'object' && b !== null;
  681. if (a === b || (ao && bo && (0, deep_compare_strict_js_1.deepCompareStrict)(a, b))) {
  682. errors.push({
  683. instanceLocation,
  684. keyword: 'uniqueItems',
  685. keywordLocation: `${schemaLocation}/uniqueItems`,
  686. error: `Duplicate items at indexes ${j} and ${k}.`
  687. });
  688. j = Number.MAX_SAFE_INTEGER;
  689. k = Number.MAX_SAFE_INTEGER;
  690. }
  691. }
  692. }
  693. }
  694. }
  695. else if (instanceType === 'number') {
  696. if (draft === '4') {
  697. if ($minimum !== undefined &&
  698. (($exclusiveMinimum === true && instance <= $minimum) ||
  699. instance < $minimum)) {
  700. errors.push({
  701. instanceLocation,
  702. keyword: 'minimum',
  703. keywordLocation: `${schemaLocation}/minimum`,
  704. error: `${instance} is less than ${$exclusiveMinimum ? 'or equal to ' : ''} ${$minimum}.`
  705. });
  706. }
  707. if ($maximum !== undefined &&
  708. (($exclusiveMaximum === true && instance >= $maximum) ||
  709. instance > $maximum)) {
  710. errors.push({
  711. instanceLocation,
  712. keyword: 'maximum',
  713. keywordLocation: `${schemaLocation}/maximum`,
  714. error: `${instance} is greater than ${$exclusiveMaximum ? 'or equal to ' : ''} ${$maximum}.`
  715. });
  716. }
  717. }
  718. else {
  719. if ($minimum !== undefined && instance < $minimum) {
  720. errors.push({
  721. instanceLocation,
  722. keyword: 'minimum',
  723. keywordLocation: `${schemaLocation}/minimum`,
  724. error: `${instance} is less than ${$minimum}.`
  725. });
  726. }
  727. if ($maximum !== undefined && instance > $maximum) {
  728. errors.push({
  729. instanceLocation,
  730. keyword: 'maximum',
  731. keywordLocation: `${schemaLocation}/maximum`,
  732. error: `${instance} is greater than ${$maximum}.`
  733. });
  734. }
  735. if ($exclusiveMinimum !== undefined && instance <= $exclusiveMinimum) {
  736. errors.push({
  737. instanceLocation,
  738. keyword: 'exclusiveMinimum',
  739. keywordLocation: `${schemaLocation}/exclusiveMinimum`,
  740. error: `${instance} is less than ${$exclusiveMinimum}.`
  741. });
  742. }
  743. if ($exclusiveMaximum !== undefined && instance >= $exclusiveMaximum) {
  744. errors.push({
  745. instanceLocation,
  746. keyword: 'exclusiveMaximum',
  747. keywordLocation: `${schemaLocation}/exclusiveMaximum`,
  748. error: `${instance} is greater than or equal to ${$exclusiveMaximum}.`
  749. });
  750. }
  751. }
  752. if ($multipleOf !== undefined) {
  753. const remainder = instance % $multipleOf;
  754. if (Math.abs(0 - remainder) >= 1.1920929e-7 &&
  755. Math.abs($multipleOf - remainder) >= 1.1920929e-7) {
  756. errors.push({
  757. instanceLocation,
  758. keyword: 'multipleOf',
  759. keywordLocation: `${schemaLocation}/multipleOf`,
  760. error: `${instance} is not a multiple of ${$multipleOf}.`
  761. });
  762. }
  763. }
  764. }
  765. else if (instanceType === 'string') {
  766. const length = $minLength === undefined && $maxLength === undefined
  767. ? 0
  768. : (0, ucs2_length_js_1.ucs2length)(instance);
  769. if ($minLength !== undefined && length < $minLength) {
  770. errors.push({
  771. instanceLocation,
  772. keyword: 'minLength',
  773. keywordLocation: `${schemaLocation}/minLength`,
  774. error: `String is too short (${length} < ${$minLength}).`
  775. });
  776. }
  777. if ($maxLength !== undefined && length > $maxLength) {
  778. errors.push({
  779. instanceLocation,
  780. keyword: 'maxLength',
  781. keywordLocation: `${schemaLocation}/maxLength`,
  782. error: `String is too long (${length} > ${$maxLength}).`
  783. });
  784. }
  785. if ($pattern !== undefined && !new RegExp($pattern, 'u').test(instance)) {
  786. errors.push({
  787. instanceLocation,
  788. keyword: 'pattern',
  789. keywordLocation: `${schemaLocation}/pattern`,
  790. error: `String does not match pattern.`
  791. });
  792. }
  793. if ($format !== undefined &&
  794. format_js_1.format[$format] &&
  795. !format_js_1.format[$format](instance)) {
  796. errors.push({
  797. instanceLocation,
  798. keyword: 'format',
  799. keywordLocation: `${schemaLocation}/format`,
  800. error: `String does not match format "${$format}".`
  801. });
  802. }
  803. }
  804. return { valid: errors.length === 0, errors };
  805. }