Skip to content

Adjust names of security schemes to match prefixed name#3861

Open
StratusFearMe21 wants to merge 6 commits into
hey-api:mainfrom
StratusFearMe21:fix-merged-security-schemes
Open

Adjust names of security schemes to match prefixed name#3861
StratusFearMe21 wants to merge 6 commits into
hey-api:mainfrom
StratusFearMe21:fix-merged-security-schemes

Conversation

@StratusFearMe21
Copy link
Copy Markdown

This PR fixes how security schemes are referenced when there are multiple input files. Each security scheme referenced in each path should have the same prefixes that are applied to the securitySchemes component when multiple files are used.

@bolt-new-by-stackblitz
Copy link
Copy Markdown

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

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 9, 2026

⚠️ No Changeset found

Latest commit: 7579098

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel
Copy link
Copy Markdown

vercel Bot commented May 9, 2026

@StratusFearMe21 is attempting to deploy a commit to the Hey API Team on Vercel.

A member of the Team first needs to authorize it.

@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. bug 🔥 Broken or incorrect behavior. labels May 9, 2026
Copy link
Copy Markdown
Contributor

@pullfrog pullfrog Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new issues found.

TL;DR — When merging multiple OpenAPI specs, components.securitySchemes entries are renamed with a per-source prefix, but operation/path-level security requirements were not, leaving them pointing at the original (now non-existent) scheme names. This PR rewrites the keys of every security requirement object during cloneAndRewrite, so the references match the renamed components.

Key changes

  • Prefix security requirement keys to match renamed schemes — In cloneAndRewrite, security requirement objects get each key rewritten to ${opIdPrefix}_${key}, matching the prefix applied to components.securitySchemes entries.

Summary | 1 file | 1 commit | base: mainfix-merged-security-schemes

Before: Operation-level security: [{ apiKey: [] }] survived merging unchanged while the matching components.securitySchemes.apiKey was renamed to <prefix>_apiKey, leaving a dangling reference.
After: Both sides get the same <prefix>_apiKey name, so the merged document is internally consistent.

The new branch is placed before the operationId handler in the same per-key chain; opIdPrefix is the same per-source prefix used for the components rename at line 505, so the strings match exactly. Empty/opt-out shapes (security: [], security: [{}]) and AND-shaped multi-key requirements ({ a: [], b: [] }) are handled correctly by the plain Object.entries(s) walk.

packages/json-schema-ref-parser/src/index.ts

Pullfrog  | View workflow run | Using Claude Opus𝕏

@codecov
Copy link
Copy Markdown

codecov Bot commented May 9, 2026

Codecov Report

❌ Patch coverage is 93.75000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 37.84%. Comparing base (02280f8) to head (7579098).

Files with missing lines Patch % Lines
packages/json-schema-ref-parser/src/index.ts 93.75% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3861      +/-   ##
==========================================
+ Coverage   37.78%   37.84%   +0.05%     
==========================================
  Files         580      580              
  Lines       20777    20791      +14     
  Branches     6047     6045       -2     
==========================================
+ Hits         7851     7868      +17     
+ Misses      10522    10518       -4     
- Partials     2404     2405       +1     
Flag Coverage Δ
unittests 37.84% <93.75%> (+0.05%) ⬆️

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
Copy Markdown

pkg-pr-new Bot commented May 9, 2026

Open in StackBlitz

@hey-api/codegen-core

npm i https://pkg.pr.new/@hey-api/codegen-core@3861

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

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

@hey-api/nuxt

npm i https://pkg.pr.new/@hey-api/nuxt@3861

@hey-api/openapi-ts

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

@hey-api/shared

npm i https://pkg.pr.new/@hey-api/shared@3861

@hey-api/spec-types

npm i https://pkg.pr.new/@hey-api/spec-types@3861

@hey-api/types

npm i https://pkg.pr.new/@hey-api/types@3861

@hey-api/vite-plugin

npm i https://pkg.pr.new/@hey-api/vite-plugin@3861

commit: 7579098

@mrlubos
Copy link
Copy Markdown
Member

mrlubos commented May 11, 2026

@StratusFearMe21 can you add a test please?

@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels May 11, 2026
@StratusFearMe21
Copy link
Copy Markdown
Author

StratusFearMe21 commented May 11, 2026

Done, the test has been added 👌. In order to make the bug testable, I did have to modify how the $id field is handled in the getResolvedInput function so that an $id can be added to an OpenAPI schema without it's type changing to url.

