Aktueller Stand

This commit is contained in:
2026-01-22 19:05:45 +01:00
parent 85dee61a4d
commit e280e4eadb
1967 changed files with 397327 additions and 74093 deletions

View File

@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10

View File

@@ -0,0 +1,25 @@
name: CI
on:
push:
branches:
- main
- next
- 'v*'
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
test:
permissions:
contents: write
pull-requests: write
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v5
with:
license-check: true
lint: true

View File

@@ -1,32 +0,0 @@
name: Run Tests
on: [push, pull_request]
permissions:
contents: read
jobs:
test:
name: Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [18.x, 20.x]
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install
run: |
npm install
- name: Test
run: |
npm run test

View File

@@ -1,5 +1,9 @@
# json-schema-ref-resolver
[![CI](https://github.com/fastify/json-schema-ref-resolver/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/json-schema-ref-resolver/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/json-schema-ref-resolver)](https://www.npmjs.com/package/json-schema-ref-resolver)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)
__json-schema-ref-resolver__ is a javascript library that resolves references in [JSON schemas](https://json-schema.org/draft/2020-12/json-schema-core#name-introduction).
- [Installation](#installation)
@@ -255,7 +259,7 @@ assert.deepStrictEqual(dependencies, { targetSchema1, targetSchema2 })
#### derefSchema(schemaId)
Dereferences all references in the schema. All dependency also will be dereferenced. If schema with the specified id is not found, an error will be thrown.
Dereferences all references in the schema. All dependencies will also be dereferenced. If schema with the specified id is not found, an error will be thrown.
- `schemaId` __\<string\>__ - schema id of the schema to dereference.
@@ -361,7 +365,7 @@ assert.deepStrictEqual(derefSourceSchema, {
#### Caveats
- If a reference schema and a source schema have a key with the same name and different values, an error will be throwing during a call to `derefSchema` or `getDerefSchema`.
- If a reference schema and a source schema have a key with the same name and different values, an error will be thrown during a call to `derefSchema` or `getDerefSchema`.
_Example:_
@@ -396,3 +400,7 @@ refResolver.addSchema(sourceSchema)
refResolver.derefSchema('sourceSchema') // Throws an error
```
## License
Licensed under [MIT](./LICENSE).

View File

@@ -0,0 +1,6 @@
'use strict'
module.exports = require('neostandard')({
ignores: require('neostandard').resolveIgnoresFromGitignore(),
ts: true
})

View File

@@ -1,6 +1,6 @@
'use strict'
const deepEqual = require('fast-deep-equal')
const { dequal: deepEqual } = require('dequal')
const jsonSchemaRefSymbol = Symbol.for('json-schema-ref')
@@ -19,15 +19,41 @@ class RefResolver {
this.#cloneSchemaWithoutRefs = opts.cloneSchemaWithoutRefs ?? false
}
addSchema (schema, schemaId) {
if (schema.$id !== undefined && schema.$id.charAt(0) !== '#') {
// Schema has an $id that is not an anchor
schemaId = schema.$id
} else {
// Schema has no $id or $id is an anchor
this.#insertSchemaBySchemaId(schema, schemaId)
addSchema (schema, rootSchemaId, isRootSchema = true) {
if (isRootSchema) {
if (schema.$id !== undefined && schema.$id.charAt(0) !== '#') {
// Schema has an $id that is not an anchor
rootSchemaId = schema.$id
} else {
// Schema has no $id or $id is an anchor
this.#insertSchemaBySchemaId(schema, rootSchemaId)
}
}
const schemaId = schema.$id
if (schemaId !== undefined && typeof schemaId === 'string') {
if (schemaId.charAt(0) === '#') {
this.#insertSchemaByAnchor(schema, rootSchemaId, schemaId)
} else {
this.#insertSchemaBySchemaId(schema, schemaId)
rootSchemaId = schemaId
}
}
const ref = schema.$ref
if (ref !== undefined && typeof ref === 'string') {
const { refSchemaId, refJsonPointer } = this.#parseSchemaRef(ref, rootSchemaId)
this.#schemas[rootSchemaId].refs.push({
schemaId: refSchemaId,
jsonPointer: refJsonPointer
})
}
for (const key in schema) {
if (typeof schema[key] === 'object' && schema[key] !== null) {
this.addSchema(schema[key], rootSchemaId, false)
}
}
this.#addSchema(schema, schemaId)
}
getSchema (schemaId, jsonPointer = '#') {
@@ -60,7 +86,11 @@ class RefResolver {
for (const ref of schema.refs) {
const dependencySchemaId = ref.schemaId
if (dependencies[dependencySchemaId] !== undefined) continue
if (
dependencySchemaId === schemaId ||
dependencies[dependencySchemaId] !== undefined
) continue
dependencies[dependencySchemaId] = this.getSchema(dependencySchemaId)
this.getSchemaDependencies(dependencySchemaId, dependencies)
}
@@ -84,12 +114,12 @@ class RefResolver {
}
const refs = []
this.#addDerefSchema(schema.schema, schemaId, refs)
this.#addDerefSchema(schema.schema, schemaId, true, refs)
const dependencies = this.getSchemaDependencies(schemaId)
for (const schemaId in dependencies) {
const schema = dependencies[schemaId]
this.#addDerefSchema(schema, schemaId, refs)
this.#addDerefSchema(schema, schemaId, true, refs)
}
for (const ref of refs) {
@@ -140,36 +170,19 @@ class RefResolver {
}
}
#addSchema (schema, rootSchemaId) {
const schemaId = schema.$id
if (schemaId !== undefined && typeof schemaId === 'string') {
if (schemaId.charAt(0) === '#') {
this.#insertSchemaByAnchor(schema, rootSchemaId, schemaId)
} else {
this.#insertSchemaBySchemaId(schema, schemaId)
rootSchemaId = schemaId
}
}
const ref = schema.$ref
if (ref !== undefined && typeof ref === 'string') {
const { refSchemaId, refJsonPointer } = this.#parseSchemaRef(ref, rootSchemaId)
this.#schemas[rootSchemaId].refs.push({
schemaId: refSchemaId,
jsonPointer: refJsonPointer
})
}
for (const key in schema) {
if (typeof schema[key] === 'object' && schema[key] !== null) {
this.#addSchema(schema[key], rootSchemaId)
}
}
}
#addDerefSchema (schema, rootSchemaId, refs = []) {
#addDerefSchema (schema, rootSchemaId, isRootSchema, refs = []) {
const derefSchema = Array.isArray(schema) ? [...schema] : { ...schema }
if (isRootSchema) {
if (schema.$id !== undefined && schema.$id.charAt(0) !== '#') {
// Schema has an $id that is not an anchor
rootSchemaId = schema.$id
} else {
// Schema has no $id or $id is an anchor
this.#insertDerefSchemaBySchemaId(derefSchema, rootSchemaId)
}
}
const schemaId = derefSchema.$id
if (schemaId !== undefined && typeof schemaId === 'string') {
if (schemaId.charAt(0) === '#') {
@@ -191,7 +204,7 @@ class RefResolver {
for (const key in derefSchema) {
const value = derefSchema[key]
if (typeof value === 'object' && value !== null) {
derefSchema[key] = this.#addDerefSchema(value, rootSchemaId, refs)
derefSchema[key] = this.#addDerefSchema(value, rootSchemaId, false, refs)
}
}
@@ -217,7 +230,7 @@ class RefResolver {
if (sourceSchema[key] !== undefined) {
if (deepEqual(sourceSchema[key], targetSchema[key])) continue
throw new Error(
`Cannot resolve ref "${ref.ref}". Property "${key}" is already exist in schema "${ref.sourceSchemaId}".`
`Cannot resolve ref "${ref.ref}". Property "${key}" already exists in schema "${ref.sourceSchemaId}".`
)
}
sourceSchema[key] = targetSchema[key]
@@ -237,7 +250,7 @@ class RefResolver {
#insertSchemaByAnchor (schema, schemaId, anchor) {
const { anchors } = this.#schemas[schemaId]
if (anchors[anchor] !== undefined) {
throw new Error(`There is already another anchor "${anchor}" in a schema "${schemaId}".`)
throw new Error(`There is already another anchor "${anchor}" in schema "${schemaId}".`)
}
anchors[anchor] = schema
}

View File

@@ -1,12 +1,13 @@
{
"name": "json-schema-ref-resolver",
"version": "1.0.1",
"version": "3.0.0",
"description": "JSON schema reference resolver",
"main": "index.js",
"type": "commonjs",
"types": "types/index.d.ts",
"scripts": {
"lint": "standard",
"lint:fix": "standard --fix",
"lint": "eslint",
"lint:fix": "eslint --fix",
"test:unit": "c8 --100 node --test",
"test:typescript": "tsd",
"test": "npm run lint && npm run test:unit && npm run test:typescript"
@@ -19,6 +20,9 @@
"type": "git",
"url": "git+https://github.com/fastify/json-schema-ref-resolver.git"
},
"bugs": {
"url": "https://github.com/fastify/json-schema-ref-resolver/issues"
},
"keywords": [
"json",
"schema",
@@ -27,18 +31,46 @@
"swagger"
],
"author": "Ivan Tymoshenko <ivan@tymoshenko.me>",
"contributors": [
{
"name": "Matteo Collina",
"email": "hello@matteocollina.com"
},
{
"name": "James Sumners",
"url": "https://james.sumners.info"
},
{
"name": "Gürgün Dayıoğlu",
"email": "hey@gurgun.day",
"url": "https://heyhey.to/G"
},
{
"name": "Frazer Smith",
"email": "frazer.dev@icloud.com",
"url": "https://github.com/fdawgs"
}
],
"license": "MIT",
"homepage": "https://github.com/fastify/json-schema-ref-resolver#readme",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"dependencies": {
"fast-deep-equal": "^3.1.3"
"dequal": "^2.0.3"
},
"devDependencies": {
"c8": "^8.0.1",
"standard": "^17.1.0",
"tsd": "^0.29.0"
},
"standard": {
"ignore": [
"types/*"
]
"@fastify/pre-commit": "^2.1.0",
"c8": "^10.1.3",
"eslint": "^9.17.0",
"neostandard": "^0.12.0",
"tsd": "^0.31.0"
}
}

View File

@@ -194,6 +194,6 @@ test('should throw if there is the same anchor in the same schema', () => {
refResolver.addSchema(schema)
assert.fail('should throw')
} catch (err) {
assert.equal(err.message, 'There is already another anchor "#subSchemaId" in a schema "schemaId1".')
assert.equal(err.message, 'There is already another anchor "#subSchemaId" in schema "schemaId1".')
}
})

View File

@@ -45,7 +45,7 @@ test('should throw id source schema has a key with a same key as ref schema, but
} catch (err) {
assert.strictEqual(
err.message,
'Cannot resolve ref "schemaId2". Property "properties" is already exist in schema "schemaId1".'
'Cannot resolve ref "schemaId2". Property "properties" already exists in schema "schemaId1".'
)
}
})
@@ -234,3 +234,54 @@ test('should throw if target ref schema is not found', () => {
)
}
})
test('should deref schema without root $id', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId1'
const schema = {
type: 'object',
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
}
},
allOf: [
{
$ref: '#/definitions/id1'
}
]
}
refResolver.addSchema(schema, schemaId)
const derefSchema = refResolver.getDerefSchema(schemaId)
assert.deepStrictEqual(derefSchema, {
type: 'object',
definitions: {
id1: {
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
}
},
allOf: [
{
type: 'object',
properties: {
id1: {
type: 'integer'
}
}
}
]
})
})

View File

@@ -4,19 +4,18 @@
* @constructor
*/
declare class RefResolver {
/**
* @param {object} opts - Options for the resolver.
* @param {boolean} opts.allowEqualDuplicates - Whether to allow schemas with equal ids to be added to the resolver.
*/
constructor(opts?: { allowEqualDuplicates?: boolean });
constructor (opts?: { allowEqualDuplicates?: boolean })
/**
* Adds the given schema to the resolver.
* @param {any} schema - The schema to be added.
* @param {string} schemaId - The default schema id of the schema to be added.
*/
addSchema(schema: any, schemaId?: string): void;
addSchema (schema: any, schemaId?: string): void
/**
* Returns the schema by the given schema id and jsonPointer.
@@ -25,34 +24,34 @@ declare class RefResolver {
* @param {string} jsonPointer - The jsonPointer of the schema to be returned.
* @returns {any | null} The schema by the given schema id and jsonPointer.
*/
getSchema(schemaId: string, jsonPointer?: string): any | null;
getSchema (schemaId: string, jsonPointer?: string): any | null
/**
* Returns true if the schema by the given schema id is added to the resolver.
* @param {string} schemaId - The schema id of the schema to be checked.
* @returns {boolean} True if the schema by the given schema id is added to the resolver.
*/
hasSchema(schemaId: string): boolean;
hasSchema (schemaId: string): boolean
/**
* Returns the schema references of the schema by the given schema id.
* @param {string} schemaId - The schema id of the schema whose references are to be returned.
* @returns {Array<{ schemaId: string; jsonPointer: string }>} The schema references of the schema by the given schema id.
*/
getSchemaRefs(schemaId: string): { schemaId: string; jsonPointer: string }[];
getSchemaRefs (schemaId: string): { schemaId: string; jsonPointer: string }[]
/**
* Returns all the schema dependencies of the schema by the given schema id.
* @param {string} schemaId - The schema id of the schema whose dependencies are to be returned.
* @returns {object} The schema dependencies of the schema by the given schema id.
*/
getSchemaDependencies(schemaId: string): { [key: string]: any };
getSchemaDependencies (schemaId: string): { [key: string]: any }
/**
* Dereferences the schema by the given schema id.
* @param {string} schemaId - The schema id of the schema to be dereferenced.
*/
derefSchema(schemaId: string): void;
derefSchema (schemaId: string): void
/**
* Returns the dereferenced schema by the given schema id and jsonPointer.
@@ -62,7 +61,7 @@ declare class RefResolver {
* @param {string} jsonPointer - The jsonPointer of the schema to be returned.
* @returns {any | null} The dereferenced schema by the given schema id and jsonPointer.
*/
getDerefSchema(schemaId: string, jsonPointer?: string): any | null;
getDerefSchema (schemaId: string, jsonPointer?: string): any | null
}
export { RefResolver };
export { RefResolver }