@jarenjs/validate 0.34.2 → 0.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ARCHITECTURE.md CHANGED
@@ -955,7 +955,7 @@ Map<string, ValidationObject> {
955
955
 
956
956
  When schemas have `$id` that changes the base URI:
957
957
 
958
- ```json
958
+ ```jsonc
959
959
  {
960
960
  "$id": "http://example.com/schema",
961
961
  "$defs": {
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @jarenjs/validate
2
2
 
3
- The JSON Schema validating compiler at the heart of [Jaren](https://github.com/jklarenbeek/jarenjs). It compiles JSON Schemas into optimized validation functions and fully supports `draft-06`, `draft-07`, `draft 2019-09` and `draft 2020-12` — passing 100% of the official [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) for draft-07, 2019-09 and 2020-12.
3
+ The JSON Schema validating compiler at the heart of [Jaren](https://github.com/jklarenbeek/jarenjs). It compiles JSON Schemas into optimized validation functions and fully supports `draft-06`, `draft-07`, `draft 2019-09` and `draft 2020-12` — passing the official [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) for draft-07 and 2019-09 in full, and all of 2020-12 except the `$dynamicRef` cases the repository's roadmap names.
4
4
 
5
5
  ## Usage
6
6
 
@@ -14,6 +14,22 @@ export declare function isOfSchemaType(schema: any, type: any): boolean;
14
14
  export declare function hasSchemaRef(schema: any): boolean;
15
15
  export declare function hasSchemaRecursiveRef(schema: any): boolean;
16
16
  export declare function hasSchemaDynamicRef(schema: any): boolean;
17
+ /**
18
+ * The keywords that assert something beside a `$ref`: the siblings draft
19
+ * 2019-09+ applies alongside the reference and draft-07 ignores.
20
+ * Membership-only (order-insensitive): the shared constraint groups plus
21
+ * the applicators and extras. One list, read by the schema compiler (does
22
+ * this `$ref` carry siblings to compile?) and by the reference resolver
23
+ * (may this hop of a `$ref` chain be flattened away?).
24
+ */
25
+ export declare const REF_SIBLING_KEYWORDS: readonly string[];
26
+ /**
27
+ * Whether a schema carrying a `$ref` also carries a keyword of
28
+ * {@link REF_SIBLING_KEYWORDS}.
29
+ * @param {object} schema
30
+ * @returns {boolean}
31
+ */
32
+ export declare function hasRefSiblings(schema: object): boolean;
17
33
  /**
18
34
  * Whether a sibling keyword of unevaluatedProperties already evaluates every
19
35
  * property of the instance. additionalProperties (boolean or schema) applies
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/validate",
3
3
  "private": false,
4
- "version": "0.34.2",
4
+ "version": "0.43.1",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./dist/types/index.d.ts",
@@ -55,7 +55,7 @@
55
55
  "prepack": "npm run build:types"
56
56
  },
57
57
  "dependencies": {
58
- "@jarenjs/core": "^0.34.2",
59
- "@jarenjs/json": "^0.34.2"
58
+ "@jarenjs/core": "^0.43.1",
59
+ "@jarenjs/json": "^0.43.1"
60
60
  }
61
61
  }
package/src/schema.js CHANGED
@@ -5,10 +5,6 @@ import {
5
5
  getStringType,
6
6
  isObjectClass,
7
7
  } from '@jarenjs/core';
8
- import {
9
- NUMERIC_CONSTRAINTS, STRING_CONSTRAINTS,
10
- ARRAY_CONSTRAINTS, OBJECT_CONSTRAINTS,
11
- } from '@jarenjs/core/schema';
12
8
 
13
9
  import {
14
10
  getUniqueArray,
@@ -29,6 +25,7 @@ import {
29
25
  hasSchemaRef,
30
26
  hasSchemaRecursiveRef,
31
27
  hasSchemaDynamicRef,
28
+ hasRefSiblings,
32
29
  } from './tools.js';
33
30
 
34
31
  import {
@@ -453,18 +450,10 @@ export function compileSchemaObject(schemaObj, jsonSchema) {
453
450
  let refWithSiblings = false;
454
451
  if (hasSchemaRef(jsonSchema) && !hasSchemaRecursiveRef(jsonSchema)) {
455
452
  // Check if there are any validation-related sibling keywords
456
- // In draft 2019-09+, if there are validation siblings, we process
457
- // them together. Membership-only (order-insensitive): the shared
458
- // constraint groups plus the applicators and extras.
459
- const validationKeywords = ['type', 'const', 'enum',
460
- ...NUMERIC_CONSTRAINTS, ...STRING_CONSTRAINTS,
461
- ...ARRAY_CONSTRAINTS, 'maxContains', 'minContains',
462
- ...OBJECT_CONSTRAINTS, 'required',
463
- 'dependentRequired', 'properties', 'patternProperties', 'additionalProperties', 'items',
464
- 'prefixItems', 'additionalItems', 'contains', 'allOf', 'anyOf', 'oneOf', 'not', 'if',
465
- 'then', 'else', 'propertyNames', 'contentEncoding', 'contentMediaType',
466
- 'unevaluatedProperties', 'unevaluatedItems', '$query'];
467
- const hasValidationSiblings = keys.some(k => validationKeywords.includes(k));
453
+ // (REF_SIBLING_KEYWORDS the one list the reference resolver reads
454
+ // too). In draft 2019-09+, if there are validation siblings, we
455
+ // process them together.
456
+ const hasValidationSiblings = hasRefSiblings(jsonSchema);
468
457
  // In draft 7 and earlier, $ref always overrides siblings regardless
469
458
  // In draft 2019-09+, $ref can have validation siblings
470
459
  if (!hasValidationSiblings || draftVersion < 2019) {
package/src/tools.js CHANGED
@@ -31,6 +31,11 @@ import {
31
31
  JSONPOINTER_NOTHING,
32
32
  } from '@jarenjs/json';
33
33
 
34
+ import {
35
+ NUMERIC_CONSTRAINTS, STRING_CONSTRAINTS,
36
+ ARRAY_CONSTRAINTS, OBJECT_CONSTRAINTS,
37
+ } from '@jarenjs/core/schema';
38
+
34
39
  //#region Object
35
40
  export function isBoolOrObjectClass(obj) {
36
41
  return isBooleanType(obj)
@@ -85,6 +90,37 @@ export function hasSchemaDynamicRef(schema) {
85
90
  && !isStringWhiteSpace(schema.$dynamicRef);
86
91
  }
87
92
 
93
+ /**
94
+ * The keywords that assert something beside a `$ref`: the siblings draft
95
+ * 2019-09+ applies alongside the reference and draft-07 ignores.
96
+ * Membership-only (order-insensitive): the shared constraint groups plus
97
+ * the applicators and extras. One list, read by the schema compiler (does
98
+ * this `$ref` carry siblings to compile?) and by the reference resolver
99
+ * (may this hop of a `$ref` chain be flattened away?).
100
+ */
101
+ export const REF_SIBLING_KEYWORDS = Object.freeze(['type', 'const', 'enum',
102
+ ...NUMERIC_CONSTRAINTS, ...STRING_CONSTRAINTS,
103
+ ...ARRAY_CONSTRAINTS, 'maxContains', 'minContains',
104
+ ...OBJECT_CONSTRAINTS, 'required',
105
+ 'dependentRequired', 'properties', 'patternProperties', 'additionalProperties', 'items',
106
+ 'prefixItems', 'additionalItems', 'contains', 'allOf', 'anyOf', 'oneOf', 'not', 'if',
107
+ 'then', 'else', 'propertyNames', 'contentEncoding', 'contentMediaType',
108
+ 'unevaluatedProperties', 'unevaluatedItems', '$query']);
109
+
110
+ /**
111
+ * Whether a schema carrying a `$ref` also carries a keyword of
112
+ * {@link REF_SIBLING_KEYWORDS}.
113
+ * @param {object} schema
114
+ * @returns {boolean}
115
+ */
116
+ export function hasRefSiblings(schema) {
117
+ const keys = Object.keys(schema);
118
+ for (let i = 0; i < keys.length; i++) {
119
+ if (REF_SIBLING_KEYWORDS.includes(keys[i])) return true;
120
+ }
121
+ return false;
122
+ }
123
+
88
124
  /**
89
125
  * Whether a sibling keyword of unevaluatedProperties already evaluates every
90
126
  * property of the instance. additionalProperties (boolean or schema) applies
package/src/traverse.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  isBoolOrObjectClass,
3
3
  hasSchemaRef,
4
+ hasRefSiblings,
4
5
  } from './tools.js';
5
6
 
6
7
  import {
@@ -416,10 +417,18 @@ export function resolveRefSchemaDeep(schemas, baseUri, refschema, opts = new Tra
416
417
  if (!isObjectClass(item))
417
418
  return { id: base, schema: item };
418
419
 
419
- // In draft 7 and earlier, $ref completely replaces the schema
420
- // and all sibling keywords must be ignored. We only keep the $ref
421
- // to resolve it, discarding all other keywords from this item.
420
+ // A pure `$ref` hop is flattened away: only the `$ref` is kept to
421
+ // resolve it, the other (annotation) keywords of this item are
422
+ // discarded. A target reached THROUGH the chain that asserts
423
+ // keywords beside its `$ref` is the end of what may be flattened:
424
+ // draft 2019-09+ applies those siblings alongside the reference, so
425
+ // collapsing the chain past them would drop assertions (an OpenAPI
426
+ // document schema is nothing but such hops). The item is kept as
427
+ // written and compiled as a schema object, where the resource's
428
+ // draft decides whether its siblings assert (draft-07 ignores them).
422
429
  if (hasSchemaRef(item)) {
430
+ if (item !== refschema && hasRefSiblings(item))
431
+ return { id: base, schema: item };
423
432
  const ref = item.$ref;
424
433
  const { id, schema } = resolveRefSchemaShallow(schemas, ref, base, opts);
425
434