{"version":3,"sources":["/Users/erunion/code/readme/oas/packages/oas/dist/reducer/index.cjs","../../src/reducer/index.ts"],"names":[],"mappings":"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","file":"/Users/erunion/code/readme/oas/packages/oas/dist/reducer/index.cjs","sourcesContent":[null,"import type { OpenAPIV3_1 } from 'openapi-types';\nimport type { ComponentsObject, HttpMethods, OASDocument, OperationObject, TagObject } from '../types.js';\n\nimport jsonPointer from 'jsonpointer';\n\nimport { query } from '../analyzer/util.js';\nimport { decodePointer } from '../lib/refs.js';\nimport { isOpenAPI31, isRef } from '../types.js';\nimport { supportedMethods } from '../utils.js';\n\nexport class OpenAPIReducer {\n  private definition: OASDocument;\n\n  /**\n   * A collection of `$ref` pointers that are used within our reduced API definition. This is used\n   * to ensure that all referenced schemas are retained in our resulting API definition. Not\n   * retaining them would result in an invalid OpenAPI definition.\n   */\n  private $refs: Set<string> = new Set();\n\n  /**\n   * A collection of OpenAPI tags that are used within our reduced API definition.\n   */\n  private usedTags: Set<string> = new Set();\n\n  /**\n   * A collection of OpenAPI paths and operations that are cross-referenced from any other paths\n   * and operations that we are reducing. This collection is used in order to ensure that those\n   * schemas are retained with our resulting API definition. Not retaining them would result in an\n   * invalid OpenAPI definition.\n   */\n  private retainPathMethods: Set<`${string}|${string}`> = new Set();\n\n  /**\n   * A collection of OpenAPI webhook names and methods that are cross-referenced from any other\n   * schemas. This collection, like `retainPathMethods`, is used in order to ensure that those\n   * schemas are retained with our resulting API definition. Not retaining them would result in an\n   * invalid OpenAPI definition.\n   */\n  private retainWebhookMethods: Set<`${string}|${string}`> = new Set();\n\n  /**\n   * An array of OpenAPI tags to reduce down to.\n   */\n  private tagsToReduceBy: string[] = [];\n\n  /**\n   * A collection of OpenAPI paths and operations to reduce down to.\n   */\n  private pathsToReduceBy: Record<string, '*' | string[]> = {};\n\n  /**\n   * A collection of OpenAPI webhooks to reduce down to.\n   */\n  private webhooksToReduceBy: Record<string, '*' | string[]> = {};\n\n  private hasTagsToReduceBy: boolean = false;\n  private hasPathsToReduceBy: boolean = false;\n  private hasWebhooksToReduceBy: boolean = false;\n\n  private constructor(definition: OASDocument) {\n    this.definition = structuredClone(definition);\n  }\n\n  /**\n   * Initialize a new instance of the `OpenAPIReducer`. The reducer allows you to reduce an OpenAPI\n   * definition down to only the information necessary to fulfill a specific set of tags, paths,\n   * operations, and webhooks.\n   *\n   * OpenAPI reduction can be helpful not only to isolate and troubleshoot issues with large API\n   * definitions, but also to compress a large API definition down to a manageable size containing\n   * a specific set of items.\n   *\n   * All OpenAPI definitions reduced will still be fully functional and valid OpenAPI definitions.\n   *\n   * @param definition An OpenAPI definition to reduce.\n   */\n  static init(definition: OASDocument): OpenAPIReducer {\n    return new OpenAPIReducer(definition);\n  }\n\n  /**\n   * Mark an OpenAPI tag to be included in our reduced API definition. Tag casing does not matter.\n   *\n   * @param tag The tag to mark for reduction.\n   */\n  byTag(tag: string): OpenAPIReducer {\n    this.tagsToReduceBy.push(tag.toLowerCase());\n    return this;\n  }\n\n  /**\n   * Mark an entire OpenAPI path, and all methods that it contains, to be included in your reduced\n   * API definition. Path casing does not matter.\n   *\n   * @param path The path to mark for reduction.\n   */\n  byPath(path: string): OpenAPIReducer {\n    this.pathsToReduceBy[path.toLowerCase()] = '*';\n    return this;\n  }\n\n  /**\n   * Mark a single OpenAPI operation to be included in your reduced API definition. If the path\n   * that this operation is a part of utilizes common parameters, those will be automatically\n   * included. Path and method casing does not matter.\n   *\n   * Note that if you previously called `.byPath()` to reduce an entire path down, calling\n   * `.byOperation()` will override that to just reduce this specific method (or this plus\n   * subsequent calls to `.byOperation()`).\n   *\n   * @param path The path that the operation is a part of.\n   * @param method The HTTP method of the operation to mark for reduction.\n   *\n   */\n  byOperation(path: string, method: string): OpenAPIReducer {\n    const pathLC = path.toLowerCase(); // Casing should not matter.\n    const methodLC = method.toLowerCase();\n\n    if (this.pathsToReduceBy[pathLC] && Array.isArray(this.pathsToReduceBy[pathLC])) {\n      this.pathsToReduceBy[pathLC].push(methodLC);\n    } else {\n      this.pathsToReduceBy[pathLC] = [methodLC];\n    }\n\n    return this;\n  }\n\n  /**\n   * Mark an OpenAPI webhook (and all of its operations) to be included in your reduced API\n   * definition. Casing does not matter.\n   *\n   * @param webhookName The webhook name to mark for reduction.\n   */\n  byWebhook(webhookName: string): OpenAPIReducer;\n\n  /**\n   * Mark a single OpenAPI webhook operation to be included in your reduced API definition.\n   * Casing does not matter.\n   *\n   * @param webhookName The webhook name that the operation belongs to.\n   * @param method The HTTP method of the webhook operation to mark for reduction.\n   */\n  byWebhook(webhookName: string, method: string): OpenAPIReducer;\n\n  byWebhook(webhookName: string, method?: string): OpenAPIReducer {\n    const nameLC = webhookName.toLowerCase();\n    if (!method) {\n      this.webhooksToReduceBy[nameLC] = '*';\n      return this;\n    }\n\n    const methodLC = method.toLowerCase();\n    if (this.webhooksToReduceBy[nameLC] && Array.isArray(this.webhooksToReduceBy[nameLC])) {\n      this.webhooksToReduceBy[nameLC].push(methodLC);\n    } else {\n      this.webhooksToReduceBy[nameLC] = [methodLC];\n    }\n\n    return this;\n  }\n\n  /**\n   * Reduce the current OpenAPI definition down to the configured filters.\n   *\n   */\n  reduce(): OASDocument {\n    if (!this.definition.openapi) {\n      throw new Error('Sorry, only OpenAPI definitions are supported.');\n    }\n\n    this.hasPathsToReduceBy = Boolean(Object.keys(this.pathsToReduceBy).length);\n    this.hasWebhooksToReduceBy = Boolean(Object.keys(this.webhooksToReduceBy).length);\n    this.hasTagsToReduceBy = Boolean(this.tagsToReduceBy.length);\n\n    // Retain any root-level security definitions, regardless if they're used or not on our reduced\n    // operations.\n    if ('security' in this.definition) {\n      Object.values(this.definition.security || {}).forEach(sec => {\n        Object.keys(sec).forEach(scheme => {\n          this.$refs.add(`#/components/securitySchemes/${scheme}`);\n        });\n      });\n    }\n\n    this.walkPaths();\n    this.walkWebhooks();\n\n    // Recursively accumulate any components that are in use.\n    this.$refs.forEach($ref => {\n      this.accumulateUsedRefs(this.definition, this.$refs, $ref);\n    });\n\n    this.$refs.forEach(ref => {\n      const usedPathRef = this.parsePathRef(ref);\n      if (usedPathRef) {\n        this.retainPathMethods.add(`${usedPathRef.path.toLowerCase()}|${usedPathRef.method.toLowerCase()}`);\n      }\n\n      const usedWebhookRef = this.parseWebhookRef(ref);\n      if (usedWebhookRef) {\n        this.retainWebhookMethods.add(`${usedWebhookRef.name.toLowerCase()}|${usedWebhookRef.method.toLowerCase()}`);\n      }\n    });\n\n    this.reducePaths();\n    this.reduceWebhooks();\n\n    // Require at least one path or one webhook in the result.\n    const hasPaths = Boolean(this.definition.paths && Object.keys(this.definition.paths).length);\n    const hasWebhooks = Boolean(\n      'webhooks' in this.definition && this.definition.webhooks && Object.keys(this.definition.webhooks).length,\n    );\n\n    if (!hasPaths && !hasWebhooks) {\n      throw new Error(\n        'All paths and webhooks in the API definition were removed. Did you supply the right path, operation, or webhook to reduce by?',\n      );\n    }\n\n    // Remove any unused components.\n    if ('components' in this.definition) {\n      Object.keys(this.definition.components || {}).forEach(componentType => {\n        Object.keys(this.definition.components?.[componentType as keyof ComponentsObject] || {}).forEach(component => {\n          // If our `$ref` either is a full, or deep match, then we should preserve it.\n          const refIsUsed =\n            this.$refs.has(`#/components/${componentType}/${component}`) ||\n            Array.from(this.$refs).some(ref => {\n              // Because you can have a `$ref` like `#/components/examples/event-min/value`, which\n              // would be accumulated via our `$refs` query, we want to make sure we account for them.\n              // If we don't look for these then we'll end up removing them from the overall reduced\n              // definition, resulting in data loss and schema corruption.\n              return ref.startsWith(`#/components/${componentType}/${component}/`);\n            });\n\n          if (!refIsUsed) {\n            delete this.definition.components?.[componentType as keyof ComponentsObject]?.[component];\n          }\n        });\n\n        // If this component group is now empty, delete it.\n        if (!Object.keys(this.definition.components?.[componentType as keyof ComponentsObject] || {}).length) {\n          delete this.definition.components?.[componentType as keyof ComponentsObject];\n        }\n      });\n\n      // If this path no longer has any components, delete it.\n      if (!Object.keys(this.definition.components || {}).length) {\n        delete this.definition.components;\n      }\n    }\n\n    // Remove any unused tags.\n    if ('tags' in this.definition) {\n      this.definition.tags = (this.definition.tags ?? []).filter((tag): tag is TagObject => {\n        return Boolean(tag) && this.usedTags.has(tag.name);\n      });\n\n      if (!this.definition.tags?.length) {\n        delete this.definition.tags;\n      }\n    }\n\n    return this.definition;\n  }\n\n  /**\n   * Recursively process a `$ref` pointer and accumulate any other `$ref` pointers that it or its\n   * children use. This handles circular references by skipping `$ref` pointers we have already seen.\n   * Additionally when a `$ref` points to `#/paths` we record the used path + method so we can\n   * retain cross-operation references within the reduced definition.\n   *\n   * @param schema JSON Schema object to look for and accumulate any `$ref` pointers that it may have.\n   * @param $refs Known set of `$ref` pointers.\n   * @param $ref `$ref` pointer to fetch a schema from out of the supplied schema.\n   */\n  private accumulateUsedRefs(schema: Record<string, unknown>, $refs: Set<string>, $ref: string): void {\n    // Record `$ref` pointers aimed at `#/paths` so we can retain any cross-operation references.\n    const pathRef = this.parsePathRef($ref);\n    if (pathRef) {\n      this.retainPathMethods.add(`${pathRef.path.toLowerCase()}|${pathRef.method.toLowerCase()}`);\n    }\n\n    const webhookRef = this.parseWebhookRef($ref);\n    if (webhookRef) {\n      this.retainWebhookMethods.add(`${webhookRef.name.toLowerCase()}|${webhookRef.method.toLowerCase()}`);\n    }\n\n    let $refSchema: unknown;\n    if (typeof $ref === 'string') $refSchema = jsonPointer.get(schema, $ref.substring(1));\n    if ($refSchema === undefined) {\n      // If the schema we have wasn't fully dereferenced or bundled for whatever reason and this\n      // `$ref` that we have doesn't exist here we shouldn't try to search for more `$ref` pointers\n      // in a schema that doesn't exist.\n      return;\n    }\n\n    this.queryForRefPointers($refSchema).forEach(({ value: currRef }) => {\n      // Because it's possible to have a schema property named `$ref` that is not a `$ref` pointer,\n      // which our JSONPath query would pick up as a false positive, we want to exclude that from\n      // `$ref` matching as it's not a reference pointer.\n      const foundRef = this.toRefString(currRef);\n      if (!foundRef) {\n        return;\n      }\n\n      // If we've already processed this `$ref` then don't send us into an infinite loop of processing\n      // circular references.\n      if ($refs.has(foundRef)) {\n        return;\n      }\n\n      $refs.add(foundRef);\n      this.accumulateUsedRefs(schema, $refs, foundRef);\n    });\n  }\n\n  /**\n   * Query a JSON Schema object for any `$ref` pointers using JSONPath and return any pointers that\n   * exist.\n   *\n   * @see {@link https://datatracker.ietf.org/doc/html/rfc9535}\n   * @param schema JSON Schema object to look for any `$ref` pointers within it.\n   */\n  private queryForRefPointers(schema: any) {\n    return query([\"$..['$ref']\"], schema);\n  }\n\n  /**\n   * Normalize a value from a `jsonpath-plus` `$ref` query to a `$ref` pointer because JSONPath\n   * queries may return the property value or the parent.\n   *\n   */\n  private toRefString(value: unknown): string | null {\n    if (typeof value === 'string') {\n      return value;\n    } else if (value && typeof value === 'object' && '$ref' in value && typeof value.$ref === 'string') {\n      return value.$ref;\n    }\n\n    return null;\n  }\n\n  /**\n   * If the given `$ref` points into a path (e.g. `#/paths/~1anything/post/...`), return the path\n   * and method so the reducer can ultimately retain cross-operation references.\n   *\n   */\n  private parsePathRef($ref: string): { path: string; method: string } | null {\n    if (typeof $ref !== 'string' || !$ref.startsWith('#/paths/')) {\n      return null;\n    }\n\n    // Extract path segment and method: `#/paths/<pathSegment>/<method>/...`\n    const match = $ref.match(/^#\\/paths\\/([^/]+)\\/([^/]+)(?:\\/|$)/);\n    if (match) {\n      const pathSegment = match[1];\n      const method = match[2];\n      if (pathSegment && method) {\n        return { path: decodePointer(pathSegment), method };\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   * If the given `$ref` points into webhooks (e.g. `#/webhooks/newBooking/post/...`), return the\n   * webhook name and method so the reducer can retain cross-referenced webhook operations.\n   *\n   */\n  private parseWebhookRef($ref: string): { name: string; method: string } | null {\n    if (typeof $ref !== 'string' || !$ref.startsWith('#/webhooks/')) {\n      return null;\n    }\n\n    // Extract path segment and method: `#/webhooks/<webhookName>/<method>/...`\n    const match = $ref.match(/^#\\/webhooks\\/([^/]+)\\/([^/]+)(?:\\/|$)/);\n    if (match) {\n      const webhookName = match[1];\n      const method = match[2];\n      if (webhookName && method) {\n        return { name: decodePointer(webhookName), method };\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   * Walk through the `paths` in our OpenAPI definition and reduce down any that we know we do not\n   * want to keep and accumulate any `$ref` pointers that we find that may be cross-referenced in\n   * paths, webhooks, operations, and schemas that we _do_ want to keep.\n   *\n   */\n  private walkPaths(): void {\n    if (!('paths' in this.definition) || !this.definition.paths) {\n      return;\n    }\n\n    Object.keys(this.definition.paths).forEach(path => {\n      const pathLC = path.toLowerCase();\n\n      // When only webhooks were requested (no path/operation filter), remove all paths.\n      if (this.hasWebhooksToReduceBy && !this.hasPathsToReduceBy) {\n        delete this.definition.paths?.[path];\n        return;\n      }\n\n      if (this.hasPathsToReduceBy) {\n        if (!(pathLC in this.pathsToReduceBy)) {\n          delete this.definition.paths?.[path];\n          return;\n        }\n      }\n\n      Object.keys(this.definition.paths?.[path] || {}).forEach(method => {\n        // Only process operations and retain any common path-level common properties like\n        // `parameters`, `servers`, `summary`, etc.\n        if (method === 'parameters' || !supportedMethods.includes(method.toLowerCase() as HttpMethods)) {\n          return;\n        }\n\n        if (this.hasPathsToReduceBy) {\n          // If we have paths we want to reduce but this isn't part of our filter set, then ignore.\n          // We'll remove it later.\n          if (\n            this.pathsToReduceBy[pathLC] !== '*' &&\n            Array.isArray(this.pathsToReduceBy[pathLC]) &&\n            !this.pathsToReduceBy[pathLC].includes(method.toLowerCase())\n          ) {\n            return;\n          }\n        }\n\n        const operation = this.definition.paths?.[path]?.[method as HttpMethods] as OperationObject;\n        if (!operation) {\n          throw new Error(`Operation \\`${method} ${path}\\` not found`);\n        }\n\n        if (this.hasTagsToReduceBy) {\n          // If this endpoint either has no tags or none that we want to preseve, then prune it.\n          if (!(operation.tags || []).filter(tag => this.tagsToReduceBy.includes(tag.toLowerCase())).length) {\n            return;\n          }\n        }\n\n        (operation.tags || []).forEach((tag: string) => {\n          this.usedTags.add(tag);\n        });\n\n        // Skipped by the `method === 'parameters'` guard above; accumulate here so refs are only\n        // retained when at least one operation on this path passes all filters.\n        const pathLevelParams = this.definition.paths?.[path]?.parameters;\n        if (pathLevelParams) {\n          this.queryForRefPointers(pathLevelParams).forEach(({ value: ref }) => {\n            const refStr = this.toRefString(ref);\n            if (!refStr) {\n              return;\n            }\n\n            this.$refs.add(refStr);\n            this.accumulateUsedRefs(this.definition, this.$refs, refStr);\n          });\n        }\n\n        this.queryForRefPointers(operation).forEach(({ value: ref }) => {\n          const refStr = this.toRefString(ref);\n          if (!refStr) {\n            return;\n          }\n\n          this.$refs.add(refStr);\n\n          // If this operation has a cross-operation `$ref` pointer then we need to track it so\n          // it's retained.\n          const pathRef = this.parsePathRef(refStr);\n          if (pathRef) {\n            this.retainPathMethods.add(`${pathRef.path.toLowerCase()}|${pathRef.method.toLowerCase()}`);\n          }\n\n          // Re-run through any `$ref` pointers that we found within this operation and search for\n          // any `$ref` pointers that they also may be using.\n          this.accumulateUsedRefs(this.definition, this.$refs, refStr);\n        });\n\n        Object.values(operation.security || {}).forEach(sec => {\n          Object.keys(sec).forEach(scheme => {\n            this.$refs.add(`#/components/securitySchemes/${scheme}`);\n          });\n        });\n      });\n    });\n  }\n\n  /**\n   * Walk through the `webhooks` in our OpenAPI definition and reduce down any that we know we do\n   * not want to keep and accumulate any `$ref` pointers that we find that may be cross-referenced\n   * in paths, operations, and schemas that we _do_ want to keep.\n   *\n   */\n  private walkWebhooks() {\n    if (!isOpenAPI31(this.definition)) {\n      return;\n    } else if (!('webhooks' in this.definition) || !this.definition.webhooks) {\n      return;\n    }\n\n    const definition = this.definition satisfies OpenAPIV3_1.Document;\n\n    Object.keys(definition.webhooks || {}).forEach(webhookName => {\n      const nameLC = webhookName.toLowerCase();\n      if (this.hasWebhooksToReduceBy && !(nameLC in this.webhooksToReduceBy)) {\n        return;\n      }\n\n      const webhook = definition.webhooks?.[webhookName];\n      if (!webhook || typeof webhook !== 'object') {\n        return;\n      }\n\n      Object.keys(webhook).forEach(method => {\n        // Only process operations and retain any common path-level common properties like\n        // `parameters`, `servers`, `summary`, etc.\n        if (method === 'parameters' || !supportedMethods.includes(method.toLowerCase() as HttpMethods)) {\n          return;\n        }\n\n        if (this.hasWebhooksToReduceBy) {\n          // If we have webhooks we want to reduce but this isn't part of our filter set, then\n          // ignore. We'll remove it later.\n          const methodFilter = this.webhooksToReduceBy[nameLC];\n          if (methodFilter !== '*' && Array.isArray(methodFilter) && !methodFilter.includes(method.toLowerCase())) {\n            return;\n          }\n        }\n\n        /**\n         * If this webhook path item is a `$ref` then ignore it.\n         * @fixme we should better support reducing this.\n         */\n        if (isRef(webhook)) {\n          return;\n        }\n\n        const operation = webhook[method as HttpMethods] as OperationObject;\n        if (!operation) {\n          return;\n        }\n\n        if (this.hasTagsToReduceBy) {\n          // If this operation either has no tags or none that we want to preseve, then prune it.\n          if (!(operation.tags || []).filter(tag => this.tagsToReduceBy.includes(tag.toLowerCase())).length) {\n            return;\n          }\n        }\n\n        (operation.tags || []).forEach((tag: string) => {\n          this.usedTags.add(tag);\n        });\n\n        // Skipped by the `method === 'parameters'` guard above; accumulate here so refs are only\n        // retained when at least one operation on this webhook passes all filters.\n        if (webhook.parameters) {\n          this.queryForRefPointers(webhook.parameters).forEach(({ value: ref }) => {\n            const refStr = this.toRefString(ref);\n            if (!refStr) {\n              return;\n            }\n\n            this.$refs.add(refStr);\n            this.accumulateUsedRefs(definition, this.$refs, refStr);\n          });\n        }\n\n        this.queryForRefPointers(operation).forEach(({ value: ref }) => {\n          const refStr = this.toRefString(ref);\n          if (!refStr) {\n            return;\n          }\n\n          this.$refs.add(refStr);\n          const pathRef = this.parsePathRef(refStr);\n          if (pathRef) {\n            this.retainPathMethods.add(`${pathRef.path.toLowerCase()}|${pathRef.method.toLowerCase()}`);\n          }\n\n          const webhookRef = this.parseWebhookRef(refStr);\n          if (webhookRef) {\n            this.retainWebhookMethods.add(`${webhookRef.name.toLowerCase()}|${webhookRef.method.toLowerCase()}`);\n          }\n\n          this.accumulateUsedRefs(definition, this.$refs, refStr);\n        });\n\n        Object.values(operation.security || {}).forEach(sec => {\n          Object.keys(sec).forEach(scheme => {\n            this.$refs.add(`#/components/securitySchemes/${scheme}`);\n          });\n        });\n      });\n    });\n  }\n\n  /**\n   * Prune back our `paths` object in the OpenAPI definition to only include paths that we want to\n   * preserve.\n   *\n   */\n  private reducePaths(): void {\n    if (!('paths' in this.definition) || !this.definition.paths) {\n      return;\n    }\n\n    Object.keys(this.definition.paths).forEach(path => {\n      const pathLC = path.toLowerCase();\n\n      if (this.hasPathsToReduceBy && !(pathLC in this.pathsToReduceBy)) {\n        delete this.definition.paths?.[path];\n        return;\n      }\n\n      Object.keys(this.definition.paths?.[path] || {}).forEach(method => {\n        const methodLC = method.toLowerCase();\n\n        // Only process operations and retain any common path-level common properties like\n        // `parameters`, `servers`, `summary`, etc.\n        if (method === 'parameters' || !supportedMethods.includes(methodLC as HttpMethods)) {\n          return;\n        }\n\n        const retainedByRef =\n          this.retainPathMethods.has(`${pathLC}|${methodLC}`) ||\n          Array.from(this.$refs).some(ref => {\n            const pathRef = this.parsePathRef(ref);\n            return pathRef?.path.toLowerCase() === pathLC && pathRef?.method.toLowerCase() === methodLC;\n          });\n\n        if (methodLC !== 'parameters') {\n          // If we're reducing paths and this operation isn't part of our filter set, and it's\n          // not a cross-referenced operation that we want to retain, then we should prune it.\n          if (this.hasPathsToReduceBy) {\n            if (\n              !retainedByRef &&\n              this.pathsToReduceBy[pathLC] !== '*' &&\n              Array.isArray(this.pathsToReduceBy[pathLC]) &&\n              !this.pathsToReduceBy[pathLC].includes(methodLC)\n            ) {\n              delete this.definition.paths?.[path]?.[method as HttpMethods];\n              return;\n            }\n          }\n        }\n\n        const operation = this.definition.paths?.[path]?.[method as HttpMethods];\n        if (!operation) {\n          throw new Error(`Operation \\`${method} ${path}\\` not found`);\n        }\n\n        // If we're reducing by tags and this operation doesn't live in one of those, remove it.\n        if (this.hasTagsToReduceBy) {\n          // If this operation doesn't have any tags that we want to preserve, and it isn't\n          // cross-referenced from an operation we _do_ want to preserve, then remove it.\n          if (!(operation.tags || []).filter(tag => this.tagsToReduceBy.includes(tag.toLowerCase())).length) {\n            if (!retainedByRef) {\n              delete this.definition.paths?.[path]?.[method as HttpMethods];\n            }\n\n            return;\n          }\n        }\n\n        // Accumulate a list of used tags so we can filter out any ones that we don't need later.\n        if ('tags' in operation) {\n          operation.tags?.forEach((tag: string) => {\n            this.usedTags.add(tag);\n          });\n        }\n\n        // Accumulate any used operation-level security schemas that we need to retain.\n        if ('security' in operation) {\n          Object.values(operation.security || {}).forEach(sec => {\n            Object.keys(sec).forEach(scheme => {\n              this.$refs.add(`#/components/securitySchemes/${scheme}`);\n            });\n          });\n        }\n      });\n\n      // If this path no longer has any methods, delete it.\n      if (!Object.keys(this.definition.paths?.[path] || {}).length) {\n        delete this.definition.paths?.[path];\n      }\n    });\n\n    // If we don't have any more paths after cleanup, and we don't have any webhooks, then throw\n    // an error because an OpenAPI definition must have at least one path.\n    if (!Object.keys(this.definition.paths || {}).length) {\n      if (!(this.definition.webhooks && Object.keys(this.definition.webhooks).length)) {\n        throw new Error(\n          'All paths in the API definition were removed. Did you supply the right path name to reduce by?',\n        );\n      }\n\n      delete this.definition.paths;\n    }\n  }\n\n  /**\n   * Prune back our `webhooks` object in the OpenAPI definition to only include webhooks that we\n   * want to preserve.\n   *\n   */\n  private reduceWebhooks(): void {\n    if (!isOpenAPI31(this.definition)) {\n      return;\n    } else if (!('webhooks' in this.definition) || !this.definition.webhooks) {\n      return;\n    }\n\n    const definition = this.definition satisfies OpenAPIV3_1.Document;\n\n    Object.keys(definition.webhooks || {}).forEach(webhookName => {\n      const nameLC = webhookName.toLowerCase();\n      if (this.hasWebhooksToReduceBy && !(nameLC in this.webhooksToReduceBy)) {\n        const retainedByRef = Array.from(this.retainWebhookMethods).some(\n          key => key.startsWith(`${nameLC}|`) || key === `${nameLC}|`,\n        );\n\n        if (!retainedByRef) {\n          delete definition.webhooks?.[webhookName];\n          return;\n        }\n      }\n\n      const webhook = definition.webhooks?.[webhookName];\n      if (!webhook || typeof webhook !== 'object') {\n        return;\n      }\n\n      /**\n       * If this webhook path item is a `$ref` then ignore it.\n       * @fixme we should better support reducing this.\n       */\n      if (isRef(webhook)) {\n        return;\n      }\n\n      Object.keys(webhook).forEach(method => {\n        const methodLC = method.toLowerCase();\n        if (method === 'parameters' || !supportedMethods.includes(methodLC as HttpMethods)) {\n          return;\n        }\n\n        const retainedByRef = this.retainWebhookMethods.has(`${nameLC}|${methodLC}`);\n        if (this.hasWebhooksToReduceBy && !retainedByRef) {\n          const methodFilter = this.webhooksToReduceBy[nameLC];\n          if (methodFilter !== '*' && Array.isArray(methodFilter) && !methodFilter.includes(methodLC)) {\n            /**\n             * If this webhook path item is a `$ref` then ignore and retain it.\n             * @fixme we should better support reducing this.\n             */\n            if (!definition.webhooks?.[webhookName] || isRef(definition.webhooks?.[webhookName])) {\n              return;\n            }\n\n            delete definition.webhooks?.[webhookName]?.[method as HttpMethods];\n          }\n        }\n      });\n\n      if (!Object.keys(definition.webhooks?.[webhookName] || {}).length) {\n        delete definition.webhooks?.[webhookName];\n      }\n    });\n\n    if (definition.webhooks && !Object.keys(definition.webhooks).length) {\n      delete definition.webhooks;\n    }\n  }\n}\n"]}