diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dce157f0..fccd0430 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,17 @@ jobs: - name: Install deps run: pnpm i + - name: Create .npmrc for GitHub Packages + run: | + cat < ~/.npmrc + @nhsdigital:registry=https://npm.pkg.github.com + //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} + always-auth=true + registry=https://registry.npmjs.org/ + EOF + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Release Pull Request or Publish to npm uses: changesets/action@master with: diff --git a/.npmrc b/.npmrc index c435a1b4..319e41e6 100644 --- a/.npmrc +++ b/.npmrc @@ -1,5 +1 @@ strict-peer-dependencies=false - -@nhsdigital:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} -always-auth=true \ No newline at end of file diff --git a/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.tsx b/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.tsx index 833e6583..1e9226da 100644 --- a/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.tsx +++ b/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useRef, useMemo } from 'react'; +import $RefParser from '@apidevtools/json-schema-ref-parser'; interface JSONSchemaViewerProps { schema: any; @@ -100,107 +101,10 @@ function mergeAllOfSchemas(schemaWithProcessor: any): any { return mergedSchema; } -function processSchema(schema: any, rootSchema?: any): any { +async function processSchema(schema: any, rootSchema?: any): Promise { if (!schema) return schema; - - const root = rootSchema || schema; - - // Handle $ref - if (schema.$ref) { - const refPath = schema.$ref; - let resolvedSchema = null; - let defName = ''; - - // Try draft-7 style first: #/definitions/ - if (refPath.startsWith('#/definitions/')) { - defName = refPath.replace('#/definitions/', ''); - if (root.definitions && root.definitions[defName]) { - resolvedSchema = root.definitions[defName]; - } - } - // Try 2020-12 style: #/$defs/ - else if (refPath.startsWith('#/$defs/')) { - defName = refPath.replace('#/$defs/', ''); - if (root.$defs && root.$defs[defName]) { - resolvedSchema = root.$defs[defName]; - } - } - // Try other common patterns - else if (refPath.startsWith('#/components/schemas/')) { - defName = refPath.replace('#/components/schemas/', ''); - if (root.components && root.components.schemas && root.components.schemas[defName]) { - resolvedSchema = root.components.schemas[defName]; - } - } - - if (resolvedSchema) { - const processedSchema = processSchema(resolvedSchema, root); - return { - ...processedSchema, - _refPath: refPath, - _refName: defName, - _originalRef: schema.$ref, - }; - } - - return { - type: 'string', - description: `Reference to ${refPath} (definition not found in root schema)`, - title: defName || refPath.split('/').pop(), - _refPath: refPath, - _refName: defName, - _refNotFound: true, - }; - } - - if (schema.allOf) { - return mergeAllOfSchemas({ ...schema, processSchema: (s: any) => processSchema(s, root) }); - } - - if (schema.oneOf) { - const processedVariants = schema.oneOf.map((variant: any) => { - const processedVariant = processSchema(variant, root); - return { - title: processedVariant.title || variant.title || 'Unnamed Variant', - required: processedVariant.required || variant.required || [], - properties: processedVariant.properties || {}, - ...processedVariant, - }; - }); - - const allProperties: Record = {}; - processedVariants.forEach((variant: any) => { - if (variant.properties) { - Object.assign(allProperties, variant.properties); - } - }); - - return { - ...schema, - type: schema.type || 'object', - properties: { - ...(schema.properties || {}), - ...allProperties, - }, - variants: processedVariants, - }; - } - - // Process nested schemas in properties - if (schema.properties) { - const processedProperties: Record = {}; - Object.entries(schema.properties).forEach(([key, prop]: [string, any]) => { - processedProperties[key] = processSchema(prop, root); - }); - schema = { ...schema, properties: processedProperties }; - } - - // Process array items - if (schema.type === 'array' && schema.items) { - schema = { ...schema, items: processSchema(schema.items, root) }; - } - - return schema; + let dereferencedSchema = await $RefParser.dereference(schema); + return dereferencedSchema; } // SchemaProperty component diff --git a/package.json b/package.json index c4e92f91..31f142a1 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@ai-sdk/google": "^2.0.17", "@ai-sdk/openai": "^2.0.42", "@ai-sdk/react": "^2.0.60", + "@apidevtools/json-schema-ref-parser": "^15.1.3", "@astrojs/markdown-remark": "^6.3.9", "@astrojs/mdx": "^4.3.12", "@astrojs/node": "^9.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e9b9b77..da068187 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@ai-sdk/react': specifier: ^2.0.60 version: 2.0.62(react@18.3.1)(zod@3.25.76) + '@apidevtools/json-schema-ref-parser': + specifier: ^15.1.3 + version: 15.1.3(@types/json-schema@7.0.15) '@astrojs/markdown-remark': specifier: ^6.3.9 version: 6.3.9 @@ -387,6 +390,12 @@ packages: '@antfu/utils@9.3.0': resolution: {integrity: sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==} + '@apidevtools/json-schema-ref-parser@15.1.3': + resolution: {integrity: sha512-XvEitlOaU8S+hOrMPuGyCjp6vC51K+syUN4HHrSUdSDLLWRWQJYjInU6xlSoRGCVBCfcoHxbRm+yiaYq2yFR5w==} + engines: {node: '>=20'} + peerDependencies: + '@types/json-schema': ^7.0.15 + '@asamuzakjp/css-color@4.0.5': resolution: {integrity: sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==} @@ -7729,6 +7738,11 @@ snapshots: '@antfu/utils@9.3.0': {} + '@apidevtools/json-schema-ref-parser@15.1.3(@types/json-schema@7.0.15)': + dependencies: + '@types/json-schema': 7.0.15 + js-yaml: 4.1.1 + '@asamuzakjp/css-color@4.0.5': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -13169,7 +13183,7 @@ snapshots: '@langchain/openai': 0.6.14(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3) '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))) js-tiktoken: 1.0.21 - js-yaml: 4.1.0 + js-yaml: 4.1.1 jsonpointer: 5.0.1 langsmith: 0.3.73(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.18.3)(zod@3.25.76)) openapi-types: 12.1.3