Skip to content

Commit 27ecfef

Browse files
fix: toJsonSchema definition key with / or ~ not encoded in $ref (#1482)
* fix: `toJsonSchema` definition key with `/` or `~` not encoded as JSON Pointer in `$ref` Fixes #1481 * Update changelog with fix for $ref generation --------- Co-authored-by: Fabian Hiller <hillerfabian11@gmail.com>
1 parent 1545619 commit 27ecfef

4 files changed

Lines changed: 59 additions & 2 deletions

File tree

packages/to-json-schema/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notable changes to the library will be documented in this file.
44

5+
## vX.X.X (Month DD, YYYY)
6+
7+
- Fix `$ref` generation to encode definition keys containing `/` or `~` as JSON Pointer tokens (pull request #1482)
8+
59
## v1.7.0 (May 05, 2026)
610

711
- Change build target to ES2020 so distributed output stays compatible with environments that lack support for newer syntax (pull request #1455)

packages/to-json-schema/src/converters/convertSchema/convertSchema.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ describe('convertSchema', () => {
2424
});
2525
});
2626

27+
test('should encode definition key as JSON Pointer in reference', () => {
28+
const schema = v.string();
29+
expect(
30+
convertSchema(
31+
{},
32+
schema,
33+
undefined,
34+
createContext({
35+
definitions: { 'Shared/User~': { type: 'string' } },
36+
referenceMap: new Map().set(schema, 'Shared/User~'),
37+
})
38+
)
39+
).toStrictEqual({
40+
$ref: '#/$defs/Shared~1User~0',
41+
});
42+
});
43+
2744
test('should skip definition if specified', () => {
2845
const stringSchema = v.string();
2946
expect(

packages/to-json-schema/src/converters/convertSchema/convertSchema.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ function flattenPipe(pipe: Pipe): Pipe {
123123
);
124124
}
125125

126+
/**
127+
* Returns the JSON Pointer reference for a definition key.
128+
*
129+
* @param referenceId The unescaped definition key.
130+
*
131+
* @returns The encoded JSON Pointer fragment.
132+
*/
133+
function getDefinitionRef(referenceId: string): string {
134+
return `#/$defs/${referenceId.replaceAll('~', '~0').replaceAll('/', '~1')}`;
135+
}
136+
126137
// Create global reference count
127138
let refCount = 0;
128139

@@ -148,7 +159,7 @@ export function convertSchema(
148159
// If schema is in reference map use reference and skip conversion
149160
const referenceId = context.referenceMap.get(valibotSchema);
150161
if (referenceId) {
151-
jsonSchema.$ref = `#/$defs/${referenceId}`;
162+
jsonSchema.$ref = getDefinitionRef(referenceId);
152163
if (config?.overrideRef) {
153164
const refOverride = config.overrideRef({
154165
...context,
@@ -631,7 +642,7 @@ export function convertSchema(
631642
}
632643

633644
// Add reference to JSON Schema object
634-
jsonSchema.$ref = `#/$defs/${referenceId}`;
645+
jsonSchema.$ref = getDefinitionRef(referenceId);
635646

636647
// Override reference, if necessary
637648
if (config?.overrideRef) {

packages/to-json-schema/src/functions/toJsonSchema/toJsonSchema.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,31 @@ describe('toJsonSchema', () => {
139139
},
140140
});
141141
});
142+
143+
test('for definitions with JSON Pointer special characters', () => {
144+
const sharedSchema = v.object({ name: v.string() });
145+
expect(
146+
toJsonSchema(v.object({ user: sharedSchema }), {
147+
definitions: { 'Shared/User~': sharedSchema },
148+
})
149+
).toStrictEqual({
150+
$schema: 'http://json-schema.org/draft-07/schema#',
151+
type: 'object',
152+
properties: {
153+
user: { $ref: '#/$defs/Shared~1User~0' },
154+
},
155+
required: ['user'],
156+
$defs: {
157+
'Shared/User~': {
158+
type: 'object',
159+
properties: {
160+
name: { type: 'string' },
161+
},
162+
required: ['name'],
163+
},
164+
},
165+
});
166+
});
142167
});
143168

144169
describe('should throw error', () => {

0 commit comments

Comments
 (0)