Skip to content

Adjust names of security schemes to match prefixed name#3861

Open
StratusFearMe21 wants to merge 8046 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 8046 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.

mrlubos and others added 30 commits April 14, 2026 15:47
…-options-headers-type

fix(client-fetch): narrow headers to Headers in ResolvedRequestOptions
…ve-preview-7.x

chore(deps): update dependency @typescript/native-preview to v7.0.0-dev.20260411.1
chore(deps): update dependency msw to v2.13.2
Paths starting with './' or absolute paths are now resolved from the current
working directory and made relative to the generated output directory, so the
resulting import path is correct regardless of where the user's runtime config
file lives.

Paths without a './' prefix (e.g. '../my-config', 'my-config', '@scope/pkg')
are kept as-is to preserve the existing convention of treating them as
relative to the generated output or as bare module specifiers.

Closes hey-api#3394
…plugins

feat: warn when duplicate plugins are specified
chore(deps): update dependency oxfmt to v0.44.0
…-action-6.x

chore(deps): update codecov/codecov-action action to v6
@mrlubos

mrlubos commented May 15, 2026

Copy link
Copy Markdown
Member

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

@mrlubos

mrlubos commented May 15, 2026

Copy link
Copy Markdown
Member

@pullfrog review the pull request

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

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
StratusFearMe21 and others added 3 commits May 15, 2026 01:30
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
@pullfrog

pullfrog Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

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

mrlubos commented May 15, 2026

Copy link
Copy Markdown
Member

@pullfrog respond to comments

@pullfrog

pullfrog Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

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𝕏

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

@StratusFearMe21 StratusFearMe21 May 15, 2026

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.

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.

8 participants