diff --git a/src/router.test.ts b/src/router.test.ts index 812f9bf..a866c0d 100644 --- a/src/router.test.ts +++ b/src/router.test.ts @@ -390,6 +390,76 @@ describe('introspection', () => { expect(formattedDeclaration).toMatchSnapshot(); }); + + test('when using the same schema reference multiple times, it is always resolved inline', async () => { + const reusedSchema = z.object({ id: z.string() }); + + const router = OneSchemaRouter.create({ + using: new Router(), + introspection: { + route: '/private/introspection', + serviceVersion: '123', + }, + }).declare({ + route: 'POST /items', + name: 'createItem', + request: z.object({}), + response: z.object({ + resProp1: reusedSchema, + resProp2: reusedSchema, + }), + }); + + const { client } = serve(router); + + const introspectionResult = await client.get('/private/introspection'); + + console.log(JSON.stringify(introspectionResult.data, null, 2)); + + expect(introspectionResult.data.schema).toStrictEqual({ + Endpoints: { + 'POST /items': { + Name: 'createItem', + Request: { + type: 'object', + properties: {}, + additionalProperties: false, + $schema: 'http://json-schema.org/draft-07/schema#', + }, + Response: { + type: 'object', + properties: { + // resProp1 and resProp2 should be inlined, even though they have identical + // schemas defined using the same reference. + resProp1: { + type: 'object', + properties: { + id: { + type: 'string', + }, + }, + required: ['id'], + additionalProperties: false, + }, + resProp2: { + type: 'object', + properties: { + id: { + type: 'string', + }, + }, + required: ['id'], + additionalProperties: false, + }, + }, + required: ['resProp1', 'resProp2'], + additionalProperties: false, + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + }, + }); + }); }); test('declaring multiple routes with the same name results in an error', () => { diff --git a/src/router.ts b/src/router.ts index 5fe8bf9..60437e9 100644 --- a/src/router.ts +++ b/src/router.ts @@ -127,9 +127,13 @@ const convertRouterSchemaToJSONSchemaStyle = ( // The JSONSchema types are very slightly different between packages. We just // trust that the interop will work fine, and use "as any" here. // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - Request: zodToJsonSchema(definition.request) as any, + Request: zodToJsonSchema(definition.request, { + $refStrategy: 'none', + }) as any, // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - Response: zodToJsonSchema(definition.response) as any, + Response: zodToJsonSchema(definition.response, { + $refStrategy: 'none', + }) as any, }; }