Skip to content

Add async support and unified type for patch.input and patch() shorthand#3411

Merged
mrlubos merged 6 commits intomainfrom
copilot/add-patch-spec-callback
Feb 17, 2026
Merged

Add async support and unified type for patch.input and patch() shorthand#3411
mrlubos merged 6 commits intomainfrom
copilot/add-patch-spec-callback

Conversation

Copy link
Contributor

Copilot AI commented Feb 17, 2026

The patch.input callback and shorthand patch() function used different types and did not support async operations, preventing users from performing asynchronous transformations during spec patching.

Changes

  • Created shared type: Introduced PatchInputFn type accepting both sync and async functions (void | Promise<void>), used by both patch.input and shorthand patch()
  • Made patchOpenApiSpec async: Function now awaits patch callbacks, enabling async operations like fetching external data or reading files
  • Updated call sites: Both openapi-ts and openapi-python createClient functions now await patchOpenApiSpec
  • Added async test coverage: Tests verify async operations complete correctly for both callback variants

Example

export default {
  parser: {
    patch: {
      input: async (spec) => {
        // Fetch parameter definitions from external source
        const params = await fetch('https://api.example.com/params').then(r => r.json());
        
        if (!spec.components) spec.components = {};
        spec.components.parameters = params;
      }
    }
  }
};

// Or using shorthand syntax
export default {
  parser: {
    patch: async (spec) => {
      const config = await loadConfig();
      spec.info.title = config.title;
    }
  }
};
Original prompt

This section details on the original issue you should resolve

<issue_title>Feature: Add patch.spec callback for full-spec transformations</issue_title>
<issue_description>### Description

Description

The parser.patch configuration provides targeted patching for schemas, operations, parameters, requestBodies, responses, meta, and version. These are excellent for surgical fixes to individual named resources.

However, there is currently no way to perform bulk or structural transformations on the raw spec before parsing -- such as adding new component definitions and injecting parameter references across many operations at once.

Use Case

We maintain a Vue.js frontend for OpenBMC that consumes the DMTF Redfish OpenAPI specification (~240k lines). The Redfish spec omits standard query parameters ($expand, $select, $filter, $top, $skip, etc.) from individual operations, even though they are part of the Redfish protocol for all GET endpoints.

