From 0d185241051d4a5a2fdd9707b4a5fe11afb7abaa Mon Sep 17 00:00:00 2001 From: DenisCDev Date: Wed, 28 Oct 2026 10:45:41 -0300 Subject: [PATCH] fix: keep external schemas available after compiling a schema with the same $id When the schema being compiled had an $id that was also among the external schemas, responseSchemaCompiler removed it by replacing fjsOpts.schema. That fjsOpts object is bound once per factory call and shared by every later compilation, so the schema stayed missing afterwards and any later { $ref: '#' } failed with "Cannot resolve ref". Fastify reuses one serializer compiler for all routes of an encapsulated context that has no addSchema of its own. A route using fastify.getSchema('user') as a response schema therefore broke every later route in that plugin that referenced 'user#', depending only on route order. The duplicate $id is now removed from a per-call copy of the options. npm test: 11/11 passing, 100% coverage, tstyche 24/24. npm run lint: clean. The new test fails on main with "Schema with id "schema1" is not found". Signed-off-by: DenisCDev --- standalone.js | 2 +- test/duplicate-schema.test.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/standalone.js b/standalone.js index 7f0d40a..e593fb1 100644 --- a/standalone.js +++ b/standalone.js @@ -11,7 +11,7 @@ function SerializerSelector () { function responseSchemaCompiler (fjsOpts, { schema /* method, url, httpStatus */ }) { if (fjsOpts.schema && schema.$id && fjsOpts.schema[schema.$id]) { - fjsOpts.schema = { ...fjsOpts.schema } + fjsOpts = { ...fjsOpts, schema: { ...fjsOpts.schema } } delete fjsOpts.schema[schema.$id] } return fastJsonStringify(schema, fjsOpts) diff --git a/test/duplicate-schema.test.js b/test/duplicate-schema.test.js index 2acaa24..e1e4377 100644 --- a/test/duplicate-schema.test.js +++ b/test/duplicate-schema.test.js @@ -24,3 +24,22 @@ test('Use input schema duplicate in the externalSchemas', async t => { t.assert.ok(true) }) + +test('Keep the external schemas for later compilations', async t => { + t.plan(1) + const externalSchemas = { + schema1: { + $id: 'schema1', + type: 'object', + properties: { name: { type: 'string' } } + } + } + + const factory = FjsCompiler() + const compiler = factory(externalSchemas) + + compiler({ schema: externalSchemas.schema1 }) + const serialize = compiler({ schema: { $ref: 'schema1#' } }) + + t.assert.equal(serialize({ name: 'a', secret: 1 }), '{"name":"a"}') +})