@mrlubos
Copy link
Copy Markdown
Member

mrlubos commented May 15, 2026

@pullfrog does this help with #3817 or is that a separate issue?

@mrlubos
Copy link
Copy Markdown
Member

mrlubos commented May 15, 2026

@pullfrog review the pull request

Copy link
Copy Markdown
Contributor

@pullfrog pullfrog Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Core fix and regression test look right, but a debug console.dir was left in the new test and the getResolvedInput refactor silently drops a previously-supported case for inline objects that have neither openapi/swagger nor $id.

TL;DR — When multiple input files are merged via bundleMany, operation-level security references kept their original scheme names while the schemes themselves were renamed with a source prefix in components.securitySchemes, producing dangling references. This PR prefixes scheme names inside security arrays the same way securitySchemes are renamed, and tightens getResolvedInput to treat inline OpenAPI/Swagger objects as JSON (preserving any $id as the base path) so external $refs in inline specs can resolve.

Key changes

  • Prefix security scheme names on operations — In cloneAndRewrite, when a security array is encountered, each entry's keys are rewritten to ${opIdPrefix}_${schemeName} to match the renamed components.securitySchemes entries.
  • Inline OpenAPI/Swagger detection in getResolvedInput — Objects with openapi or swagger are now treated as inline JSON, with $id (if present) used as the base path for resolving external $refs.
  • Tolerate trailing slashes in baseNamebaseName('file:///spec1/') now returns spec1 instead of falling back to schema.
  • Regression test — New mergeMany test asserts that operation-level security entries from two specs end up referencing the prefixed scheme names.

Summary | 2 files | 2 commits | base: mainfix-merged-security-schemes


Prefixed security references on operations

Before: security: [{ bearerAuth: [] }] was emitted verbatim while the matching scheme was renamed to spec1_bearerAuth, leaving the operation pointing at a name that no longer exists.
After: the operation emits security: [{ spec1_bearerAuth: [] }], matching components.securitySchemes.

The rewrite happens inside cloneAndRewrite, so it inherits the same opIdPrefix already used for operationId renaming.

packages/json-schema-ref-parser/src/index.ts · packages/json-schema-ref-parser/src/__tests__/bundle.test.ts


Inline OpenAPI/Swagger spec handling

Before: an inline spec with $id (e.g. { ...spec, $id: 'file:///spec1.json' }) hit the $id branch and got path: 'file://:80', type: 'url', schema: undefined, then the bundler tried to fetch the synthetic URL.
After: the spec is recognized via openapi/swagger, kept as schema, and $id is preserved as path so external $refs resolve relative to it.

This is what makes the new test's bundleMany({ pathOrUrlOrSchemas: [{ ...spec1, $id: 'file:///spec1.json' }, ...] }) invocation actually work. One concern: the refactor removed the previous else branch that set schema = pathOrUrlOrSchema; type = 'json' for any object input. Inline objects that have neither openapi/swagger nor $id now fall through with schema: undefined and type: 'url', which used to be a supported case for plain JSON Schema inputs in this package. See the inline comment.

packages/json-schema-ref-parser/src/index.ts

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

Comment thread packages/json-schema-ref-parser/src/__tests__/bundle.test.ts Outdated
Comment thread packages/json-schema-ref-parser/src/index.ts
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
@pullfrog
Copy link
Copy Markdown
Contributor

pullfrog Bot commented May 15, 2026

Separate issue.

#3861 fixes a bug in @hey-api/json-schema-ref-parser: when multiple input files are bundled, each per-operation security reference now gets the same opIdPrefix applied that's already applied to the securitySchemes component keys, so the lookup resolves. Edit is in packages/json-schema-ref-parser/src/index.ts:467 (the new k === 'security' branch in the bundler walk).

#3817 is a runtime/SDK feature request: expose the original securitySchemes key (e.g. bearerAuth vs refreshAuth) on the Auth object passed to the auth callback so consumers can pick the right token when multiple schemes share the same resolved shape. That requires changes to the auth callback contract and the SDK client, not the bundler. Nothing in this PR touches that path.

Pullfrog  | View workflow run | via Pullfrog | Using Claude Opus𝕏

@mrlubos
Copy link
Copy Markdown
Member

mrlubos commented May 15, 2026

@pullfrog respond to comments

@pullfrog
Copy link
Copy Markdown
Contributor

pullfrog Bot commented May 15, 2026