We need to:

  1. Create new entries in components.parameters (the Redfish query parameter definitions don't exist in the upstream spec)
  2. Add $ref parameters to every GET operation under /redfish/v1/ (thousands of paths)

The existing patch options cannot achieve this because:

  • patch.parameters can only modify existing component parameters (lookup by key in spec.components.parameters), not create new ones
  • patch.operations requires enumerating every operation by exact key (e.g., 'GET /redfish/v1/Systems') -- impractical for thousands of paths with no wildcard/regex support
  • patch.meta only receives spec.info, not the full spec

We currently handle this with a separate preprocessing script, but would prefer to consolidate into the openapi-ts config using the parser.

Proposed Solution

Add an optional spec callback to the Patch type that receives the entire raw OpenAPI spec object, invoked before any other patch callbacks:

// In packages/shared/src/config/parser/patch.ts
export type Patch = {
  /**
   * Patch the raw OpenAPI spec object in place. Called before all other
   * patch callbacks. Useful for bulk/structural transformations such as
   * adding new component definitions or modifying many operations at once.
   */
  spec?: (
    spec: OpenApi.V2_0_X | OpenApi.V3_0_X | OpenApi.V3_1_X,
  ) => void;

  // ...existing fields
};
// In packages/shared/src/openApi/shared/utils/patch.ts, at the top of patchOpenApiSpec:
if (patchOptions.spec) {
  patchOptions.spec(spec);
}

Example usage

// openapi-ts.config.ts
import { defineConfig } from '@hey-api/openapi-ts';

const REDFISH_QUERY_PARAMS = [
  { key: '$expand', description: 'Expand related resources.' },
  { key: '$select', description: 'Select a subset of properties.' },
  { key: '$filter', description: 'Filter collection members.' },
  { key: '$top', description: 'Limit returned members.', schema: { type: 'integer', minimum: 0 } },
  { key: '$skip', description: 'Skip members before returning.', schema: { type: 'integer', minimum: 0 } },
];

export default defineConfig({
  input: 'src/api/schema/openapi_bundle.yaml',
  output: 'src/heyapi',
  plugins: ['@hey-api/client-axios', '@hey-api/typescript'],
  parser: {
    patch: {
      spec: (spec) => {
        // 1. Create component parameter definitions
        if (!spec.components) spec.components = {};
        if (!spec.components.parameters) spec.components.parameters = {};

        for (const param of REDFISH_QUERY_PARAMS) {
          spec.components.parameters[`Redfish_${param.key}`] = {
            in: 'query',
            name: param.key,
            required: false,
            schema: param.schema ?? { type: 'string' },
            description: param.description,
          };
        }

        // 2. Inject $ref into all Redfish GET operations
        for (const [path, pathItem] of Object.entries(spec.paths ?? {})) {
          if (!path.startsWith('/redfish/v1') || path.includes('$metadata')) continue;
          const getOp = pathItem?.get;
          if (!getOp) continue;

          if (!Array.isArray(getOp.parameters)) getOp.parameters = [];
          for (const param of REDFISH_QUERY_PARAMS) {
            getOp.parameters.push({
              $ref: `#/components/parameters/Redfish_${param.key}`,
            });
          }
        }
      },
    },
  },
});

Alternatives Considered

  1. Dynamic patch.operations map: Pre-read the spec file in the config to discover all GET paths and build a map with thousands of entries. Works but is roundabout -- the config reads the same file that input points to.

  2. Regex/glob support in patch.operations keys: Would help with the operation...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@vercel
Copy link

vercel bot commented Feb 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hey-api-docs Ready Ready Preview, Comment Feb 17, 2026 9:18pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Feb 17, 2026

🦋 Changeset detected

Latest commit: ba9f893

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hey-api/openapi-ts Patch
@hey-api/shared Patch
@hey-api/openapi-python Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Copilot AI changed the title [WIP] Add patch.spec callback for full-spec transformations Add patch.input and shorthand patch() for full-spec transformations Feb 17, 2026
Copilot AI requested a review from mrlubos February 17, 2026 19:54
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Copilot AI changed the title Add patch.input and shorthand patch() for full-spec transformations Add async support and unified type for patch.input and patch() shorthand Feb 17, 2026
@mrlubos mrlubos marked this pull request as ready for review February 17, 2026 20:08
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. feature 🚀 Feature request. labels Feb 17, 2026
@codecov
Copy link

codecov bot commented Feb 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 38.93%. Comparing base (5ae69ba) to head (ba9f893).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3411      +/-   ##
==========================================
+ Coverage   38.91%   38.93%   +0.01%     
==========================================
  Files         473      473              
  Lines       17287    17292       +5     
  Branches     5210     5212       +2     
==========================================
+ Hits         6727     6732       +5     
  Misses       8480     8480              
  Partials     2080     2080              
Flag Coverage Δ
unittests 38.93% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 17, 2026

Open in StackBlitz

@hey-api/codegen-core

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/codegen-core@3411

@hey-api/json-schema-ref-parser

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/json-schema-ref-parser@3411

@hey-api/nuxt

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/nuxt@3411

@hey-api/openapi-ts

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/openapi-ts@3411

@hey-api/shared

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/shared@3411

@hey-api/types

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/types@3411

@hey-api/vite-plugin

npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/vite-plugin@3411

commit: ba9f893

Add `patch.input` and shorthand `patch()` option for full specification transformations
@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Feb 17, 2026
@mrlubos mrlubos merged commit 37026a1 into main Feb 17, 2026
12 checks passed
@mrlubos mrlubos deleted the copilot/add-patch-spec-callback branch February 17, 2026 21:46
@hey-api hey-api bot mentioned this pull request Feb 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature 🚀 Feature request. lgtm This PR has been approved by a maintainer size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Add patch.spec callback for full-spec transformations

2 participants