commits
fix: surface actual error message when spec fetch fails
Cover the fallback resolution and crawl path rebase fixes in
inventory$Ref with five scenarios: bare wrapper chain, extended
wrapper chain, direct reference (no wrapper), multiple siblings
through an extended wrapper, and collision handling when two
external files expose same-named sibling schemas.
Signed-off-by: Jason Westover <jwestover@nvidia.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Use unprefixed schema names from external files unless conflicts exist
Ran `pnpm test:update` to regenerate all test snapshots after implementing conditional schema name prefixing. External schemas now use unprefixed names (e.g., "Foo") instead of prefixed names (e.g., "external_Foo") when there are no naming conflicts.
This affects all OpenAPI test snapshots across 2.0.x, 3.0.x, and 3.1.x versions.
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
When `$RefParser.bundle()` processes external files that use wrapper/redirect patterns (common in DMTF Redfish and similar large-scale OpenAPI specs), sibling schemas within versioned files are not hoisted into the root spec. This produces dangling `$ref` pointers and "Skipping unresolvable $ref" warnings.
**Affects:** 35 schemas lost when bundling the [DMTF Redfish OpenAPI spec](https://github.com/DMTF/Redfish-Publications/blob/main/openapi/openapi.yaml).
The bundler's crawl path retains the **wrapper file** URL as context when traversing the resolved schema's properties. When a local `$ref` like `#/components/schemas/SiblingSchema` is encountered inside the resolved schema, `url.resolve()` resolves it against the wrapper file — which doesn't contain the sibling. The sibling only exists in the versioned file.
**Example chain:**
1. `openapi.yaml` → `Message.v1_2_1.yaml` (HTTP)
2. `Message.v1_2_1.ya→ `ResolutionStep.yaml` (wrapper)
3. `ResolutionStep.yaml` → `ResolutionStep.v1_0_1.yaml` (versioned)
4. `ResolutionStep.v1_0_1.yaml` has `ResolutionStep_v1_0_1_ResolutionStep` with local `$ref: '#/components/schemas/ResolutionStep_v1_0_1_ResolutionType'`
`bundle()` hoists `ResolutionStep_v1_0_1_ResolutionStep` but fails to resolve its sibling `ResolutionStep_v1_0_1_ResolutionType` because it looks in `ResolutionStep.yaml` (the wrapper) instead of `ResolutionStep.v1_0_1.yaml` (the versioned file).
When `_resolve()` fails with `MissingPointerError`, try resolving the same hash fragment against all other files in the `$refs` registry. This handles the case where a local `$ref` targets a sibling schema that exists in a different file than the one retained in the crawl path.
```typescript
// Before: fail immediately
catch (error) {
if (error instanceof MissingPointerError) {
console.warn(`Skipping unresolvable $ref: ${$refPath}`);
return;
}
}
// After: try other files before giving atch (error) {
if (error instanceof MissingPointerError) {
const hash = url.getHash($refPath);
if (hash) {
const baseFile = url.stripHash($refPath);
for (const filePath of Object.keys($refs._$refs)) {
if (filePath === baseFile) continue;
try {
pointer = $refs._resolve(filePath + hash, pathFromRoot, options);
if (pointer) break;
} catch { /* try next file */ }
}
}
if (!pointer) {
console.warn(`Skipping unresolvable $ref: ${$refPath}`);
return;
}
}
}
```
Additionally, when `inventory$Ref` resolves a `$ref` that chains to a different file, the recursive crawl's path is rebased to the resolved file URL so that subsequent local `$ref`s resolve against the correct file.
Tested against the full DMTF Redfish OpenAPI specification (~2600 schemas, ~2670 paths, hundreds of external HTTP files):
- **Before:** 37 "Skipping unresolvable $ref" warnings, 35 schemas lost
- **After:** 0 warnings, all schemas correctly hoisted
- `packages/json-schema-ref-parser/src/bundle.ts` — two changes in `inventory$Ref`:
1. Fallback resolution against all `$refs` files when `MissingPointerError` occurs
2. Crawl path rebase when resolution chains to a different file
Fixes #3412
Signed-off-by: Jason Westover <jwestover@nvidia.com>
- Update type definition to support both Record and bulk callback function
- Implement bulk callback iteration for OpenAPI v2 and v3
- Add comprehensive tests for bulk callback functionality
- Support async operations for both patterns
- All tests passing (61/61)
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
The fixDanglingRefs() function was handling invalid OpenAPI/JSON Schema usage where external files use local-looking refs (e.g., #/components/schemas/SchemaB) to reference schemas in other external files.
Per JSON Schema and OpenAPI specifications, a ref starting with # is a local reference to the current document only. Cross-file references must use proper relative or absolute URIs.
Removed:
- fixDanglingRefs() function from bundle.ts
- Cross-file reference test case
- Test spec files: cross-file-ref-*.json
- Associated snapshot file
Invalid specs should fail with clear errors rather than being silently "fixed". Users with such specs should be asked to use correct syntax like "file2.json#/components/schemas/SchemaB".
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>
Only add file prefix to external schema names when there's a naming conflict. This results in cleaner schema names like "User" instead of "file1_User" when there's no ambiguity.
Changes:
- Modified remap() to try unprefixed names first
- Only add file prefix when the unprefixed name is already taken
- Updated fixDanglingRefs() to handle both prefixed and unprefixed names
- Updated test snapshots to reflect new cleaner naming
- Verified conflict resolution still works correctly
Example:
- No conflict: "SchemaA" (was "file1_SchemaA")
- With conflict: First gets "User", second gets "file2_User"
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Created expectBundledSchemaToMatchSnapshot() helper to eliminate repeated code in all 5 bundle tests. The helper handles:
- Creating output and snapshot paths
- Ensuring directory exists
- Writing bundled result to temp file
- Comparing with snapshot
This reduces duplication and makes tests more maintainable.
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Following the pattern from Zod v4 tests, convert bundle tests from value comparisons to snapshot testing for better visibility of bundled specs.
Changes:
- Added __snapshots__ directory with JSON snapshot files for each test
- Updated bundle.test.ts to use toMatchFileSnapshot matcher
- All 5 tests now write bundled output to temp directory and compare with snapshots
- Snapshots provide complete visibility of bundled spec structure
- Removed verbose assertion code, replaced with simple snapshot comparisons
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>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
When an external file references a schema from another external file using a local-looking ref (e.g., #/components/schemas/SchemaB), the bundle() function would:
1. Try to resolve it relative to the current file
2. Fail and log "Skipping unresolvable $ref"
3. Leave the ref as-is in the bundled output
4. Create a dangling reference since SchemaB is hoisted with a different name
Added fixDanglingRefs() post-processing step that:
- Identifies dangling internal $refs
- Matches them to hoisted schemas from other files
- Rewrites them to the correct hoisted names
Test case added: cross-file reference scenario
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
feat: add bulk callback and async support for patch.schemas
- Created test specs with YAML files mimicking Redfish structure
- Added comprehensive test for versioned schema names
- Verified sibling schemas are hoisted correctly with YAML
- All 4 bundle tests pass
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
- Created test specs with external files containing multiple schemas
- Added test case for sibling schema hoisting in bundle.test.ts
- All tests pass - sibling schemas are correctly hoisted
- Tested with local files, nested references, and HTTP URLs
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>
- Updated type definition to allow Promise<void> return type for both Record and bulk callback
- Updated implementation to await all schema callback invocations (v2 and v3)
- Added comprehensive tests for async bulk callbacks and async Record-based callbacks
- Verified with manual CLI test that async operations work correctly
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>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
- Updated type definition to allow function signature in addition to Record
- Implemented bulk callback for OpenAPI v3 schemas
- Implemented bulk callback for OpenAPI v2 (swagger) definitions
- Added comprehensive tests for both v2 and v3 specs
- Tests cover basic callbacks, mutations, version extraction, and invalid schema handling
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Add `patch.input` and shorthand `patch()` option for full specification transformations
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>
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Cover the fallback resolution and crawl path rebase fixes in
inventory$Ref with five scenarios: bare wrapper chain, extended
wrapper chain, direct reference (no wrapper), multiple siblings
through an extended wrapper, and collision handling when two
external files expose same-named sibling schemas.
Signed-off-by: Jason Westover <jwestover@nvidia.com>
Ran `pnpm test:update` to regenerate all test snapshots after implementing conditional schema name prefixing. External schemas now use unprefixed names (e.g., "Foo") instead of prefixed names (e.g., "external_Foo") when there are no naming conflicts.
This affects all OpenAPI test snapshots across 2.0.x, 3.0.x, and 3.1.x versions.
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
When `$RefParser.bundle()` processes external files that use wrapper/redirect patterns (common in DMTF Redfish and similar large-scale OpenAPI specs), sibling schemas within versioned files are not hoisted into the root spec. This produces dangling `$ref` pointers and "Skipping unresolvable $ref" warnings.
**Affects:** 35 schemas lost when bundling the [DMTF Redfish OpenAPI spec](https://github.com/DMTF/Redfish-Publications/blob/main/openapi/openapi.yaml).
The bundler's crawl path retains the **wrapper file** URL as context when traversing the resolved schema's properties. When a local `$ref` like `#/components/schemas/SiblingSchema` is encountered inside the resolved schema, `url.resolve()` resolves it against the wrapper file — which doesn't contain the sibling. The sibling only exists in the versioned file.
**Example chain:**
1. `openapi.yaml` → `Message.v1_2_1.yaml` (HTTP)
2. `Message.v1_2_1.ya→ `ResolutionStep.yaml` (wrapper)
3. `ResolutionStep.yaml` → `ResolutionStep.v1_0_1.yaml` (versioned)
4. `ResolutionStep.v1_0_1.yaml` has `ResolutionStep_v1_0_1_ResolutionStep` with local `$ref: '#/components/schemas/ResolutionStep_v1_0_1_ResolutionType'`
`bundle()` hoists `ResolutionStep_v1_0_1_ResolutionStep` but fails to resolve its sibling `ResolutionStep_v1_0_1_ResolutionType` because it looks in `ResolutionStep.yaml` (the wrapper) instead of `ResolutionStep.v1_0_1.yaml` (the versioned file).
When `_resolve()` fails with `MissingPointerError`, try resolving the same hash fragment against all other files in the `$refs` registry. This handles the case where a local `$ref` targets a sibling schema that exists in a different file than the one retained in the crawl path.
```typescript
// Before: fail immediately
catch (error) {
if (error instanceof MissingPointerError) {
console.warn(`Skipping unresolvable $ref: ${$refPath}`);
return;
}
}
// After: try other files before giving atch (error) {
if (error instanceof MissingPointerError) {
const hash = url.getHash($refPath);
if (hash) {
const baseFile = url.stripHash($refPath);
for (const filePath of Object.keys($refs._$refs)) {
if (filePath === baseFile) continue;
try {
pointer = $refs._resolve(filePath + hash, pathFromRoot, options);
if (pointer) break;
} catch { /* try next file */ }
}
}
if (!pointer) {
console.warn(`Skipping unresolvable $ref: ${$refPath}`);
return;
}
}
}
```
Additionally, when `inventory$Ref` resolves a `$ref` that chains to a different file, the recursive crawl's path is rebased to the resolved file URL so that subsequent local `$ref`s resolve against the correct file.
Tested against the full DMTF Redfish OpenAPI specification (~2600 schemas, ~2670 paths, hundreds of external HTTP files):
- **Before:** 37 "Skipping unresolvable $ref" warnings, 35 schemas lost
- **After:** 0 warnings, all schemas correctly hoisted
- `packages/json-schema-ref-parser/src/bundle.ts` — two changes in `inventory$Ref`:
1. Fallback resolution against all `$refs` files when `MissingPointerError` occurs
2. Crawl path rebase when resolution chains to a different file
Fixes #3412
Signed-off-by: Jason Westover <jwestover@nvidia.com>
- Update type definition to support both Record and bulk callback function
- Implement bulk callback iteration for OpenAPI v2 and v3
- Add comprehensive tests for bulk callback functionality
- Support async operations for both patterns
- All tests passing (61/61)
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
The fixDanglingRefs() function was handling invalid OpenAPI/JSON Schema usage where external files use local-looking refs (e.g., #/components/schemas/SchemaB) to reference schemas in other external files.
Per JSON Schema and OpenAPI specifications, a ref starting with # is a local reference to the current document only. Cross-file references must use proper relative or absolute URIs.
Removed:
- fixDanglingRefs() function from bundle.ts
- Cross-file reference test case
- Test spec files: cross-file-ref-*.json
- Associated snapshot file
Invalid specs should fail with clear errors rather than being silently "fixed". Users with such specs should be asked to use correct syntax like "file2.json#/components/schemas/SchemaB".
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Only add file prefix to external schema names when there's a naming conflict. This results in cleaner schema names like "User" instead of "file1_User" when there's no ambiguity.
Changes:
- Modified remap() to try unprefixed names first
- Only add file prefix when the unprefixed name is already taken
- Updated fixDanglingRefs() to handle both prefixed and unprefixed names
- Updated test snapshots to reflect new cleaner naming
- Verified conflict resolution still works correctly
Example:
- No conflict: "SchemaA" (was "file1_SchemaA")
- With conflict: First gets "User", second gets "file2_User"
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Created expectBundledSchemaToMatchSnapshot() helper to eliminate repeated code in all 5 bundle tests. The helper handles:
- Creating output and snapshot paths
- Ensuring directory exists
- Writing bundled result to temp file
- Comparing with snapshot
This reduces duplication and makes tests more maintainable.
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
Following the pattern from Zod v4 tests, convert bundle tests from value comparisons to snapshot testing for better visibility of bundled specs.
Changes:
- Added __snapshots__ directory with JSON snapshot files for each test
- Updated bundle.test.ts to use toMatchFileSnapshot matcher
- All 5 tests now write bundled output to temp directory and compare with snapshots
- Snapshots provide complete visibility of bundled spec structure
- Removed verbose assertion code, replaced with simple snapshot comparisons
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
When an external file references a schema from another external file using a local-looking ref (e.g., #/components/schemas/SchemaB), the bundle() function would:
1. Try to resolve it relative to the current file
2. Fail and log "Skipping unresolvable $ref"
3. Leave the ref as-is in the bundled output
4. Create a dangling reference since SchemaB is hoisted with a different name
Added fixDanglingRefs() post-processing step that:
- Identifies dangling internal $refs
- Matches them to hoisted schemas from other files
- Rewrites them to the correct hoisted names
Test case added: cross-file reference scenario
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
- Created test specs with external files containing multiple schemas
- Added test case for sibling schema hoisting in bundle.test.ts
- All tests pass - sibling schemas are correctly hoisted
- Tested with local files, nested references, and HTTP URLs
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
- Updated type definition to allow Promise<void> return type for both Record and bulk callback
- Updated implementation to await all schema callback invocations (v2 and v3)
- Added comprehensive tests for async bulk callbacks and async Record-based callbacks
- Verified with manual CLI test that async operations work correctly
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>
- Updated type definition to allow function signature in addition to Record
- Implemented bulk callback for OpenAPI v3 schemas
- Implemented bulk callback for OpenAPI v2 (swagger) definitions
- Added comprehensive tests for both v2 and v3 specs
- Tests cover basic callbacks, mutations, version extraction, and invalid schema handling
Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>