Restored the missing fallback else { schema = pathOrUrlOrSchema; type = 'json'; } in getResolvedInput so inline JSON Schema objects without openapi/swagger or $id keep working, replied to the author's question on that thread, and pushed 7339164. All 14 bundle.test.ts and 3 index.test.ts cases pass locally.

Task list (5/5 completed)
  • Restore fallback else branch in getResolvedInput
  • Commit and push
  • Reply to author on index.ts thread
  • Resolve addressed threads
  • Report progress

Pullfrog  | View workflow run | via Pullfrog | Using Claude Opus𝕏

pullfrog Bot and others added 3 commits May 15, 2026 09:01
Without the else branch, an inline object input that has neither openapi/swagger
nor $id falls through with schema undefined and type 'url', which broke the
package's general-purpose JSON Schema bundling contract.
resolvedInput.type = 'file';
} else if (!resolvedInput.path && pathOrUrlOrSchema && typeof pathOrUrlOrSchema === 'object') {
if ('$id' in pathOrUrlOrSchema && pathOrUrlOrSchema.$id) {
if (
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@StratusFearMe21 why did you need to add this piece? I see the test fails without it, but I'm concerned about tying internals to OpenAPI/Swagger like this

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That piece is so that the regression test can set resolvedInput.path on each input schema without the type of the resolvedInput being set to 'url'. This is so that when the test calls bundleMany on the input, the prefixes on the output securitySchemas is predictable.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand, is this just to satisfy the test suite? Or is this needed for a real world usage?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it there to satisfy the test suite, but if you were using this package in the real world, passing multiple JSON objects to bundleMany would be broken without this change since both schemas would resolve to the same path . Either url.cwd() or 'schema'.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to handle that as a separate issue. I'm also concerned about this package, it feels like it's doing too much too poorly because I'm sure there are other similar edge cases we just didn't run into yet

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why $id on a schema object is essential to bundleMany

Line 45 is in getResolvedInput, which is called by parseMany (line 250) — the first step of bundleMany. The critical logic is at lines 44-59:

When a JSON object (not a file path or URL) is passed as an input, resolvedInput.path starts as '' (line 30). Without a $id, the path stays empty, and line 62-64 kicks in:

if (resolvedInput.type !== 'json') {
  resolvedInput.path = url.resolve(url.cwd(), resolvedInput.path);
}

This means a schema without $id falls through entirelytype stays 'url', no path is set, and it gets resolved against url.cwd() (the current working directory).

But when the schema does have $id (and is an OpenAPI/Swagger document), lines 51-52 set resolvedInput.path to the $id value and type to 'json'. This is critical for bundleMany because:

  1. parseMany (line 295) stores this path in this.schemaManySources[i]:

    this.schemaManySources.push(path && path.length ? path : url.cwd());
  2. mergeMany (line 489-490) uses that source path to compute the prefix used for namespacing all components:

    const sourcePath = this.schemaManySources[i] || `multi://input/${i + 1}`;
    const prefix = baseName(sourcePath);
  3. That prefix is then used to rename all components (schemas, parameters, securitySchemes, etc.) to avoid collisions between the multiple inputs:

    const newName = `${prefix}_${name}`;  // e.g., "petstore_Pet" instead of "Pet"
  4. It's also used to rewrite $ref pointers (line 512), prefix operation IDs (line 479), prefix tag names (line 522), and resolve relative external references (line 463).

Without $id, an inline schema object would get path = '', which would make baseName return a generic name (or fall back to the CWD-derived path), causing unpredictable or colliding prefixes when multiple JSON objects are bundled together. The $id provides a stable, unique identity for each inline schema, enabling the entire namespacing and reference-rewriting machinery of bundleMany to work correctly.')

const withoutHash = p.split('#')[0]!;
const parts = withoutHash.split('/');
const withoutTrailingSlash = withoutHash.replace(/\/+$/, '');
const parts = withoutTrailingSlash.split('/');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@StratusFearMe21 why is this change needed? Reverting it doesn't fail tests

Copy link
Copy Markdown
Author

@StratusFearMe21 StratusFearMe21 May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I didn't mean to keep that in this PR, that's my mistake.

It fixes another bug where if the path on the resolvedInput is set to url.cwd(), because that function always puts a trailing slash at the end of the path, splitting the path /foo/bar/baz/ for example would return ['foo', 'bar', 'baz', '']. Because the last element in parts is empty, baseName would just return 'schema' instead of the last component of the path.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to open a separate pull request for that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug 🔥 Broken or incorrect behavior. size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants