From 3fb8ae0b2a07031962e551a32ee7abe00c7ce9ae Mon Sep 17 00:00:00 2001
From: Derek Thompson <dotslashderek@gmail.com>
Date: Tue, 17 May 2022 07:45:06 -0400
Subject: [PATCH 01/11] fix(mergeallof): make sure fragment is fully merged
 (#16)

Right now we merge on a fragment with multiple nested refs at most twice; we have some that require more merge
operations to be fully merged.

Co-authored-by: Derek Thompson <derekthompson@Dereks-MacBook-Pro.local>
---
 src/mergers/mergeAllOf.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/mergers/mergeAllOf.ts b/src/mergers/mergeAllOf.ts
index 627bb8a..9d759c5 100644
--- a/src/mergers/mergeAllOf.ts
+++ b/src/mergers/mergeAllOf.ts
@@ -59,9 +59,9 @@ export function mergeAllOf(fragment: SchemaFragment, path: string[], walkingOpti
     store.set(walkingOptions.resolveRef, new WeakMap());
   }
 
-  const merged = _mergeAllOf(fragment, path, walkingOptions.resolveRef);
-  if ('allOf' in merged) {
-    return _mergeAllOf(merged, path, walkingOptions.resolveRef);
+  let merged = _mergeAllOf(fragment, path, walkingOptions.resolveRef);
+  while ('allOf' in merged) {
+    merged = _mergeAllOf(merged, path, walkingOptions.resolveRef);
   }
 
   return merged;

From a14a678e68206bfc02d69ac0697310c5bd4b4a11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= <jakub@stoplight.io>
Date: Tue, 2 Aug 2022 22:11:04 +0200
Subject: [PATCH 02/11] fix(walker): handle $ref siblings (#18)

---
 src/__tests__/tree.spec.ts | 80 ++++++++++++++++++++++++++++++++++++++
 src/walker/walker.ts       |  9 ++++-
 2 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/src/__tests__/tree.spec.ts b/src/__tests__/tree.spec.ts
index 335b7a1..f001544 100644
--- a/src/__tests__/tree.spec.ts
+++ b/src/__tests__/tree.spec.ts
@@ -677,6 +677,86 @@ describe('SchemaTree', () => {
     it('given empty schema, should output empty tree', () => {
       expect(printTree({})).toEqual('');
     });
+
+    it('should override description', () => {
+      const schema = {
+        type: 'object',
+        properties: {
+          caves: {
+            type: 'array',
+            items: {
+              summary: 'Bear cave',
+              $ref: '#/$defs/Cave',
+              description: 'Apparently Tom likes bears',
+            },
+          },
+          greatestBear: {
+            $ref: '#/$defs/Bear',
+            description: 'The greatest bear!',
+          },
+          bestBear: {
+            $ref: '#/$defs/Bear',
+            summary: 'The best bear!',
+          },
+        },
+        $defs: {
+          Bear: {
+            type: 'string',
+            summary: "Tom's favorite bear",
+          },
+          Cave: {
+            type: 'string',
+            summary: 'A cave',
+            description: '_Everyone_ ~hates~ loves caves',
+          },
+        },
+      };
+
+      const tree = new SchemaTree(schema, {});
+      tree.populate();
+
+      expect(tree.root).toEqual(
+        expect.objectContaining({
+          children: [
+            expect.objectContaining({
+              primaryType: 'object',
+              types: ['object'],
+              children: [
+                expect.objectContaining({
+                  primaryType: 'array',
+                  subpath: ['properties', 'caves'],
+                  types: ['array'],
+                  children: [
+                    expect.objectContaining({
+                      primaryType: 'string',
+                      types: ['string'],
+                      subpath: ['items'],
+                      annotations: {
+                        description: 'Apparently Tom likes bears',
+                      },
+                    }),
+                  ],
+                }),
+                expect.objectContaining({
+                  primaryType: 'string',
+                  types: ['string'],
+                  subpath: ['properties', 'greatestBear'],
+                  annotations: {
+                    description: 'The greatest bear!',
+                  },
+                }),
+                expect.objectContaining({
+                  primaryType: 'string',
+                  types: ['string'],
+                  subpath: ['properties', 'bestBear'],
+                  annotations: {},
+                }),
+              ],
+            }),
+          ],
+        }),
+      );
+    });
   });
 
   describe('position', () => {
diff --git a/src/walker/walker.ts b/src/walker/walker.ts
index 172b803..28c5462 100644
--- a/src/walker/walker.ts
+++ b/src/walker/walker.ts
@@ -275,7 +275,14 @@ export class Walker extends EventEmitter<WalkerEmitter> {
         return [new ReferenceNode(fragment, '$ref is not a string'), fragment];
       } else if (walkingOptions.resolveRef !== null) {
         try {
-          fragment = walkingOptions.resolveRef(path, fragment.$ref);
+          let newFragment = walkingOptions.resolveRef(path, fragment.$ref);
+
+          if (typeof fragment.description === 'string') {
+            newFragment = { ...newFragment };
+            Object.assign(newFragment, { description: fragment.description });
+          }
+
+          fragment = newFragment;
         } catch (ex) {
           super.emit('error', createMagicError(ex));
           return [new ReferenceNode(fragment, ex?.message ?? 'Unknown resolving error'), fragment];

From 7d1bc9d0de8a6e345e450d18a917cce4e09ef3d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= <jakub@stoplight.io>
Date: Mon, 15 May 2023 23:34:50 +0200
Subject: [PATCH 03/11] perf: improve handling of  under compound keyword combo
 (#20)

---
 package.json                                  |   2 +-
 .../combiners/allOfs/nested-refs.json         |  60 ++++++++
 src/__tests__/__snapshots__/tree.spec.ts.snap | 138 ++++++++++++++++++
 src/mergers/mergeAllOf.ts                     |  38 +++--
 src/mergers/mergeOneOrAnyOf.ts                |  32 ++--
 src/walker/walker.ts                          |   8 +-
 yarn.lock                                     |   8 +-
 7 files changed, 252 insertions(+), 34 deletions(-)
 create mode 100644 src/__tests__/__fixtures__/combiners/allOfs/nested-refs.json

diff --git a/package.json b/package.json
index bf9a074..844c8a0 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
   "peerDependencies": {},
   "dependencies": {
     "@stoplight/json": "^3.12.0",
-    "@stoplight/json-schema-merge-allof": "^0.7.7",
+    "@stoplight/json-schema-merge-allof": "^0.7.8",
     "@stoplight/lifecycle": "^2.3.2",
     "@types/json-schema": "^7.0.7",
     "magic-error": "0.0.1"
diff --git a/src/__tests__/__fixtures__/combiners/allOfs/nested-refs.json b/src/__tests__/__fixtures__/combiners/allOfs/nested-refs.json
new file mode 100644
index 0000000..d44142c
--- /dev/null
+++ b/src/__tests__/__fixtures__/combiners/allOfs/nested-refs.json
@@ -0,0 +1,60 @@
+{
+  "required": ["limit", "order"],
+  "type": "object",
+  "properties": {
+    "dimensions": { "type": "array", "items": { "type": "string" } },
+    "measures": { "type": "array", "items": { "type": "string" } },
+    "limit": {
+      "maximum": 2000,
+      "minimum": 0,
+      "type": "integer",
+      "format": "int32"
+    },
+    "offset": {
+      "minimum": 0,
+      "type": "integer",
+      "format": "int32",
+      "maximum": 2147483647
+    },
+    "filters": {
+      "type": "array",
+      "items": { "oneOf": [{ "$ref": "#/$defs/Logical" }, { "$ref": "#/$defs/Plain" }] }
+    },
+    "timeDimensions": {
+      "maxItems": 1,
+      "minItems": 0,
+      "type": "array",
+      "items": { "$ref": "#/$defs/TimeDimension" }
+    },
+    "order": { "type": "object", "additionalProperties": { "type": "string", "enum": ["ASC", "DESC"] } },
+    "nextToken": { "type": "string" }
+  },
+  "$defs": {
+    "Logical": { "type": "object", "allOf": [{ "$ref": "#/$defs/Filter" }] },
+    "Filter": { "type": "object", "anyOf": [{ "$ref": "#/$defs/Logical" }, { "$ref": "#/$defs/Plain" }] },
+    "Plain": {
+      "required": ["member", "operator"],
+      "type": "object",
+      "allOf": [
+        { "$ref": "#/$defs/Filter" },
+        {
+          "type": "object",
+          "properties": {
+            "member": { "type": "string" },
+            "operator": { "pattern": "equals|notEquals|gt|gte|lt|lte|set|notSet|inDateRange", "type": "string" },
+            "values": { "type": "array", "items": { "type": "object" } }
+          }
+        }
+      ]
+    },
+    "TimeDimension": {
+      "required": ["dateRange", "dimension", "granularity"],
+      "type": "object",
+      "properties": {
+        "dimension": { "type": "string" },
+        "granularity": { "pattern": "second|minute|hour|day", "type": "string" },
+        "dateRange": { "type": "object", "example": ["2022-04-19T16:00:00.000Z", "2022-04-19T17:00:00.000Z"] }
+      }
+    }
+  }
+}
diff --git a/src/__tests__/__snapshots__/tree.spec.ts.snap b/src/__tests__/__snapshots__/tree.spec.ts.snap
index f414438..6402999 100644
--- a/src/__tests__/__snapshots__/tree.spec.ts.snap
+++ b/src/__tests__/__snapshots__/tree.spec.ts.snap
@@ -694,6 +694,144 @@ exports[`SchemaTree output should generate valid tree for combiners/allOfs/compl
 "
 `;
 
+exports[`SchemaTree output should generate valid tree for combiners/allOfs/nested-refs.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: object
+   ├─ primaryType: object
+   └─ children
+      ├─ 0
+      │  └─ #/properties/dimensions
+      │     ├─ types
+      │     │  └─ 0: array
+      │     ├─ primaryType: array
+      │     └─ children
+      │        └─ 0
+      │           └─ #/properties/dimensions/items
+      │              ├─ types
+      │              │  └─ 0: string
+      │              └─ primaryType: string
+      ├─ 1
+      │  └─ #/properties/measures
+      │     ├─ types
+      │     │  └─ 0: array
+      │     ├─ primaryType: array
+      │     └─ children
+      │        └─ 0
+      │           └─ #/properties/measures/items
+      │              ├─ types
+      │              │  └─ 0: string
+      │              └─ primaryType: string
+      ├─ 2
+      │  └─ #/properties/limit
+      │     ├─ types
+      │     │  └─ 0: integer
+      │     └─ primaryType: integer
+      ├─ 3
+      │  └─ #/properties/offset
+      │     ├─ types
+      │     │  └─ 0: integer
+      │     └─ primaryType: integer
+      ├─ 4
+      │  └─ #/properties/filters
+      │     ├─ types
+      │     │  └─ 0: array
+      │     ├─ primaryType: array
+      │     └─ children
+      │        └─ 0
+      │           └─ #/properties/filters/items
+      │              ├─ combiners
+      │              │  └─ 0: oneOf
+      │              └─ children
+      │                 ├─ 0
+      │                 │  └─ #/properties/filters/items/oneOf/0
+      │                 │     ├─ types
+      │                 │     │  └─ 0: object
+      │                 │     ├─ primaryType: object
+      │                 │     ├─ combiners
+      │                 │     │  └─ 0: anyOf
+      │                 │     └─ children
+      │                 │        ├─ 0
+      │                 │        │  └─ #/properties/filters/items/oneOf/0/anyOf/0
+      │                 │        │     └─ mirrors: #/properties/filters/items/oneOf/0
+      │                 │        └─ 1
+      │                 │           └─ #/properties/filters/items/oneOf/0/anyOf/1
+      │                 │              ├─ types
+      │                 │              │  └─ 0: object
+      │                 │              ├─ primaryType: object
+      │                 │              ├─ combiners
+      │                 │              │  └─ 0: anyOf
+      │                 │              └─ children
+      │                 │                 ├─ 0
+      │                 │                 │  └─ #/properties/filters/items/oneOf/0/anyOf/1/anyOf/0
+      │                 │                 │     └─ mirrors: #/properties/filters/items/oneOf/0
+      │                 │                 ├─ 1
+      │                 │                 │  └─ #/properties/filters/items/oneOf/0/anyOf/1/anyOf/1
+      │                 │                 │     └─ mirrors: #/properties/filters/items/oneOf/0/anyOf/1
+      │                 │                 ├─ 2
+      │                 │                 │  └─ #/properties/filters/items/oneOf/0/anyOf/1/properties/member
+      │                 │                 │     ├─ types
+      │                 │                 │     │  └─ 0: string
+      │                 │                 │     └─ primaryType: string
+      │                 │                 ├─ 3
+      │                 │                 │  └─ #/properties/filters/items/oneOf/0/anyOf/1/properties/operator
+      │                 │                 │     ├─ types
+      │                 │                 │     │  └─ 0: string
+      │                 │                 │     └─ primaryType: string
+      │                 │                 └─ 4
+      │                 │                    └─ #/properties/filters/items/oneOf/0/anyOf/1/properties/values
+      │                 │                       ├─ types
+      │                 │                       │  └─ 0: array
+      │                 │                       ├─ primaryType: array
+      │                 │                       └─ children
+      │                 │                          └─ 0
+      │                 │                             └─ #/properties/filters/items/oneOf/0/anyOf/1/properties/values/items
+      │                 │                                ├─ types
+      │                 │                                │  └─ 0: object
+      │                 │                                └─ primaryType: object
+      │                 └─ 1
+      │                    └─ #/properties/filters/items/oneOf/1
+      │                       └─ mirrors: #/properties/filters/items/oneOf/0/anyOf/1
+      ├─ 5
+      │  └─ #/properties/timeDimensions
+      │     ├─ types
+      │     │  └─ 0: array
+      │     ├─ primaryType: array
+      │     └─ children
+      │        └─ 0
+      │           └─ #/properties/timeDimensions/items
+      │              ├─ types
+      │              │  └─ 0: object
+      │              ├─ primaryType: object
+      │              └─ children
+      │                 ├─ 0
+      │                 │  └─ #/properties/timeDimensions/items/properties/dimension
+      │                 │     ├─ types
+      │                 │     │  └─ 0: string
+      │                 │     └─ primaryType: string
+      │                 ├─ 1
+      │                 │  └─ #/properties/timeDimensions/items/properties/granularity
+      │                 │     ├─ types
+      │                 │     │  └─ 0: string
+      │                 │     └─ primaryType: string
+      │                 └─ 2
+      │                    └─ #/properties/timeDimensions/items/properties/dateRange
+      │                       ├─ types
+      │                       │  └─ 0: object
+      │                       └─ primaryType: object
+      ├─ 6
+      │  └─ #/properties/order
+      │     ├─ types
+      │     │  └─ 0: object
+      │     └─ primaryType: object
+      └─ 7
+         └─ #/properties/nextToken
+            ├─ types
+            │  └─ 0: string
+            └─ primaryType: string
+"
+`;
+
 exports[`SchemaTree output should generate valid tree for combiners/allOfs/todo-full.json 1`] = `
 "└─ #
    ├─ types
diff --git a/src/mergers/mergeAllOf.ts b/src/mergers/mergeAllOf.ts
index 9d759c5..24d89dd 100644
--- a/src/mergers/mergeAllOf.ts
+++ b/src/mergers/mergeAllOf.ts
@@ -1,4 +1,4 @@
-import { pathToPointer, stringify } from '@stoplight/json';
+import { pathToPointer } from '@stoplight/json';
 
 import { ResolvingError } from '../errors';
 import type { SchemaFragment } from '../types';
@@ -8,8 +8,18 @@ const resolveAllOf = require('@stoplight/json-schema-merge-allof');
 
 const store = new WeakMap<WalkerRefResolver, WeakMap<SchemaFragment, string[]>>();
 
-function _mergeAllOf(fragment: SchemaFragment, path: string[], resolveRef: WalkerRefResolver | null): SchemaFragment {
-  return resolveAllOf(fragment, {
+function _mergeAllOf(
+  fragment: SchemaFragment,
+  path: string[],
+  resolveRef: WalkerRefResolver | null,
+  seen: WeakMap<SchemaFragment, SchemaFragment>,
+): SchemaFragment {
+  const cached = seen.get(fragment);
+  if (cached !== void 0) {
+    return cached;
+  }
+
+  const merged = resolveAllOf(fragment, {
     deep: false,
     resolvers: resolveAllOf.stoplightResolvers,
     ...(resolveRef !== null
@@ -30,8 +40,8 @@ function _mergeAllOf(fragment: SchemaFragment, path: string[], resolveRef: Walke
               schemaRefs = [$ref];
               allRefs.set(fragment, schemaRefs);
             } else if (schemaRefs.includes($ref)) {
-              const safelyResolved = JSON.parse(stringify(resolveRef(null, $ref)));
-              return 'allOf' in safelyResolved ? _mergeAllOf(safelyResolved, path, resolveRef) : safelyResolved;
+              const resolved = resolveRef(null, $ref);
+              return 'allOf' in resolved ? _mergeAllOf(resolved, path, resolveRef, seen) : resolved;
             } else {
               schemaRefs.push($ref);
             }
@@ -52,17 +62,25 @@ function _mergeAllOf(fragment: SchemaFragment, path: string[], resolveRef: Walke
         }
       : null),
   });
+
+  seen.set(fragment, merged);
+  return merged;
 }
 
-export function mergeAllOf(fragment: SchemaFragment, path: string[], walkingOptions: WalkingOptions) {
+export function mergeAllOf(
+  fragment: SchemaFragment,
+  path: string[],
+  walkingOptions: WalkingOptions,
+  seen: WeakMap<SchemaFragment, SchemaFragment>,
+) {
   if (walkingOptions.resolveRef !== null && !store.has(walkingOptions.resolveRef)) {
     store.set(walkingOptions.resolveRef, new WeakMap());
   }
 
-  let merged = _mergeAllOf(fragment, path, walkingOptions.resolveRef);
-  while ('allOf' in merged) {
-    merged = _mergeAllOf(merged, path, walkingOptions.resolveRef);
-  }
+  let merged = fragment;
+  do {
+    merged = _mergeAllOf(merged, path, walkingOptions.resolveRef, seen);
+  } while ('allOf' in merged);
 
   return merged;
 }
diff --git a/src/mergers/mergeOneOrAnyOf.ts b/src/mergers/mergeOneOrAnyOf.ts
index 3e1e320..6730737 100644
--- a/src/mergers/mergeOneOrAnyOf.ts
+++ b/src/mergers/mergeOneOrAnyOf.ts
@@ -7,6 +7,7 @@ export function mergeOneOrAnyOf(
   fragment: SchemaFragment,
   path: string[],
   walkingOptions: WalkingOptions,
+  mergedAllOfs: WeakMap<SchemaFragment, SchemaFragment>,
 ): SchemaFragment[] {
   const combiner = SchemaCombinerName.OneOf in fragment ? SchemaCombinerName.OneOf : SchemaCombinerName.AnyOf;
   const items = fragment[combiner];
@@ -15,7 +16,7 @@ export function mergeOneOrAnyOf(
 
   const merged: SchemaFragment[] = [];
 
-  if (Array.isArray(fragment.allOf) && Array.isArray(items)) {
+  if (Array.isArray(fragment.allOf)) {
     for (const item of items) {
       merged.push({
         allOf: [...fragment.allOf, item],
@@ -24,26 +25,23 @@ export function mergeOneOrAnyOf(
 
     return merged;
   } else {
-    for (const item of items) {
-      const prunedSchema = { ...fragment };
-      delete prunedSchema[combiner];
+    const prunedSchema = { ...fragment };
+    delete prunedSchema[combiner];
 
+    for (const item of items) {
       if (Object.keys(prunedSchema).length === 0) {
         merged.push(item);
       } else {
-        const resolvedItem =
-          typeof item.$ref === 'string' && walkingOptions.resolveRef !== null
-            ? walkingOptions.resolveRef(null, item.$ref)
-            : item;
-        const mergedSchema = {
-          allOf: [prunedSchema, resolvedItem],
-        };
-
-        try {
-          merged.push(mergeAllOf(mergedSchema, path, walkingOptions));
-        } catch {
-          merged.push(mergedSchema);
-        }
+        merged.push(
+          mergeAllOf(
+            {
+              allOf: [prunedSchema, item],
+            },
+            path,
+            walkingOptions,
+            mergedAllOfs,
+          ),
+        );
       }
     }
   }
diff --git a/src/walker/walker.ts b/src/walker/walker.ts
index 28c5462..521a1f0 100644
--- a/src/walker/walker.ts
+++ b/src/walker/walker.ts
@@ -28,7 +28,9 @@ export class Walker extends EventEmitter<WalkerEmitter> {
   protected fragment: SchemaFragment;
   protected schemaNode: RegularNode | RootNode;
 
+  private mergedAllOfs: WeakMap<SchemaFragment, SchemaFragment>;
   private processedFragments: WeakMap<ProcessedFragment, SchemaNode>;
+
   private readonly hooks: Partial<Dictionary<WalkerHookHandler, WalkerHookAction>>;
 
   constructor(protected readonly root: RootNode, protected readonly walkingOptions: WalkingOptions) {
@@ -39,6 +41,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
     this.fragment = root.fragment;
     this.schemaNode = root;
     this.processedFragments = new WeakMap<SchemaFragment, SchemaNode>();
+    this.mergedAllOfs = new WeakMap();
 
     this.hooks = {};
   }
@@ -49,6 +52,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
     this.fragment = this.root.fragment;
     this.schemaNode = this.root;
     this.processedFragments = new WeakMap<SchemaFragment, RegularNode | ReferenceNode>();
+    this.mergedAllOfs = new WeakMap();
   }
 
   public loadSnapshot(snapshot: WalkerSnapshot) {
@@ -299,7 +303,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
           initialFragment = fragment.allOf;
         }
 
-        fragment = mergeAllOf(fragment, path, walkingOptions);
+        fragment = mergeAllOf(fragment, path, walkingOptions, this.mergedAllOfs);
       } catch (ex) {
         initialFragment = fragment;
         super.emit('error', createMagicError(new MergingError(ex?.message ?? 'Unknown merging error')));
@@ -309,7 +313,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
 
     if (SchemaCombinerName.OneOf in fragment || SchemaCombinerName.AnyOf in fragment) {
       try {
-        const merged = mergeOneOrAnyOf(fragment, path, walkingOptions);
+        const merged = mergeOneOrAnyOf(fragment, path, walkingOptions, this.mergedAllOfs);
         if (merged.length === 1) {
           return [new RegularNode(merged[0], { originalFragment }), initialFragment];
         } else {
diff --git a/yarn.lock b/yarn.lock
index 6cc6e3c..0ec7f5f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -936,10 +936,10 @@
   dependencies:
     eslint-config-prettier "^7.1.0"
 
-"@stoplight/json-schema-merge-allof@^0.7.7":
-  version "0.7.7"
-  resolved "https://registry.yarnpkg.com/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.7.7.tgz#d79ca8729aa5d6420e40cc545b1854b518b9240f"
-  integrity sha512-3QrZ+2hhTvsPAjl8HD9rQI3MPHSCl/kCf0rEkKM4YjLumDsfX1TSrhESDIt4lSzpSdNyT2oXSJx0ADVGE2gTyA==
+"@stoplight/json-schema-merge-allof@^0.7.8":
+  version "0.7.8"
+  resolved "https://registry.yarnpkg.com/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.7.8.tgz#7efe5e0086dff433eb011f617e82f7295c3de061"
+  integrity sha512-JTDt6GYpCWQSb7+UW1P91IAp/pcLWis0mmEzWVFcLsrNgtUYK7JLtYYz0ZPSR4QVL0fJ0YQejM+MPq5iNDFO4g==
   dependencies:
     compute-lcm "^1.1.0"
     json-schema-compare "^0.2.2"

From c16711d1a040334bf1bedc0a9284db2f6398439c Mon Sep 17 00:00:00 2001
From: Kayla Chun <58235624+kaylachun@users.noreply.github.com>
Date: Wed, 2 Aug 2023 13:53:59 -0700
Subject: [PATCH 04/11] fix(gettypes.ts): add null type if nullable is true
 (#21)

---
 src/accessors/getTypes.ts | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/src/accessors/getTypes.ts b/src/accessors/getTypes.ts
index 16db198..17d13d0 100644
--- a/src/accessors/getTypes.ts
+++ b/src/accessors/getTypes.ts
@@ -1,20 +1,36 @@
-import type { SchemaNodeKind } from '../nodes/types';
+import { SchemaNodeKind } from '../nodes/types';
 import type { SchemaFragment } from '../types';
 import { isValidType } from './guards/isValidType';
 import { inferType } from './inferType';
 
 export function getTypes(fragment: SchemaFragment): SchemaNodeKind[] | null {
+  const types: SchemaNodeKind[] = [];
+  let isNullable = false;
+
+  if ('nullable' in fragment) {
+    if (fragment.nullable === true) {
+      isNullable = true;
+    }
+  }
   if ('type' in fragment) {
     if (Array.isArray(fragment.type)) {
-      return fragment.type.filter(isValidType);
+      types.push(...fragment.type.filter(isValidType));
     } else if (isValidType(fragment.type)) {
-      return [fragment.type];
+      types.push(fragment.type);
     }
+    if (isNullable && !types.includes(SchemaNodeKind.Null)) {
+      types.push(SchemaNodeKind.Null);
+    }
+    return types;
   }
 
   const inferredType = inferType(fragment);
   if (inferredType !== null) {
-    return [inferredType];
+    types.push(inferredType);
+    if (isNullable && !types.includes(SchemaNodeKind.Null)) {
+      types.push(SchemaNodeKind.Null);
+    }
+    return types;
   }
 
   return null;

From 12b56230eb096140e7870dcfadfa1ac7cbaecd4e Mon Sep 17 00:00:00 2001
From: paulatulis <47359669+paulatulis@users.noreply.github.com>
Date: Wed, 9 Aug 2023 09:26:17 -0500
Subject: [PATCH 05/11] fix (walker): show description for $ref in fragments of
 type array (#23)

---
 src/__tests__/tree.spec.ts | 95 ++++++++++++++++++++++++++++++++++++++
 src/walker/walker.ts       | 24 +++++++++-
 2 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/src/__tests__/tree.spec.ts b/src/__tests__/tree.spec.ts
index f001544..5f80a29 100644
--- a/src/__tests__/tree.spec.ts
+++ b/src/__tests__/tree.spec.ts
@@ -757,6 +757,101 @@ describe('SchemaTree', () => {
         }),
       );
     });
+
+    it('node of type array should adopt description of referenced node', () => {
+      const schema = {
+        definitions: {
+          Cave: {
+            type: 'string',
+            summary: 'A cave',
+            description: '_Everyone_ ~hates~ loves caves',
+          },
+        },
+        type: 'object',
+        properties: {
+          caves: {
+            type: 'array',
+            items: {
+              $ref: '#/definitions/Cave',
+            },
+          },
+        },
+      };
+
+      const tree = new SchemaTree(schema);
+      tree.populate();
+
+      expect(
+        // @ts-ignore
+        tree.root.children[0].children[0].annotations.description,
+      ).toEqual('_Everyone_ ~hates~ loves caves');
+    });
+
+    it('node of type array should keep its own description even when referenced node has a description', () => {
+      const schema = {
+        definitions: {
+          Cave: {
+            type: 'string',
+            summary: 'A cave',
+            description: '_Everyone_ ~hates~ loves caves',
+          },
+        },
+        type: 'object',
+        properties: {
+          caves: {
+            type: 'array',
+            description: 'I have my own description',
+            items: {
+              $ref: '#/definitions/Cave',
+            },
+          },
+        },
+      };
+
+      const tree = new SchemaTree(schema);
+      tree.populate();
+
+      expect(
+        // @ts-ignore
+        tree.root.children[0].children[0].annotations.description,
+      ).toEqual('I have my own description');
+    });
+
+    it('referenced node description should appear for all properties with that ref', () => {
+      const schema = {
+        definitions: {
+          Cave: {
+            type: 'string',
+            summary: 'A cave',
+            description: '_Everyone_ ~hates~ loves caves',
+          },
+        },
+        type: 'object',
+        properties: {
+          caves: {
+            type: 'array',
+            items: {
+              $ref: '#/definitions/Cave',
+            },
+          },
+          bear: {
+            $ref: '#/definitions/Cave',
+          },
+        },
+      };
+
+      const tree = new SchemaTree(schema);
+      tree.populate();
+
+      expect(
+        // @ts-ignore
+        tree.root.children[0].children[0].annotations.description,
+      ).toEqual('_Everyone_ ~hates~ loves caves');
+      expect(
+        // @ts-ignore
+        tree.root.children[0].children[1].annotations.description,
+      ).toEqual('_Everyone_ ~hates~ loves caves');
+    });
   });
 
   describe('position', () => {
diff --git a/src/walker/walker.ts b/src/walker/walker.ts
index 521a1f0..b8eedeb 100644
--- a/src/walker/walker.ts
+++ b/src/walker/walker.ts
@@ -295,7 +295,29 @@ export class Walker extends EventEmitter<WalkerEmitter> {
         return [new ReferenceNode(fragment, null), fragment];
       }
     }
-
+    //fragment with type 'array' and no description should adopt description of $ref if it exists
+    if (fragment.type === 'array' && fragment.description === void 0) {
+      if (fragment.items !== void 0 && isObjectLiteral(fragment.items)) {
+        for (const key of Object.keys(fragment.items)) {
+          if (key === '$ref') {
+            const refToResolve = fragment.items[key];
+            if (typeof refToResolve !== 'string') {
+              return [new ReferenceNode(fragment, '$ref is not a string'), fragment];
+            } else if (walkingOptions.resolveRef !== null) {
+              try {
+                let newFragment = walkingOptions.resolveRef(path, refToResolve);
+                if (newFragment.description !== void 0) {
+                  newFragment = { ...newFragment };
+                  Object.assign(fragment, { description: newFragment.description });
+                }
+              } catch (ex) {
+                super.emit('error', createMagicError(ex));
+              }
+            }
+          }
+        }
+      }
+    }
     let initialFragment: ProcessedFragment = fragment;
     if (walkingOptions.mergeAllOf && SchemaCombinerName.AllOf in fragment) {
       try {

From 5cf28016dae7da482d2c3bf2899f483920cd50c1 Mon Sep 17 00:00:00 2001
From: paulatulis <47359669+paulatulis@users.noreply.github.com>
Date: Wed, 9 Aug 2023 11:26:04 -0500
Subject: [PATCH 06/11] fix(walker): show description for $ref in fragments of
 type array (#24)

---
 src/walker/walker.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/walker/walker.ts b/src/walker/walker.ts
index b8eedeb..4e144aa 100644
--- a/src/walker/walker.ts
+++ b/src/walker/walker.ts
@@ -295,7 +295,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
         return [new ReferenceNode(fragment, null), fragment];
       }
     }
-    //fragment with type 'array' and no description should adopt description of $ref if it exists
+    //fragment with type 'array' and no description should adopt description of $ref if it exists.
     if (fragment.type === 'array' && fragment.description === void 0) {
       if (fragment.items !== void 0 && isObjectLiteral(fragment.items)) {
         for (const key of Object.keys(fragment.items)) {

From 628627cb4fde4c0ed4472a4dc5040b36eb58d2ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= <jakub@stoplight.io>
Date: Wed, 15 Nov 2023 19:52:38 +0100
Subject: [PATCH 07/11] feat(tree): account for resolveInlineRef behavior when
 $ref has siblings (#25)

* fix(tree): account for resolveInlineRef behavior when $ref has siblings

* feat(walker): adds a max depth option for refs

---------

Co-authored-by: Daniel A. White <daniel.white@stoplight.io>
---
 .../__fixtures__/recursive-schema.json        | 24 +++++++++++++++++++
 .../references/with-overrides.json            | 23 ++++++++++++++++++
 src/__tests__/__snapshots__/tree.spec.ts.snap | 23 ++++++++++++++++++
 src/__tests__/tree.spec.ts                    | 15 +++++++++++-
 src/tree/tree.ts                              |  9 +++++++
 src/tree/types.ts                             |  3 +++
 src/walker/types.ts                           |  3 +++
 src/walker/walker.ts                          | 21 ++++++++++++----
 8 files changed, 116 insertions(+), 5 deletions(-)
 create mode 100644 src/__tests__/__fixtures__/recursive-schema.json
 create mode 100644 src/__tests__/__fixtures__/references/with-overrides.json

diff --git a/src/__tests__/__fixtures__/recursive-schema.json b/src/__tests__/__fixtures__/recursive-schema.json
new file mode 100644
index 0000000..610dfc1
--- /dev/null
+++ b/src/__tests__/__fixtures__/recursive-schema.json
@@ -0,0 +1,24 @@
+{
+  "title": "Thing",
+  "allOf": [
+    {
+      "$ref": "#/definitions/User"
+    }
+  ],
+  "description": "baz",
+  "definitions": {
+    "User": {
+      "type": "object",
+      "description": "user",
+      "properties": {
+        "manager": {
+          "$ref": "#/definitions/Boss"
+        }
+      }
+    },
+    "Boss": {
+      "$ref": "#/definitions/User",
+      "description": "xyz"
+    }
+  }
+}
diff --git a/src/__tests__/__fixtures__/references/with-overrides.json b/src/__tests__/__fixtures__/references/with-overrides.json
new file mode 100644
index 0000000..85ed288
--- /dev/null
+++ b/src/__tests__/__fixtures__/references/with-overrides.json
@@ -0,0 +1,23 @@
+{
+  "oneOf": [
+    {
+      "$ref": "#/definitions/User"
+    }
+  ],
+  "description": "User Model",
+  "definitions": {
+    "User": {
+      "type": "object",
+      "description": "Plain User",
+      "properties": {
+        "manager": {
+          "$ref": "#/definitions/Admin"
+        }
+      }
+    },
+    "Admin": {
+      "$ref": "#/definitions/User",
+      "description": "Admin User"
+    }
+  }
+}
diff --git a/src/__tests__/__snapshots__/tree.spec.ts.snap b/src/__tests__/__snapshots__/tree.spec.ts.snap
index 6402999..2708563 100644
--- a/src/__tests__/__snapshots__/tree.spec.ts.snap
+++ b/src/__tests__/__snapshots__/tree.spec.ts.snap
@@ -1293,6 +1293,29 @@ exports[`SchemaTree output should generate valid tree for references/nullish.jso
 "
 `;
 
+exports[`SchemaTree output should generate valid tree for references/with-overrides.json 1`] = `
+"└─ #
+   ├─ combiners
+   │  └─ 0: oneOf
+   └─ children
+      └─ 0
+         └─ #/oneOf/0
+            ├─ types
+            │  └─ 0: object
+            ├─ primaryType: object
+            └─ children
+               └─ 0
+                  └─ #/oneOf/0/properties/manager
+                     ├─ types
+                     │  └─ 0: object
+                     ├─ primaryType: object
+                     └─ children
+                        └─ 0
+                           └─ #/oneOf/0/properties/manager/properties/manager
+                              └─ mirrors: #/oneOf/0/properties/manager
+"
+`;
+
 exports[`SchemaTree output should generate valid tree for tickets.schema.json 1`] = `
 "└─ #
    ├─ types
diff --git a/src/__tests__/tree.spec.ts b/src/__tests__/tree.spec.ts
index 5f80a29..4216bad 100644
--- a/src/__tests__/tree.spec.ts
+++ b/src/__tests__/tree.spec.ts
@@ -13,7 +13,7 @@ describe('SchemaTree', () => {
     it.each(
       fastGlob.sync('**/*.json', {
         cwd: path.join(__dirname, '__fixtures__'),
-        ignore: ['stress-schema.json'],
+        ignore: ['stress-schema.json', 'recursive-schema.json'],
       }),
     )('should generate valid tree for %s', async filename => {
       const schema = JSON.parse(await fs.promises.readFile(path.resolve(__dirname, '__fixtures__', filename), 'utf8'));
@@ -985,4 +985,17 @@ describe('SchemaTree', () => {
       });
     });
   });
+
+  describe('recursive walking', () => {
+    it('should load with a max depth', async () => {
+      const schema = JSON.parse(
+        await fs.promises.readFile(path.resolve(__dirname, '__fixtures__', 'recursive-schema.json'), 'utf8'),
+      );
+
+      const w = new SchemaTree(schema, {
+        maxRefDepth: 1000,
+      });
+      w.populate();
+    });
+  });
 });
diff --git a/src/tree/tree.ts b/src/tree/tree.ts
index 761e1ae..e81be64 100644
--- a/src/tree/tree.ts
+++ b/src/tree/tree.ts
@@ -11,18 +11,22 @@ import type { SchemaTreeOptions } from './types';
 export class SchemaTree {
   public walker: Walker;
   public root: RootNode;
+  private readonly resolvedRefs = new Map();
 
   constructor(public schema: SchemaFragment, protected readonly opts?: Partial<SchemaTreeOptions>) {
     this.root = new RootNode(schema);
+    this.resolvedRefs = new Map();
     this.walker = new Walker(this.root, {
       mergeAllOf: this.opts?.mergeAllOf !== false,
       resolveRef: opts?.refResolver === null ? null : this.resolveRef,
+      maxRefDepth: opts?.maxRefDepth,
     });
   }
 
   public destroy() {
     this.root.children.length = 0;
     this.walker.destroy();
+    this.resolvedRefs.clear();
   }
 
   public populate() {
@@ -34,6 +38,10 @@ export class SchemaTree {
   }
 
   protected resolveRef: WalkerRefResolver = (path, $ref) => {
+    if (this.resolvedRefs.has($ref)) {
+      return this.resolvedRefs.get($ref);
+    }
+
     const seenRefs: string[] = [];
     let cur$ref: unknown = $ref;
     let resolvedValue!: SchemaFragment;
@@ -48,6 +56,7 @@ export class SchemaTree {
       cur$ref = resolvedValue.$ref;
     }
 
+    this.resolvedRefs.set($ref, resolvedValue);
     return resolvedValue;
   };
 
diff --git a/src/tree/types.ts b/src/tree/types.ts
index 9d76dce..1034782 100644
--- a/src/tree/types.ts
+++ b/src/tree/types.ts
@@ -2,7 +2,10 @@ import type { SchemaFragment } from '../types';
 
 export type SchemaTreeOptions = {
   mergeAllOf: boolean;
+  /** Resolves references to the schemas. If providing a custom implementation, it must return the same object reference for the same reference string. */
   refResolver: SchemaTreeRefDereferenceFn | null;
+  /** Controls the level of recursion of refs. Prevents overly complex trees and running out of stack depth. */
+  maxRefDepth?: number | null;
 };
 
 export type SchemaTreeRefInfo = {
diff --git a/src/walker/types.ts b/src/walker/types.ts
index aa67f2f..34f7b74 100644
--- a/src/walker/types.ts
+++ b/src/walker/types.ts
@@ -6,7 +6,10 @@ export type WalkerRefResolver = (path: string[] | null, $ref: string) => SchemaF
 
 export type WalkingOptions = {
   mergeAllOf: boolean;
+  /** Resolves references to the schemas. If providing a custom implementation, it must return the same object reference for the same reference string. */
   resolveRef: WalkerRefResolver | null;
+  /** Controls the level of recursion of refs. Prevents overly complex trees and running out of stack depth. */
+  maxRefDepth?: number | null;
 };
 
 export type WalkerSnapshot = {
diff --git a/src/walker/walker.ts b/src/walker/walker.ts
index 4e144aa..af4e0c0 100644
--- a/src/walker/walker.ts
+++ b/src/walker/walker.ts
@@ -36,11 +36,22 @@ export class Walker extends EventEmitter<WalkerEmitter> {
   constructor(protected readonly root: RootNode, protected readonly walkingOptions: WalkingOptions) {
     super();
 
+    let maxRefDepth = walkingOptions.maxRefDepth ?? null;
+    if (typeof maxRefDepth === 'number') {
+      if (maxRefDepth < 1) {
+        maxRefDepth = null;
+      } else if (maxRefDepth > 1000) {
+        // experimented with 1500 and the recursion limit is still lower than that
+        maxRefDepth = 1000;
+      }
+    }
+    walkingOptions.maxRefDepth = maxRefDepth;
+
     this.path = [];
     this.depth = -1;
     this.fragment = root.fragment;
     this.schemaNode = root;
-    this.processedFragments = new WeakMap<SchemaFragment, SchemaNode>();
+    this.processedFragments = new WeakMap();
     this.mergedAllOfs = new WeakMap();
 
     this.hooks = {};
@@ -51,7 +62,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
     this.depth = -1;
     this.fragment = this.root.fragment;
     this.schemaNode = this.root;
-    this.processedFragments = new WeakMap<SchemaFragment, RegularNode | ReferenceNode>();
+    this.processedFragments = new WeakMap();
     this.mergedAllOfs = new WeakMap();
   }
 
@@ -265,7 +276,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
   }
 
   protected processFragment(): [SchemaNode, ProcessedFragment] {
-    const { walkingOptions, path, fragment: originalFragment } = this;
+    const { walkingOptions, path, fragment: originalFragment, depth } = this;
     let { fragment } = this;
 
     let retrieved = isNonNullable(fragment) ? this.retrieveFromFragment(fragment, originalFragment) : null;
@@ -275,7 +286,9 @@ export class Walker extends EventEmitter<WalkerEmitter> {
     }
 
     if ('$ref' in fragment) {
-      if (typeof fragment.$ref !== 'string') {
+      if (typeof walkingOptions.maxRefDepth === 'number' && walkingOptions.maxRefDepth < depth) {
+        return [new ReferenceNode(fragment, `max $ref depth limit reached`), fragment];
+      } else if (typeof fragment.$ref !== 'string') {
         return [new ReferenceNode(fragment, '$ref is not a string'), fragment];
       } else if (walkingOptions.resolveRef !== null) {
         try {

From 46d92ce6252cf645111dec702b09624ec1a65dc4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= <jakub@stoplight.io>
Date: Fri, 17 Nov 2023 17:30:23 +0100
Subject: [PATCH 08/11] fix(walker): support circular JSON $refs with overrides
 (#26)

---
 .../references/circular-with-overrides.json   | 38 +++++++++++++++++++
 src/__tests__/__snapshots__/tree.spec.ts.snap | 30 +++++++++++++++
 src/walker/walker.ts                          |  8 +++-
 3 files changed, 75 insertions(+), 1 deletion(-)
 create mode 100644 src/__tests__/__fixtures__/references/circular-with-overrides.json

diff --git a/src/__tests__/__fixtures__/references/circular-with-overrides.json b/src/__tests__/__fixtures__/references/circular-with-overrides.json
new file mode 100644
index 0000000..49ce245
--- /dev/null
+++ b/src/__tests__/__fixtures__/references/circular-with-overrides.json
@@ -0,0 +1,38 @@
+{
+  "type": "object",
+  "properties": {
+    "order": {
+      "$ref": "#/definitions/Order",
+      "description": "My Order"
+    }
+  },
+  "definitions": {
+    "Cart": {
+      "type": "object",
+      "properties": {
+        "order": {
+          "$ref": "#/definitions/Order",
+          "description": "My Order"
+        }
+      }
+    },
+    "Order": {
+      "type": "object",
+      "properties": {
+        "member": {
+          "$ref": "#/definitions/Member",
+          "description": "Member"
+        }
+      }
+    },
+    "Member": {
+      "type": "object",
+      "properties": {
+        "referredMember": {
+          "$ref": "#/definitions/Member",
+          "description": "Member"
+        }
+      }
+    }
+  }
+}
diff --git a/src/__tests__/__snapshots__/tree.spec.ts.snap b/src/__tests__/__snapshots__/tree.spec.ts.snap
index 2708563..15893f9 100644
--- a/src/__tests__/__snapshots__/tree.spec.ts.snap
+++ b/src/__tests__/__snapshots__/tree.spec.ts.snap
@@ -1279,6 +1279,36 @@ exports[`SchemaTree output should generate valid tree for references/base.json 1
 "
 `;
 
+exports[`SchemaTree output should generate valid tree for references/circular-with-overrides.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: object
+   ├─ primaryType: object
+   └─ children
+      └─ 0
+         └─ #/properties/order
+            ├─ types
+            │  └─ 0: object
+            ├─ primaryType: object
+            └─ children
+               └─ 0
+                  └─ #/properties/order/properties/member
+                     ├─ types
+                     │  └─ 0: object
+                     ├─ primaryType: object
+                     └─ children
+                        └─ 0
+                           └─ #/properties/order/properties/member/properties/referredMember
+                              ├─ types
+                              │  └─ 0: object
+                              ├─ primaryType: object
+                              └─ children
+                                 └─ 0
+                                    └─ #/properties/order/properties/member/properties/referredMember/properties/referredMember
+                                       └─ mirrors: #/properties/order/properties/member/properties/referredMember
+"
+`;
+
 exports[`SchemaTree output should generate valid tree for references/nullish.json 1`] = `
 "└─ #
    ├─ types
diff --git a/src/walker/walker.ts b/src/walker/walker.ts
index af4e0c0..7989d90 100644
--- a/src/walker/walker.ts
+++ b/src/walker/walker.ts
@@ -285,6 +285,8 @@ export class Walker extends EventEmitter<WalkerEmitter> {
       return retrieved;
     }
 
+    let initialFragment: ProcessedFragment = fragment;
+
     if ('$ref' in fragment) {
       if (typeof walkingOptions.maxRefDepth === 'number' && walkingOptions.maxRefDepth < depth) {
         return [new ReferenceNode(fragment, `max $ref depth limit reached`), fragment];
@@ -297,6 +299,11 @@ export class Walker extends EventEmitter<WalkerEmitter> {
           if (typeof fragment.description === 'string') {
             newFragment = { ...newFragment };
             Object.assign(newFragment, { description: fragment.description });
+          } else {
+            retrieved = this.retrieveFromFragment(newFragment, originalFragment);
+            if (retrieved) {
+              return retrieved;
+            }
           }
 
           fragment = newFragment;
@@ -331,7 +338,6 @@ export class Walker extends EventEmitter<WalkerEmitter> {
         }
       }
     }
-    let initialFragment: ProcessedFragment = fragment;
     if (walkingOptions.mergeAllOf && SchemaCombinerName.AllOf in fragment) {
       try {
         if (Array.isArray(fragment.allOf)) {

From 44abda77236b9d14c584426d7b00c4777c2cb5a8 Mon Sep 17 00:00:00 2001
From: Brenda Rearden <brendarearden@gmail.com>
Date: Thu, 14 Dec 2023 11:52:41 -0700
Subject: [PATCH 09/11] fix(json-schema-merge-allof): bump
 json-schema-merge-allOf version (#28)

BREAKING CHANGE: description and summary reference siblings will not get overwritten by description
and summary from the referenced object
---
 package.json               |  2 +-
 src/__tests__/tree.spec.ts | 55 ++++++++++++++++++++++++++++++++++++++
 yarn.lock                  |  8 +++---
 3 files changed, 60 insertions(+), 5 deletions(-)

diff --git a/package.json b/package.json
index 844c8a0..a4b078e 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
   "peerDependencies": {},
   "dependencies": {
     "@stoplight/json": "^3.12.0",
-    "@stoplight/json-schema-merge-allof": "^0.7.8",
+    "@stoplight/json-schema-merge-allof": "^0.8.0",
     "@stoplight/lifecycle": "^2.3.2",
     "@types/json-schema": "^7.0.7",
     "magic-error": "0.0.1"
diff --git a/src/__tests__/tree.spec.ts b/src/__tests__/tree.spec.ts
index 4216bad..721cf98 100644
--- a/src/__tests__/tree.spec.ts
+++ b/src/__tests__/tree.spec.ts
@@ -787,6 +787,61 @@ describe('SchemaTree', () => {
       ).toEqual('_Everyone_ ~hates~ loves caves');
     });
 
+    it('should not override description reference siblings', () => {
+      const schema = {
+        $schema: 'http://json-schema.org/draft-07/schema#',
+        type: 'object',
+        properties: {
+          AAAAA: {
+            allOf: [{ description: 'AAAAA', type: 'string' }, { examples: ['AAAAA'] }],
+          },
+          BBBBB: {
+            allOf: [
+              {
+                $ref: '#/properties/AAAAA/allOf/0',
+                description: 'BBBBB',
+              },
+              { examples: ['BBBBB'] },
+            ],
+          },
+        },
+      };
+
+      const tree = new SchemaTree(schema, {});
+      tree.populate();
+
+      expect(tree.root).toEqual(
+        expect.objectContaining({
+          children: [
+            expect.objectContaining({
+              primaryType: 'object',
+              types: ['object'],
+              children: [
+                expect.objectContaining({
+                  primaryType: 'string',
+                  subpath: ['properties', 'AAAAA'],
+                  types: ['string'],
+                  annotations: {
+                    description: 'AAAAA',
+                    examples: ['AAAAA'],
+                  },
+                }),
+                expect.objectContaining({
+                  primaryType: 'string',
+                  subpath: ['properties', 'BBBBB'],
+                  types: ['string'],
+                  annotations: {
+                    description: 'BBBBB',
+                    examples: ['BBBBB'],
+                  },
+                }),
+              ],
+            }),
+          ],
+        }),
+      );
+    });
+
     it('node of type array should keep its own description even when referenced node has a description', () => {
       const schema = {
         definitions: {
diff --git a/yarn.lock b/yarn.lock
index 0ec7f5f..0a95cbc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -936,10 +936,10 @@
   dependencies:
     eslint-config-prettier "^7.1.0"
 
-"@stoplight/json-schema-merge-allof@^0.7.8":
-  version "0.7.8"
-  resolved "https://registry.yarnpkg.com/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.7.8.tgz#7efe5e0086dff433eb011f617e82f7295c3de061"
-  integrity sha512-JTDt6GYpCWQSb7+UW1P91IAp/pcLWis0mmEzWVFcLsrNgtUYK7JLtYYz0ZPSR4QVL0fJ0YQejM+MPq5iNDFO4g==
+"@stoplight/json-schema-merge-allof@^0.8.0":
+  version "0.8.0"
+  resolved "https://registry.yarnpkg.com/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.8.0.tgz#62f8116f59d9df5a910d037b1965decd2efab472"
+  integrity sha512-g8e0s43v96Xbzvd8d6KKUuJTO16CS2oJglJrviUi8ASIUxzFvAJqTHWLtGmpTryisQopqg1evXGJfi0+164+Qw==
   dependencies:
     compute-lcm "^1.1.0"
     json-schema-compare "^0.2.2"

From 8300b1224d1a44215506481f0574235bc10011ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= <jakub@stoplight.io>
Date: Tue, 23 Jan 2024 11:22:23 +0100
Subject: [PATCH 10/11] feat: support booleanish schemas & represent
 additional{Items,Properties} (#31)

BREAKING CHANGE: additionalProperties/additionalItems keywords are now processed as schemas
BREAKING CHANGE: true/false schemas are now represented
---
 jest.config.js                                |   1 +
 .../__fixtures__/arrays/additional-empty.json |   4 +
 .../__fixtures__/arrays/additional-false.json |   4 +
 .../arrays/additional-schema.json             |  11 ++
 .../__fixtures__/arrays/additional-true.json  |   4 +
 .../arrays/with-multiple-arrayish-items.json  |   5 +-
 .../arrays/with-single-arrayish-items.json    |   5 +-
 .../objects/additional-empty.json             |   4 +
 .../objects/additional-false.json             |   4 +
 .../objects/additional-schema.json            |  11 ++
 .../__fixtures__/objects/additional-true.json |   4 +
 src/__tests__/__snapshots__/tree.spec.ts.snap | 121 +++++++++++++++++-
 src/__tests__/tree.spec.ts                    |  28 ++++
 src/__tests__/utils/printTree.ts              |  29 +++--
 src/accessors/getValidations.ts               |   4 +-
 src/guards/nodes.ts                           |   5 +
 src/nodes/BaseNode.ts                         |   3 +-
 src/nodes/BooleanishNode.ts                   |   7 +
 src/nodes/ReferenceNode.ts                    |   4 +-
 src/nodes/RegularNode.ts                      |   5 +-
 src/nodes/RootNode.ts                         |   2 +-
 src/nodes/index.ts                            |   1 +
 src/nodes/mirrored/MirroredReferenceNode.ts   |   6 +-
 src/nodes/mirrored/MirroredRegularNode.ts     |  24 +++-
 src/nodes/types.ts                            |   3 +-
 src/utils/guards.ts                           |   6 +
 src/walker/types.ts                           |   4 +-
 src/walker/walker.ts                          |  67 +++++++---
 28 files changed, 317 insertions(+), 59 deletions(-)
 create mode 100644 src/__tests__/__fixtures__/arrays/additional-empty.json
 create mode 100644 src/__tests__/__fixtures__/arrays/additional-false.json
 create mode 100644 src/__tests__/__fixtures__/arrays/additional-schema.json
 create mode 100644 src/__tests__/__fixtures__/arrays/additional-true.json
 create mode 100644 src/__tests__/__fixtures__/objects/additional-empty.json
 create mode 100644 src/__tests__/__fixtures__/objects/additional-false.json
 create mode 100644 src/__tests__/__fixtures__/objects/additional-schema.json
 create mode 100644 src/__tests__/__fixtures__/objects/additional-true.json
 create mode 100644 src/nodes/BooleanishNode.ts

diff --git a/jest.config.js b/jest.config.js
index 58f0a45..5346809 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,6 +1,7 @@
 module.exports = {
   rootDir: process.cwd(),
   testEnvironment: 'node',
+  roots: ['<rootDir>/src'],
   setupFilesAfterEnv: ['./setupTests.ts'],
   testMatch: ['<rootDir>/src/**/__tests__/*.(ts|js)?(x)'],
   transform: {
diff --git a/src/__tests__/__fixtures__/arrays/additional-empty.json b/src/__tests__/__fixtures__/arrays/additional-empty.json
new file mode 100644
index 0000000..b968368
--- /dev/null
+++ b/src/__tests__/__fixtures__/arrays/additional-empty.json
@@ -0,0 +1,4 @@
+{
+  "type": "array",
+  "additionalItems": {}
+}
diff --git a/src/__tests__/__fixtures__/arrays/additional-false.json b/src/__tests__/__fixtures__/arrays/additional-false.json
new file mode 100644
index 0000000..024923c
--- /dev/null
+++ b/src/__tests__/__fixtures__/arrays/additional-false.json
@@ -0,0 +1,4 @@
+{
+  "type": "array",
+  "additionalItems": false
+}
diff --git a/src/__tests__/__fixtures__/arrays/additional-schema.json b/src/__tests__/__fixtures__/arrays/additional-schema.json
new file mode 100644
index 0000000..d795cb4
--- /dev/null
+++ b/src/__tests__/__fixtures__/arrays/additional-schema.json
@@ -0,0 +1,11 @@
+{
+  "type": "array",
+  "additionalItems": {
+    "type": "object",
+    "properties": {
+      "baz": {
+        "type": "number"
+      }
+    }
+  }
+}
diff --git a/src/__tests__/__fixtures__/arrays/additional-true.json b/src/__tests__/__fixtures__/arrays/additional-true.json
new file mode 100644
index 0000000..01777c1
--- /dev/null
+++ b/src/__tests__/__fixtures__/arrays/additional-true.json
@@ -0,0 +1,4 @@
+{
+  "type": "array",
+  "additionalItems": true
+}
diff --git a/src/__tests__/__fixtures__/arrays/with-multiple-arrayish-items.json b/src/__tests__/__fixtures__/arrays/with-multiple-arrayish-items.json
index e45d26d..6a9e8ef 100644
--- a/src/__tests__/__fixtures__/arrays/with-multiple-arrayish-items.json
+++ b/src/__tests__/__fixtures__/arrays/with-multiple-arrayish-items.json
@@ -17,10 +17,7 @@
           "type": "string"
         }
       },
-      "required": [
-        "code",
-        "msg"
-      ]
+      "required": ["code", "msg"]
     }
   ]
 }
diff --git a/src/__tests__/__fixtures__/arrays/with-single-arrayish-items.json b/src/__tests__/__fixtures__/arrays/with-single-arrayish-items.json
index f15e8d3..b5b53c5 100644
--- a/src/__tests__/__fixtures__/arrays/with-single-arrayish-items.json
+++ b/src/__tests__/__fixtures__/arrays/with-single-arrayish-items.json
@@ -14,10 +14,7 @@
           "type": "string"
         }
       },
-      "required": [
-        "code",
-        "msg"
-      ]
+      "required": ["code", "msg"]
     }
   ]
 }
diff --git a/src/__tests__/__fixtures__/objects/additional-empty.json b/src/__tests__/__fixtures__/objects/additional-empty.json
new file mode 100644
index 0000000..08c87bb
--- /dev/null
+++ b/src/__tests__/__fixtures__/objects/additional-empty.json
@@ -0,0 +1,4 @@
+{
+  "type": "object",
+  "additionalProperties": {}
+}
diff --git a/src/__tests__/__fixtures__/objects/additional-false.json b/src/__tests__/__fixtures__/objects/additional-false.json
new file mode 100644
index 0000000..97ea2a4
--- /dev/null
+++ b/src/__tests__/__fixtures__/objects/additional-false.json
@@ -0,0 +1,4 @@
+{
+  "type": "object",
+  "additionalProperties": false
+}
diff --git a/src/__tests__/__fixtures__/objects/additional-schema.json b/src/__tests__/__fixtures__/objects/additional-schema.json
new file mode 100644
index 0000000..66de7a3
--- /dev/null
+++ b/src/__tests__/__fixtures__/objects/additional-schema.json
@@ -0,0 +1,11 @@
+{
+  "type": "object",
+  "additionalProperties": {
+    "type": "object",
+    "properties": {
+      "baz": {
+        "type": "number"
+      }
+    }
+  }
+}
diff --git a/src/__tests__/__fixtures__/objects/additional-true.json b/src/__tests__/__fixtures__/objects/additional-true.json
new file mode 100644
index 0000000..9bc2c95
--- /dev/null
+++ b/src/__tests__/__fixtures__/objects/additional-true.json
@@ -0,0 +1,4 @@
+{
+  "type": "object",
+  "additionalProperties": true
+}
diff --git a/src/__tests__/__snapshots__/tree.spec.ts.snap b/src/__tests__/__snapshots__/tree.spec.ts.snap
index 15893f9..28d8a9e 100644
--- a/src/__tests__/__snapshots__/tree.spec.ts.snap
+++ b/src/__tests__/__snapshots__/tree.spec.ts.snap
@@ -296,6 +296,61 @@ exports[`SchemaTree output compound keywords given oneOf combiner placed next to
 "
 `;
 
+exports[`SchemaTree output should generate valid tree for arrays/additional-empty.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: array
+   ├─ primaryType: array
+   └─ children
+      └─ 0
+         └─ #/additionalItems
+"
+`;
+
+exports[`SchemaTree output should generate valid tree for arrays/additional-false.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: array
+   ├─ primaryType: array
+   └─ children
+      └─ 0
+         └─ #/additionalItems
+            └─ value: false
+"
+`;
+
+exports[`SchemaTree output should generate valid tree for arrays/additional-schema.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: array
+   ├─ primaryType: array
+   └─ children
+      └─ 0
+         └─ #/additionalItems
+            ├─ types
+            │  └─ 0: object
+            ├─ primaryType: object
+            └─ children
+               └─ 0
+                  └─ #/additionalItems/properties/baz
+                     ├─ types
+                     │  └─ 0: number
+                     └─ primaryType: number
+"
+`;
+
+exports[`SchemaTree output should generate valid tree for arrays/additional-true.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: array
+   ├─ primaryType: array
+   └─ children
+      └─ 0
+         └─ #/additionalItems
+            └─ value: true
+"
+`;
+
 exports[`SchemaTree output should generate valid tree for arrays/of-allofs.json 1`] = `
 "└─ #
    ├─ types
@@ -823,7 +878,16 @@ exports[`SchemaTree output should generate valid tree for combiners/allOfs/neste
       │  └─ #/properties/order
       │     ├─ types
       │     │  └─ 0: object
-      │     └─ primaryType: object
+      │     ├─ primaryType: object
+      │     └─ children
+      │        └─ 0
+      │           └─ #/properties/order/additionalProperties
+      │              ├─ types
+      │              │  └─ 0: string
+      │              ├─ primaryType: string
+      │              └─ enum
+      │                 ├─ 0: ASC
+      │                 └─ 1: DESC
       └─ 7
          └─ #/properties/nextToken
             ├─ types
@@ -1246,6 +1310,61 @@ exports[`SchemaTree output should generate valid tree for formats-schema.json 1`
 "
 `;
 
+exports[`SchemaTree output should generate valid tree for objects/additional-empty.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: object
+   ├─ primaryType: object
+   └─ children
+      └─ 0
+         └─ #/additionalProperties
+"
+`;
+
+exports[`SchemaTree output should generate valid tree for objects/additional-false.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: object
+   ├─ primaryType: object
+   └─ children
+      └─ 0
+         └─ #/additionalProperties
+            └─ value: false
+"
+`;
+
+exports[`SchemaTree output should generate valid tree for objects/additional-schema.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: object
+   ├─ primaryType: object
+   └─ children
+      └─ 0
+         └─ #/additionalProperties
+            ├─ types
+            │  └─ 0: object
+            ├─ primaryType: object
+            └─ children
+               └─ 0
+                  └─ #/additionalProperties/properties/baz
+                     ├─ types
+                     │  └─ 0: number
+                     └─ primaryType: number
+"
+`;
+
+exports[`SchemaTree output should generate valid tree for objects/additional-true.json 1`] = `
+"└─ #
+   ├─ types
+   │  └─ 0: object
+   ├─ primaryType: object
+   └─ children
+      └─ 0
+         └─ #/additionalProperties
+            └─ value: true
+"
+`;
+
 exports[`SchemaTree output should generate valid tree for references/base.json 1`] = `
 "└─ #
    ├─ types
diff --git a/src/__tests__/tree.spec.ts b/src/__tests__/tree.spec.ts
index 721cf98..0f88231 100644
--- a/src/__tests__/tree.spec.ts
+++ b/src/__tests__/tree.spec.ts
@@ -907,6 +907,34 @@ describe('SchemaTree', () => {
         tree.root.children[0].children[1].annotations.description,
       ).toEqual('_Everyone_ ~hates~ loves caves');
     });
+
+    it('should render true/false schemas', () => {
+      const schema = {
+        type: 'object',
+        properties: {
+          bear: true,
+          cave: false,
+        },
+      };
+
+      const tree = new SchemaTree(schema);
+      tree.populate();
+
+      expect(printTree(schema)).toMatchInlineSnapshot(`
+        "└─ #
+           ├─ types
+           │  └─ 0: object
+           ├─ primaryType: object
+           └─ children
+              ├─ 0
+              │  └─ #/properties/bear
+              │     └─ value: true
+              └─ 1
+                 └─ #/properties/cave
+                    └─ value: false
+        "
+      `);
+    });
   });
 
   describe('position', () => {
diff --git a/src/__tests__/utils/printTree.ts b/src/__tests__/utils/printTree.ts
index 18d7cbb..f329a91 100644
--- a/src/__tests__/utils/printTree.ts
+++ b/src/__tests__/utils/printTree.ts
@@ -2,8 +2,9 @@ import { pathToPointer } from '@stoplight/json';
 import type { Dictionary } from '@stoplight/types';
 import * as treeify from 'treeify';
 
-import { isMirroredNode, isReferenceNode, isRegularNode } from '../../guards';
+import { isBooleanishNode, isMirroredNode, isReferenceNode, isRegularNode, isRootNode } from '../../guards';
 import type { MirroredSchemaNode, ReferenceNode, RegularNode, SchemaNode } from '../../nodes';
+import type { BooleanishNode } from '../../nodes/BooleanishNode';
 import type { SchemaTreeOptions } from '../../tree';
 import { SchemaTree } from '../../tree';
 import type { SchemaFragment } from '../../types';
@@ -41,6 +42,12 @@ function printReferenceNode(node: ReferenceNode) {
   };
 }
 
+function printBooleanishNode(node: BooleanishNode) {
+  return {
+    value: node.fragment,
+  };
+}
+
 function printMirrorNode(node: MirroredSchemaNode): any {
   return {
     mirrors: pathToPointer(node.mirroredNode.path as string[]),
@@ -48,15 +55,17 @@ function printMirrorNode(node: MirroredSchemaNode): any {
 }
 
 function printNode(node: SchemaNode) {
-  return isMirroredNode(node)
-    ? printMirrorNode(node)
-    : isRegularNode(node)
-    ? printRegularNode(node)
-    : isReferenceNode(node)
-    ? printReferenceNode(node)
-    : {
-        kind: 'unknown node',
-      };
+  if (isMirroredNode(node)) {
+    return printMirrorNode(node);
+  } else if (isRegularNode(node)) {
+    return printRegularNode(node);
+  } else if (isReferenceNode(node)) {
+    return printReferenceNode(node);
+  } else if (isBooleanishNode(node)) {
+    return printBooleanishNode(node);
+  } else if (isRootNode(node)) {
+    return {};
+  }
 }
 
 function prepareTree(node: SchemaNode) {
diff --git a/src/accessors/getValidations.ts b/src/accessors/getValidations.ts
index 2d5394d..6425248 100644
--- a/src/accessors/getValidations.ts
+++ b/src/accessors/getValidations.ts
@@ -12,8 +12,8 @@ const VALIDATION_TYPES: Partial<Dictionary<(keyof SchemaFragment)[], SchemaNodeK
   get integer() {
     return this.number;
   },
-  object: ['additionalProperties', 'minProperties', 'maxProperties'],
-  array: ['additionalItems', 'minItems', 'maxItems', 'uniqueItems'],
+  object: ['minProperties', 'maxProperties'],
+  array: ['minItems', 'maxItems', 'uniqueItems'],
 };
 
 function getTypeValidations(types: SchemaNodeKind[]): (keyof SchemaFragment)[] | null {
diff --git a/src/guards/nodes.ts b/src/guards/nodes.ts
index 0557b41..55c3e48 100644
--- a/src/guards/nodes.ts
+++ b/src/guards/nodes.ts
@@ -7,6 +7,7 @@ import {
   RootNode,
   SchemaNode,
 } from '../nodes';
+import type { BooleanishNode } from '../nodes/BooleanishNode';
 
 export function isSchemaNode(node: unknown): node is SchemaNode {
   const name = Object.getPrototypeOf(node).constructor.name;
@@ -34,3 +35,7 @@ export function isMirroredNode(node: SchemaNode): node is MirroredSchemaNode {
 export function isReferenceNode(node: SchemaNode): node is ReferenceNode {
   return 'external' in node && 'value' in node;
 }
+
+export function isBooleanishNode(node: SchemaNode): node is BooleanishNode {
+  return typeof node.fragment === 'boolean';
+}
diff --git a/src/nodes/BaseNode.ts b/src/nodes/BaseNode.ts
index 29d50b6..9546f80 100644
--- a/src/nodes/BaseNode.ts
+++ b/src/nodes/BaseNode.ts
@@ -1,4 +1,3 @@
-import type { SchemaFragment } from '../types';
 import type { MirroredRegularNode } from './mirrored';
 import type { RegularNode } from './RegularNode';
 import type { RootNode } from './RootNode';
@@ -35,7 +34,7 @@ export abstract class BaseNode {
     return this.pos === this.parentChildren.length - 1;
   }
 
-  protected constructor(public readonly fragment: SchemaFragment) {
+  protected constructor() {
     this.id = String(SEED++);
     this.subpath = [];
   }
diff --git a/src/nodes/BooleanishNode.ts b/src/nodes/BooleanishNode.ts
new file mode 100644
index 0000000..57fad0e
--- /dev/null
+++ b/src/nodes/BooleanishNode.ts
@@ -0,0 +1,7 @@
+import { BaseNode } from './BaseNode';
+
+export class BooleanishNode extends BaseNode {
+  constructor(public readonly fragment: boolean) {
+    super();
+  }
+}
diff --git a/src/nodes/ReferenceNode.ts b/src/nodes/ReferenceNode.ts
index b9401b7..b05bff1 100644
--- a/src/nodes/ReferenceNode.ts
+++ b/src/nodes/ReferenceNode.ts
@@ -7,8 +7,8 @@ import { BaseNode } from './BaseNode';
 export class ReferenceNode extends BaseNode {
   public readonly value: string | null;
 
-  constructor(fragment: SchemaFragment, public readonly error: string | null) {
-    super(fragment);
+  constructor(public readonly fragment: SchemaFragment, public readonly error: string | null) {
+    super();
 
     this.value = unwrapStringOrNull(fragment.$ref);
   }
diff --git a/src/nodes/RegularNode.ts b/src/nodes/RegularNode.ts
index 865ac24..ed8aa7a 100644
--- a/src/nodes/RegularNode.ts
+++ b/src/nodes/RegularNode.ts
@@ -10,6 +10,7 @@ import { isDeprecated } from '../accessors/isDeprecated';
 import { unwrapArrayOrNull, unwrapStringOrNull } from '../accessors/unwrap';
 import type { SchemaFragment } from '../types';
 import { BaseNode } from './BaseNode';
+import type { BooleanishNode } from './BooleanishNode';
 import type { ReferenceNode } from './ReferenceNode';
 import { MirroredSchemaNode, SchemaAnnotations, SchemaCombinerName, SchemaNodeKind } from './types';
 
@@ -25,14 +26,14 @@ export class RegularNode extends BaseNode {
   public readonly title: string | null;
   public readonly deprecated: boolean;
 
-  public children: (RegularNode | ReferenceNode | MirroredSchemaNode)[] | null | undefined;
+  public children: (RegularNode | BooleanishNode | ReferenceNode | MirroredSchemaNode)[] | null | undefined;
 
   public readonly annotations: Readonly<Partial<Dictionary<unknown, SchemaAnnotations>>>;
   public readonly validations: Readonly<Dictionary<unknown>>;
   public readonly originalFragment: SchemaFragment;
 
   constructor(public readonly fragment: SchemaFragment, context?: { originalFragment?: SchemaFragment }) {
-    super(fragment);
+    super();
 
     this.$id = unwrapStringOrNull('id' in fragment ? fragment.id : fragment.$id);
     this.types = getTypes(fragment);
diff --git a/src/nodes/RootNode.ts b/src/nodes/RootNode.ts
index 6c744b4..9d7f8f1 100644
--- a/src/nodes/RootNode.ts
+++ b/src/nodes/RootNode.ts
@@ -7,7 +7,7 @@ export class RootNode extends BaseNode {
   public readonly children: SchemaNode[];
 
   constructor(public readonly fragment: SchemaFragment) {
-    super(fragment);
+    super();
     this.children = [];
   }
 }
diff --git a/src/nodes/index.ts b/src/nodes/index.ts
index 4bd4715..b60c970 100644
--- a/src/nodes/index.ts
+++ b/src/nodes/index.ts
@@ -1,4 +1,5 @@
 export { BaseNode } from './BaseNode';
+export { BooleanishNode } from './BooleanishNode';
 export * from './mirrored';
 export { ReferenceNode } from './ReferenceNode';
 export { RegularNode } from './RegularNode';
diff --git a/src/nodes/mirrored/MirroredReferenceNode.ts b/src/nodes/mirrored/MirroredReferenceNode.ts
index d1404c8..89cccf4 100644
--- a/src/nodes/mirrored/MirroredReferenceNode.ts
+++ b/src/nodes/mirrored/MirroredReferenceNode.ts
@@ -1,9 +1,13 @@
+import type { SchemaFragment } from '../../types';
 import { BaseNode } from '../BaseNode';
 import type { ReferenceNode } from '../ReferenceNode';
 
 export class MirroredReferenceNode extends BaseNode implements ReferenceNode {
+  public readonly fragment: SchemaFragment;
+
   constructor(public readonly mirroredNode: ReferenceNode) {
-    super(mirroredNode.fragment);
+    super();
+    this.fragment = mirroredNode.fragment;
   }
 
   get error() {
diff --git a/src/nodes/mirrored/MirroredRegularNode.ts b/src/nodes/mirrored/MirroredRegularNode.ts
index 7eb07b7..873c15f 100644
--- a/src/nodes/mirrored/MirroredRegularNode.ts
+++ b/src/nodes/mirrored/MirroredRegularNode.ts
@@ -1,15 +1,17 @@
 import type { Dictionary } from '@stoplight/types';
 
-import { isRegularNode } from '../../guards';
+import { isReferenceNode, isRegularNode } from '../../guards';
 import type { SchemaFragment } from '../../types';
 import { isNonNullable } from '../../utils';
 import { BaseNode } from '../BaseNode';
+import { BooleanishNode } from '../BooleanishNode';
 import type { ReferenceNode } from '../ReferenceNode';
 import type { RegularNode } from '../RegularNode';
 import type { SchemaAnnotations, SchemaCombinerName, SchemaNodeKind } from '../types';
 import { MirroredReferenceNode } from './MirroredReferenceNode';
 
 export class MirroredRegularNode extends BaseNode implements RegularNode {
+  public readonly fragment: SchemaFragment;
   public readonly $id!: string | null;
   public readonly types!: SchemaNodeKind[] | null;
   public readonly primaryType!: SchemaNodeKind | null;
@@ -28,10 +30,14 @@ export class MirroredRegularNode extends BaseNode implements RegularNode {
   public readonly simple!: boolean;
   public readonly unknown!: boolean;
 
-  private readonly cache: WeakMap<RegularNode | ReferenceNode, MirroredRegularNode | MirroredReferenceNode>;
+  private readonly cache: WeakMap<
+    RegularNode | BooleanishNode | ReferenceNode,
+    MirroredRegularNode | BooleanishNode | MirroredReferenceNode
+  >;
 
   constructor(public readonly mirroredNode: RegularNode, context?: { originalFragment?: SchemaFragment }) {
-    super(mirroredNode.fragment);
+    super();
+    this.fragment = mirroredNode.fragment;
     this.originalFragment = context?.originalFragment ?? mirroredNode.originalFragment;
 
     this.cache = new WeakMap();
@@ -59,9 +65,9 @@ export class MirroredRegularNode extends BaseNode implements RegularNode {
 
   private readonly _this: MirroredRegularNode;
 
-  private _children?: (MirroredRegularNode | MirroredReferenceNode)[];
+  private _children?: (MirroredRegularNode | BooleanishNode | MirroredReferenceNode)[];
 
-  public get children(): (MirroredRegularNode | MirroredReferenceNode)[] | null | undefined {
+  public get children(): (MirroredRegularNode | BooleanishNode | MirroredReferenceNode)[] | null | undefined {
     const referencedChildren = this.mirroredNode.children;
 
     if (!isNonNullable(referencedChildren)) {
@@ -74,7 +80,7 @@ export class MirroredRegularNode extends BaseNode implements RegularNode {
       this._children.length = 0;
     }
 
-    const children: (MirroredRegularNode | MirroredReferenceNode)[] = this._children;
+    const children: (MirroredRegularNode | BooleanishNode | MirroredReferenceNode)[] = this._children;
     for (const child of referencedChildren) {
       // this is to avoid pointing at nested mirroring
       const cached = this.cache.get(child);
@@ -84,7 +90,11 @@ export class MirroredRegularNode extends BaseNode implements RegularNode {
         continue;
       }
 
-      const mirroredChild = isRegularNode(child) ? new MirroredRegularNode(child) : new MirroredReferenceNode(child);
+      const mirroredChild = isRegularNode(child)
+        ? new MirroredRegularNode(child)
+        : isReferenceNode(child)
+        ? new MirroredReferenceNode(child)
+        : new BooleanishNode(child.fragment);
 
       mirroredChild.parent = this._this;
       mirroredChild.subpath = child.subpath;
diff --git a/src/nodes/types.ts b/src/nodes/types.ts
index 5d69517..2723d4a 100644
--- a/src/nodes/types.ts
+++ b/src/nodes/types.ts
@@ -1,3 +1,4 @@
+import type { BooleanishNode } from './BooleanishNode';
 import type { MirroredReferenceNode } from './mirrored/MirroredReferenceNode';
 import type { MirroredRegularNode } from './mirrored/MirroredRegularNode';
 import type { ReferenceNode } from './ReferenceNode';
@@ -6,7 +7,7 @@ import type { RootNode } from './RootNode';
 
 export type MirroredSchemaNode = MirroredRegularNode | MirroredReferenceNode;
 
-export type SchemaNode = RootNode | RegularNode | ReferenceNode | MirroredSchemaNode;
+export type SchemaNode = RootNode | RegularNode | BooleanishNode | ReferenceNode | MirroredSchemaNode;
 
 export enum SchemaNodeKind {
   Any = 'any',
diff --git a/src/utils/guards.ts b/src/utils/guards.ts
index 085f14a..778deae 100644
--- a/src/utils/guards.ts
+++ b/src/utils/guards.ts
@@ -1,5 +1,7 @@
 import type { Dictionary } from '@stoplight/types';
 
+import type { SchemaFragment } from '../types';
+
 export function isStringOrNumber(value: unknown): value is number | string {
   return typeof value === 'string' || typeof value === 'number';
 }
@@ -23,3 +25,7 @@ export function isObjectLiteral(maybeObj: unknown): maybeObj is Dictionary<unkno
 export function isNonNullable<T = unknown>(maybeNullable: T): maybeNullable is NonNullable<T> {
   return maybeNullable !== void 0 && maybeNullable !== null;
 }
+
+export function isValidSchemaFragment(maybeSchemaFragment: unknown): maybeSchemaFragment is SchemaFragment {
+  return typeof maybeSchemaFragment === 'boolean' || isObjectLiteral(maybeSchemaFragment);
+}
diff --git a/src/walker/types.ts b/src/walker/types.ts
index 34f7b74..284f73d 100644
--- a/src/walker/types.ts
+++ b/src/walker/types.ts
@@ -13,7 +13,7 @@ export type WalkingOptions = {
 };
 
 export type WalkerSnapshot = {
-  readonly fragment: SchemaFragment;
+  readonly fragment: SchemaFragment | boolean;
   readonly depth: number;
   readonly schemaNode: RegularNode | RootNode;
   readonly path: string[];
@@ -23,7 +23,7 @@ export type WalkerHookAction = 'filter' | 'stepIn';
 export type WalkerHookHandler = (node: SchemaNode) => boolean;
 
 export type WalkerNodeEventHandler = (node: SchemaNode) => void;
-export type WalkerFragmentEventHandler = (node: SchemaFragment) => void;
+export type WalkerFragmentEventHandler = (node: SchemaFragment | boolean) => void;
 export type WalkerErrorEventHandler = (ex: Error) => void;
 
 export type WalkerEmitter = {
diff --git a/src/walker/walker.ts b/src/walker/walker.ts
index 7989d90..02efbd5 100644
--- a/src/walker/walker.ts
+++ b/src/walker/walker.ts
@@ -7,10 +7,11 @@ import { isMirroredNode, isReferenceNode, isRegularNode, isRootNode } from '../g
 import { mergeAllOf } from '../mergers/mergeAllOf';
 import { mergeOneOrAnyOf } from '../mergers/mergeOneOrAnyOf';
 import { MirroredReferenceNode, MirroredRegularNode, MirroredSchemaNode, ReferenceNode, RegularNode } from '../nodes';
+import { BooleanishNode } from '../nodes/BooleanishNode';
 import type { RootNode } from '../nodes/RootNode';
 import { SchemaCombinerName, SchemaNode, SchemaNodeKind } from '../nodes/types';
 import type { SchemaFragment } from '../types';
-import { isNonNullable, isObjectLiteral } from '../utils/guards';
+import { isNonNullable, isObjectLiteral, isValidSchemaFragment } from '../utils/guards';
 import type { WalkerEmitter, WalkerHookAction, WalkerHookHandler, WalkerSnapshot, WalkingOptions } from './types';
 
 type InternalWalkerState = {
@@ -25,7 +26,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
   public readonly path: string[];
   public depth: number;
 
-  protected fragment: SchemaFragment;
+  protected fragment: SchemaFragment | boolean;
   protected schemaNode: RegularNode | RootNode;
 
   private mergedAllOfs: WeakMap<SchemaFragment, SchemaFragment>;
@@ -124,12 +125,19 @@ export class Walker extends EventEmitter<WalkerEmitter> {
     super.emit('enterNode', schemaNode);
 
     const actualNode = isMirroredNode(schemaNode) ? schemaNode.mirroredNode : schemaNode;
-    this.processedFragments.set(schemaNode.fragment, actualNode);
-    this.processedFragments.set(initialFragment, actualNode);
+    if (typeof schemaNode.fragment !== 'boolean' && initialFragment !== null) {
+      this.processedFragments.set(schemaNode.fragment, actualNode);
+      this.processedFragments.set(initialFragment, actualNode);
+    }
 
     this.fragment = schemaNode.fragment;
     this.depth = initialDepth + 1;
 
+    if (!isRootNode(schemaNode)) {
+      schemaNode.parent = initialSchemaNode;
+      schemaNode.subpath = this.path.slice(initialSchemaNode.path.length);
+    }
+
     const isIncluded = this.hooks.filter?.(schemaNode);
 
     if (isIncluded === false) {
@@ -137,11 +145,6 @@ export class Walker extends EventEmitter<WalkerEmitter> {
       return;
     }
 
-    if (!isRootNode(schemaNode)) {
-      schemaNode.parent = initialSchemaNode;
-      schemaNode.subpath = this.path.slice(initialSchemaNode.path.length);
-    }
-
     if ('children' in initialSchemaNode && !isRootNode(schemaNode)) {
       if (initialSchemaNode.children === void 0) {
         (initialSchemaNode as RegularNode).children = [schemaNode];
@@ -186,7 +189,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
   protected walkNodeChildren(): void {
     const { fragment, schemaNode } = this;
 
-    if (!isRegularNode(schemaNode)) return;
+    if (!isRegularNode(schemaNode) || typeof fragment === 'boolean') return;
 
     const state = this.dumpInternalWalkerState();
 
@@ -213,17 +216,26 @@ export class Walker extends EventEmitter<WalkerEmitter> {
           let i = -1;
           for (const item of fragment.items) {
             i++;
-            if (!isObjectLiteral(item)) continue;
+            if (!isValidSchemaFragment(item)) continue;
             this.fragment = item;
             this.restoreInternalWalkerState(state);
             this.path.push('items', String(i));
             this.walk();
           }
-        } else if (isObjectLiteral(fragment.items)) {
-          this.fragment = fragment.items;
-          this.restoreInternalWalkerState(state);
-          this.path.push('items');
-          this.walk();
+        } else {
+          if (isObjectLiteral(fragment.items)) {
+            this.fragment = fragment.items;
+            this.restoreInternalWalkerState(state);
+            this.path.push('items');
+            this.walk();
+          }
+
+          if (isValidSchemaFragment(fragment.additionalItems)) {
+            this.fragment = fragment.additionalItems;
+            this.restoreInternalWalkerState(state);
+            this.path.push('additionalItems');
+            this.walk();
+          }
         }
 
         break;
@@ -231,7 +243,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
         if (isObjectLiteral(fragment.properties)) {
           for (const key of Object.keys(fragment.properties)) {
             const value = fragment.properties[key];
-            if (!isObjectLiteral(value)) continue;
+            if (!isValidSchemaFragment(value)) continue;
             this.fragment = value;
             this.restoreInternalWalkerState(state);
             this.path.push('properties', key);
@@ -242,7 +254,7 @@ export class Walker extends EventEmitter<WalkerEmitter> {
         if (isObjectLiteral(fragment.patternProperties)) {
           for (const key of Object.keys(fragment.patternProperties)) {
             const value = fragment.patternProperties[key];
-            if (!isObjectLiteral(value)) continue;
+            if (!isValidSchemaFragment(value)) continue;
             this.fragment = value;
             this.restoreInternalWalkerState(state);
             this.path.push('patternProperties', key);
@@ -250,6 +262,13 @@ export class Walker extends EventEmitter<WalkerEmitter> {
           }
         }
 
+        if (isValidSchemaFragment(fragment.additionalProperties)) {
+          this.fragment = fragment.additionalProperties;
+          this.restoreInternalWalkerState(state);
+          this.path.push('additionalProperties');
+          this.walk();
+        }
+
         break;
     }
 
@@ -275,11 +294,19 @@ export class Walker extends EventEmitter<WalkerEmitter> {
     }
   }
 
-  protected processFragment(): [SchemaNode, ProcessedFragment] {
+  protected processFragment(): [SchemaNode, ProcessedFragment | null] {
     const { walkingOptions, path, fragment: originalFragment, depth } = this;
     let { fragment } = this;
 
-    let retrieved = isNonNullable(fragment) ? this.retrieveFromFragment(fragment, originalFragment) : null;
+    if (typeof fragment === 'boolean') {
+      return [new BooleanishNode(fragment), null];
+    }
+
+    if (typeof originalFragment === 'boolean') {
+      throw new TypeError('Original fragment cannot be a boolean');
+    }
+
+    let retrieved = isNonNullable(fragment) ? this.retrieveFromFragment(fragment, fragment) : null;
 
     if (retrieved) {
       return retrieved;

From 636840c263b426d62e699aa6ca83da9acf283e67 Mon Sep 17 00:00:00 2001
From: SB-harshitajadhav <165134656+SB-harshitajadhav@users.noreply.github.com>
Date: Wed, 8 Jan 2025 17:34:39 +0530
Subject: [PATCH 11/11] chore(deps): bump cross-spawn 7.0.5 (#38)

---
 package.json |  3 +++
 yarn.lock    | 69 ++++++----------------------------------------------
 2 files changed, 10 insertions(+), 62 deletions(-)

diff --git a/package.json b/package.json
index a4b078e..f4b6090 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,9 @@
     "ts-jest": "^26.4.4",
     "typescript": "^4.1.3"
   },
+  "resolutions": {
+    "cross-spawn": "7.0.5"
+  },
   "lint-staged": {
     "*.{ts,tsx}": [
       "eslint --fix --cache --cache-location .cache"
diff --git a/yarn.lock b/yarn.lock
index 0a95cbc..786cc24 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2441,30 +2441,10 @@ create-error-class@^3.0.0:
   dependencies:
     capture-stack-trace "^1.0.0"
 
-cross-spawn@^5.0.1:
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
-  integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
-  dependencies:
-    lru-cache "^4.0.1"
-    shebang-command "^1.2.0"
-    which "^1.2.9"
-
-cross-spawn@^6.0.0, cross-spawn@^6.0.5:
-  version "6.0.5"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
-  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
-  dependencies:
-    nice-try "^1.0.4"
-    path-key "^2.0.1"
-    semver "^5.5.0"
-    shebang-command "^1.2.0"
-    which "^1.2.9"
-
-cross-spawn@^7.0.0, cross-spawn@^7.0.2:
-  version "7.0.3"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
-  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+cross-spawn@7.0.5, cross-spawn@^5.0.1, cross-spawn@^6.0.0, cross-spawn@^6.0.5, cross-spawn@^7.0.0, cross-spawn@^7.0.2:
+  version "7.0.5"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82"
+  integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==
   dependencies:
     path-key "^3.1.0"
     shebang-command "^2.0.0"
@@ -5688,14 +5668,6 @@ lowercase-keys@^1.0.0:
   resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
   integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
 
-lru-cache@^4.0.1:
-  version "4.1.5"
-  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
-  integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
-  dependencies:
-    pseudomap "^1.0.2"
-    yallist "^2.1.2"
-
 lru-cache@^5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@@ -6078,11 +6050,6 @@ nerf-dart@^1.0.0:
   resolved "https://registry.yarnpkg.com/nerf-dart/-/nerf-dart-1.0.0.tgz#e6dab7febf5ad816ea81cf5c629c5a0ebde72c1a"
   integrity sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=
 
-nice-try@^1.0.4:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
-  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
-
 node-emoji@^1.10.0:
   version "1.10.0"
   resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da"
@@ -6856,7 +6823,7 @@ path-is-inside@^1.0.1, path-is-inside@^1.0.2, path-is-inside@~1.0.2:
   resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
   integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
 
-path-key@^2.0.0, path-key@^2.0.1:
+path-key@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
   integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
@@ -7052,11 +7019,6 @@ prr@~1.0.1:
   resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
   integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
 
-pseudomap@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
-  integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
-
 psl@^1.1.28:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/psl/-/psl-1.3.0.tgz#e1ebf6a3b5564fa8376f3da2275da76d875ca1bd"
@@ -7713,7 +7675,7 @@ semver-regex@^2.0.0:
   resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338"
   integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==
 
-"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1:
+"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.4.1, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -7762,13 +7724,6 @@ sha@^3.0.0:
   dependencies:
     graceful-fs "^4.1.2"
 
-shebang-command@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
-  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
-  dependencies:
-    shebang-regex "^1.0.0"
-
 shebang-command@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -7776,11 +7731,6 @@ shebang-command@^2.0.0:
   dependencies:
     shebang-regex "^3.0.0"
 
-shebang-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
-  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
-
 shebang-regex@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
@@ -8957,7 +8907,7 @@ which-pm-runs@^1.0.0:
   resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
   integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
 
-which@1, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1:
+which@1, which@^1.2.14, which@^1.3.0, which@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -9112,11 +9062,6 @@ y18n@^4.0.0:
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
   integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
 
-yallist@^2.1.2:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
-  integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
-
 yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"