Projektstart

This commit is contained in:
2026-01-22 15:49:12 +01:00
parent 7212eb6f7a
commit 57e5f652f8
10637 changed files with 2598792 additions and 64 deletions

View File

@@ -0,0 +1,2 @@
# Set default behavior to automatically convert line endings
* text=auto eol=lf

View File

@@ -0,0 +1,32 @@
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

21
backend/node_modules/json-schema-ref-resolver/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Fastify
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

398
backend/node_modules/json-schema-ref-resolver/README.md generated vendored Normal file
View File

@@ -0,0 +1,398 @@
# json-schema-ref-resolver
__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)
- [Usage](#usage)
- [API](#api)
- [RefResolver([options])](#refresolveroptions)
- [addSchema(schema, [schemaId])](#addschemaschema-schemaid)
- [getSchema(schemaId, [jsonPointer])](#getschemaschemaid-jsonpointer)
- [getSchemaRefs(schemaId)](#getschemarefsschemaid)
- [getSchemaDependencies(schemaId)](#getschemadependenciesschemaid)
- [derefSchema(schemaId)](#derefschemaschemaid)
- [getDerefSchema(schemaId, [jsonPointer])](#getderefschemaschemaid-jsonpointer)
- [Caveats](#caveats)
<a name="installation"></a>
## Installation
```bash
npm install json-schema-ref-resolver
```
<a name="usage"></a>
## Usage
```javascript
const assert = require('node:assert')
const { RefResolver } = require('json-schema-ref-resolver')
const sourceSchema = {
$id: 'sourceSchema',
type: 'object',
properties: {
foo: {
$ref: 'targetSchema#/definitions/bar'
}
}
}
const targetSchema = {
$id: 'targetSchema',
definitions: {
bar: {
type: 'string'
}
}
}
const refResolver = new RefResolver()
refResolver.addSchema(sourceSchema)
refResolver.addSchema(targetSchema)
const derefSourceSchema = refResolver.getDerefSchema('sourceSchema')
assert.deepStrictEqual(derefSourceSchema, {
$id: 'sourceSchema',
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
```
<a name="api"></a>
## API
<a name="constructor"></a>
#### RefResolver([options])
- __allowEqualDuplicates__ - if set to `false`, an error will be thrown if a schema with the same `$id` is added to the resolver. If set to `true`, the error will be thrown only if the schemas are not equal. Default: `true`.
- __insertRefSymbol__ - if set to `true` resolver inserts a `Symbol.for('json-schema-ref')` instead of the `$ref` property when dereferencing a schema. Default `false`.
- __cloneSchemaWithoutRefs__ - if set to `false` resolver would not clone a schema if it does not have references. That allows to significantly improve performance when dereferencing a schema. If you want to modify a schema after dereferencing, set this option to `true`. Default: `false`.
<a name="add-schema"></a>
#### addSchema(schema, [schemaId])
Adds a json schema to the resolver. If the schema has an `$id` property, it will be used as the schema id. Otherwise, the `schemaId` argument will be used as the schema id. During the addition of the schema, ref resolver will find and add all nested schemas and references.
- `schema` __\<object\>__ - json schema to add to the resolver.
- `schemaId` __\<string\>__ - schema id to use. Will be used only if the schema does not have an `$id` property.
<a name="get-schema"></a>
#### getSchema(schemaId, [jsonPointer])
Returns a json schema by its id. If the `jsonPointer` argument is provided, ref resolver will return the schema at the specified json pointer location.
If shema with the specified id is not found, an error will be thrown. If the `jsonPointer` argument is provided and the schema at the specified location is not found, getSchema will return `null`.
- `schemaId` __\<string\>__ - schema id of the schema to return.
- `jsonPointer` __\<string\>__ - json pointer to the schema location.
- __Returns__ - json schema or `null` if the schema at the specified location is not found.
_Example:_
```javascript
const assert = require('node:assert')
const { RefResolver } = require('json-schema-ref-resolver')
const schema = {
$id: 'schema',
type: 'object',
properties: {
foo: { type: 'string' }
}
}
const refResolver = new RefResolver()
refResolver.addSchema(schema)
const rootSchema = refResolver.getSchema(schema.$id)
assert.deepStrictEqual(rootSchema, schema)
const subSchema = refResolver.getSchema(schema.$id, '#/properties/foo')
assert.deepStrictEqual(subSchema, { type: 'string' })
```
`getSchema` can also be used to get a schema by its [json schema anchor](https://json-schema.org/draft/2020-12/json-schema-core#section-8.2.2). To get schema by schema anchor, the `schemaId` argument must be set to the `$id` of the schema that contains the anchor, and the `jsonPointer` argument must be set to the anchor.
_Example:_
```javascript
const assert = require('node:assert')
const { RefResolver } = require('json-schema-ref-resolver')
const schema = {
$id: 'schema',
definitions: {
bar: {
$id: '#bar',
type: 'string'
}
}
}
const refResolver = new RefResolver()
refResolver.addSchema(schema)
const anchorSchema = refResolver.getSchema(schema.$id, '#bar')
assert.deepStrictEqual(subSchema, {
$id: '#bar',
type: 'string'
})
```
<a name="get-schema-refs"></a>
#### getSchemaRefs(schemaId)
Returns all references found in the schema during the addition of the schema to the resolver. If schema with the specified id is not found, an error will be thrown.
- `schemaId` __\<string\>__ - schema id of the schema to return references for.
- __Returns__ - array of objects with the following properties:
- `schemaId` __\<string\>__ - schema id of the reference.
- `jsonPointer` __\<string\>__ - json pointer of the reference.
If a reference does not have a schema id part, the schema id of the schema that contains the reference is used as the schema id of the reference.
_Example:_
```javascript
const assert = require('node:assert')
const { RefResolver } = require('json-schema-ref-resolver')
const sourceSchema = {
$id: 'sourceSchema',
type: 'object',
properties: {
foo: {
$ref: 'targetSchema#/definitions/bar'
},
baz: {
$ref: 'targetSchema#/definitions/qux'
}
}
}
const refResolver = new RefResolver()
refResolver.addSchema(sourceSchema)
const refs = refResolver.getSchemaRefs('sourceSchema')
assert.deepStrictEqual(refs, [
{
schemaId: 'targetSchema',
jsonPointer: '#/definitions/bar'
},
{
schemaId: 'targetSchema',
jsonPointer: '#/definitions/qux'
}
])
```
<a name="get-schema-dependencies"></a>
#### getSchemaDependencies(schemaId)
Returns all dependencies including nested dependencies found in the schema during the addition of the schema to the resolver. If schema with the specified id is not found, an error will be thrown.
- `schemaId` __\<string\>__ - schema id of the schema to return dependencies for.
- __Returns__ - an object with all found dependencies. The object keys are schema ids and the values are schema dependencies.
_Example:_
```javascript
const assert = require('node:assert')
const { RefResolver } = require('json-schema-ref-resolver')
const targetSchema1 = {
$id: 'targetSchema1',
definitions: {
bar: { type: 'string' }
}
}
const targetSchema2 = {
$id: 'targetSchema2',
type: 'object',
properties: {
qux: { $ref: 'targetSchema1' }
}
}
const sourceSchema = {
$id: 'sourceSchema',
type: 'object',
properties: {
foo: { $ref: 'targetSchema2' }
}
}
const refResolver = new RefResolver()
refResolver.addSchema(sourceSchema)
refResolver.addSchema(targetSchema1)
refResolver.addSchema(targetSchema2)
const dependencies = refResolver.getSchemaDependencies('sourceSchema')
assert.deepStrictEqual(dependencies, { targetSchema1, targetSchema2 })
```
<a name="deref-schema"></a>
#### 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.
- `schemaId` __\<string\>__ - schema id of the schema to dereference.
__If a json schema has circular references or circular cross-references, dereferenced schema will have js circular references. Be careful when traversing the dereferenced schema.__
_Example_
```javascript
const assert = require('node:assert')
const { RefResolver } = require('json-schema-ref-resolver')
const sourceSchema = {
$id: 'sourceSchema',
type: 'object',
properties: {
foo: {
$ref: 'targetSchema#/definitions/bar'
}
}
}
const targetSchema = {
$id: 'targetSchema',
definitions: {
bar: {
type: 'string'
}
}
}
const refResolver = new RefResolver()
refResolver.addSchema(sourceSchema)
refResolver.addSchema(targetSchema)
refResolver.derefSchema('sourceSchema')
const derefSourceSchema = refResolver.getDerefSchema('sourceSchema')
assert.deepStrictEqual(derefSourceSchema, {
$id: 'sourceSchema',
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
```
<a name="get-deref-schema"></a>
#### getDerefSchema(schemaId, [jsonPointer])
Returns a dereferenced schema by schema id and json pointer. If schema with the specified id is not found, an error will be thrown. If the `jsonPointer` argument is provided, ref resolver will return the schema at the specified json pointer location. If the `jsonPointer` argument is provided and the schema at the specified location is not found, getDerefSchema will return `null`.
If schema was not dereferenced before, it will be dereferenced during the call to `getDerefSchema`.
- `schemaId` __\<string\>__ - schema id of the schema to return.
- `jsonPointer` __\<string\>__ - json pointer to the schema location.
- __Returns__ - dereferenced json schema or `null` if the schema at the specified location is not found.
_Example:_
```javascript
const assert = require('node:assert')
const { RefResolver } = require('json-schema-ref-resolver')
const sourceSchema = {
$id: 'sourceSchema',
type: 'object',
properties: {
foo: {
$ref: 'targetSchema#/definitions/bar'
}
}
}
const targetSchema = {
$id: 'targetSchema',
definitions: {
bar: {
type: 'string'
}
}
}
const refResolver = new RefResolver()
refResolver.addSchema(sourceSchema)
refResolver.addSchema(targetSchema)
const derefSourceSchema = refResolver.getDerefSchema('sourceSchema')
assert.deepStrictEqual(derefSourceSchema, {
$id: 'sourceSchema',
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
```
#### 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`.
_Example:_
```javascript
const assert = require('node:assert')
const { RefResolver } = require('json-schema-ref-resolver')
const targetSchema = {
$id: 'targetSchema',
definitions: {
bar: {
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
const sourceSchema = {
$id: 'sourceSchema',
type: 'object',
$ref: 'targetSchema#/definitions/bar'
properties: {
foo: { type: 'number' }
}
}
const refResolver = new RefResolver()
refResolver.addSchema(targetSchema)
refResolver.addSchema(sourceSchema)
refResolver.derefSchema('sourceSchema') // Throws an error
```

271
backend/node_modules/json-schema-ref-resolver/index.js generated vendored Normal file
View File

@@ -0,0 +1,271 @@
'use strict'
const deepEqual = require('fast-deep-equal')
const jsonSchemaRefSymbol = Symbol.for('json-schema-ref')
class RefResolver {
#schemas
#derefSchemas
#insertRefSymbol
#allowEqualDuplicates
#cloneSchemaWithoutRefs
constructor (opts = {}) {
this.#schemas = {}
this.#derefSchemas = {}
this.#insertRefSymbol = opts.insertRefSymbol ?? false
this.#allowEqualDuplicates = opts.allowEqualDuplicates ?? true
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)
}
this.#addSchema(schema, schemaId)
}
getSchema (schemaId, jsonPointer = '#') {
const schema = this.#schemas[schemaId]
if (schema === undefined) {
throw new Error(
`Cannot resolve ref "${schemaId}${jsonPointer}". Schema with id "${schemaId}" is not found.`
)
}
if (schema.anchors[jsonPointer] !== undefined) {
return schema.anchors[jsonPointer]
}
return getDataByJSONPointer(schema.schema, jsonPointer)
}
hasSchema (schemaId) {
return this.#schemas[schemaId] !== undefined
}
getSchemaRefs (schemaId) {
const schema = this.#schemas[schemaId]
if (schema === undefined) {
throw new Error(`Schema with id "${schemaId}" is not found.`)
}
return schema.refs
}
getSchemaDependencies (schemaId, dependencies = {}) {
const schema = this.#schemas[schemaId]
for (const ref of schema.refs) {
const dependencySchemaId = ref.schemaId
if (dependencies[dependencySchemaId] !== undefined) continue
dependencies[dependencySchemaId] = this.getSchema(dependencySchemaId)
this.getSchemaDependencies(dependencySchemaId, dependencies)
}
return dependencies
}
derefSchema (schemaId) {
if (this.#derefSchemas[schemaId] !== undefined) return
const schema = this.#schemas[schemaId]
if (schema === undefined) {
throw new Error(`Schema with id "${schemaId}" is not found.`)
}
if (!this.#cloneSchemaWithoutRefs && schema.refs.length === 0) {
this.#derefSchemas[schemaId] = {
schema: schema.schema,
anchors: schema.anchors
}
}
const refs = []
this.#addDerefSchema(schema.schema, schemaId, refs)
const dependencies = this.getSchemaDependencies(schemaId)
for (const schemaId in dependencies) {
const schema = dependencies[schemaId]
this.#addDerefSchema(schema, schemaId, refs)
}
for (const ref of refs) {
const {
refSchemaId,
refJsonPointer
} = this.#parseSchemaRef(ref.ref, ref.sourceSchemaId)
const targetSchema = this.getDerefSchema(refSchemaId, refJsonPointer)
if (targetSchema === null) {
throw new Error(
`Cannot resolve ref "${ref.ref}". Ref "${refJsonPointer}" is not found in schema "${refSchemaId}".`
)
}
ref.targetSchema = targetSchema
ref.targetSchemaId = refSchemaId
}
for (const ref of refs) {
this.#resolveRef(ref, refs)
}
}
getDerefSchema (schemaId, jsonPointer = '#') {
let derefSchema = this.#derefSchemas[schemaId]
if (derefSchema === undefined) {
this.derefSchema(schemaId)
derefSchema = this.#derefSchemas[schemaId]
}
if (derefSchema.anchors[jsonPointer] !== undefined) {
return derefSchema.anchors[jsonPointer]
}
return getDataByJSONPointer(derefSchema.schema, jsonPointer)
}
#parseSchemaRef (ref, schemaId) {
const sharpIndex = ref.indexOf('#')
if (sharpIndex === -1) {
return { refSchemaId: ref, refJsonPointer: '#' }
}
if (sharpIndex === 0) {
return { refSchemaId: schemaId, refJsonPointer: ref }
}
return {
refSchemaId: ref.slice(0, sharpIndex),
refJsonPointer: ref.slice(sharpIndex)
}
}
#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 = []) {
const derefSchema = Array.isArray(schema) ? [...schema] : { ...schema }
const schemaId = derefSchema.$id
if (schemaId !== undefined && typeof schemaId === 'string') {
if (schemaId.charAt(0) === '#') {
this.#insertDerefSchemaByAnchor(derefSchema, rootSchemaId, schemaId)
} else {
this.#insertDerefSchemaBySchemaId(derefSchema, schemaId)
rootSchemaId = schemaId
}
}
if (derefSchema.$ref !== undefined) {
refs.push({
ref: derefSchema.$ref,
sourceSchemaId: rootSchemaId,
sourceSchema: derefSchema
})
}
for (const key in derefSchema) {
const value = derefSchema[key]
if (typeof value === 'object' && value !== null) {
derefSchema[key] = this.#addDerefSchema(value, rootSchemaId, refs)
}
}
return derefSchema
}
#resolveRef (ref, refs) {
const { sourceSchema, targetSchema } = ref
if (!sourceSchema.$ref) return
if (this.#insertRefSymbol) {
sourceSchema[jsonSchemaRefSymbol] = sourceSchema.$ref
}
delete sourceSchema.$ref
if (targetSchema.$ref) {
const targetSchemaRef = refs.find(ref => ref.sourceSchema === targetSchema)
this.#resolveRef(targetSchemaRef, refs)
}
for (const key in targetSchema) {
if (key === '$id') continue
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}".`
)
}
sourceSchema[key] = targetSchema[key]
}
ref.isResolved = true
}
#insertSchemaBySchemaId (schema, schemaId) {
const foundSchema = this.#schemas[schemaId]
if (foundSchema !== undefined) {
if (this.#allowEqualDuplicates && deepEqual(schema, foundSchema.schema)) return
throw new Error(`There is already another schema with id "${schemaId}".`)
}
this.#schemas[schemaId] = { schema, anchors: {}, refs: [] }
}
#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}".`)
}
anchors[anchor] = schema
}
#insertDerefSchemaBySchemaId (schema, schemaId) {
const foundSchema = this.#derefSchemas[schemaId]
if (foundSchema !== undefined) return
this.#derefSchemas[schemaId] = { schema, anchors: {} }
}
#insertDerefSchemaByAnchor (schema, schemaId, anchor) {
const { anchors } = this.#derefSchemas[schemaId]
anchors[anchor] = schema
}
}
function getDataByJSONPointer (data, jsonPointer) {
const parts = jsonPointer.split('/')
let current = data
for (const part of parts) {
if (part === '' || part === '#') continue
if (typeof current !== 'object' || current === null) {
return null
}
current = current[part]
}
return current ?? null
}
module.exports = { RefResolver }

View File

@@ -0,0 +1,44 @@
{
"name": "json-schema-ref-resolver",
"version": "1.0.1",
"description": "JSON schema reference resolver",
"main": "index.js",
"types": "types/index.d.ts",
"scripts": {
"lint": "standard",
"lint:fix": "standard --fix",
"test:unit": "c8 --100 node --test",
"test:typescript": "tsd",
"test": "npm run lint && npm run test:unit && npm run test:typescript"
},
"precommit": [
"lint",
"test"
],
"repository": {
"type": "git",
"url": "git+https://github.com/fastify/json-schema-ref-resolver.git"
},
"keywords": [
"json",
"schema",
"reference",
"openapi",
"swagger"
],
"author": "Ivan Tymoshenko <ivan@tymoshenko.me>",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
"devDependencies": {
"c8": "^8.0.1",
"standard": "^17.1.0",
"tsd": "^0.29.0"
},
"standard": {
"ignore": [
"types/*"
]
}
}

View File

@@ -0,0 +1,56 @@
'use strict'
const assert = require('node:assert/strict')
const { test } = require('node:test')
const { RefResolver } = require('../index.js')
test('should get a sub schema by sub schema anchor', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const subSchemaAnchor = '#subSchemaId'
const schema = {
$id: schemaId,
definitions: {
subSchema: {
$id: subSchemaAnchor,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
refResolver.addSchema(schema)
const resolvedSchema = refResolver.getSchema(schemaId, subSchemaAnchor)
assert.equal(resolvedSchema, schema.definitions.subSchema)
})
test('should fail to find a schema using an anchor instead of schema id', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const subSchemaAnchor = '#subSchemaId'
const schema = {
$id: schemaId,
definitions: {
subSchema: {
$id: subSchemaAnchor,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
refResolver.addSchema(schema)
try {
refResolver.getSchema(subSchemaAnchor)
} catch (err) {
assert.equal(
err.message, 'Cannot resolve ref "#subSchemaId#". Schema with id "#subSchemaId" is not found.'
)
}
})

View File

@@ -0,0 +1,199 @@
'use strict'
const assert = require('node:assert/strict')
const { test } = require('node:test')
const { RefResolver } = require('../index.js')
test('should not throw if there is a same schema with a same id', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
$id: schemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema)
refResolver.addSchema(schema)
const resolvedSchema = refResolver.getSchema(schemaId)
assert.deepStrictEqual(resolvedSchema, schema)
})
test('should throw if there is a same schema with a same id (allowEqualDuplicates === false)', () => {
const refResolver = new RefResolver({
allowEqualDuplicates: false
})
const schemaId = 'schemaId'
const schema = {
$id: schemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema)
try {
refResolver.addSchema(schema)
assert.fail('should throw')
} catch (err) {
assert.equal(err.message, `There is already another schema with id "${schemaId}".`)
}
})
test('should throw if there is another schema with a same id', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema1 = {
$id: schemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
const schema2 = {
$id: schemaId,
type: 'object',
properties: {
bar: { type: 'string' }
}
}
refResolver.addSchema(schema1)
try {
refResolver.addSchema(schema2)
assert.fail('should throw')
} catch (err) {
assert.equal(err.message, `There is already another schema with id "${schemaId}".`)
}
const resolvedSchema = refResolver.getSchema(schemaId)
assert.deepStrictEqual(resolvedSchema, schema1)
})
test('should not throw if there is a same sub schema with a same id', () => {
const refResolver = new RefResolver()
const subSchemaId = 'subSchemaId'
const schema1 = {
$id: 'schemaId1',
definitions: {
subSchema: {
$id: subSchemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
const schema2 = {
$id: 'schemaId2',
definitions: {
subSchema: {
$id: subSchemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
const resolvedSchema = refResolver.getSchema(subSchemaId)
assert.deepStrictEqual(resolvedSchema, schema1.definitions.subSchema)
assert.deepStrictEqual(resolvedSchema, schema2.definitions.subSchema)
})
test('should throw if there is another different sub schema with a same id', () => {
const refResolver = new RefResolver()
const subSchemaId = 'subSchemaId'
const schema1 = {
$id: 'schemaId1',
definitions: {
subSchema: {
$id: subSchemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
const schema2 = {
$id: 'schemaId2',
definitions: {
subSchema: {
$id: subSchemaId,
type: 'object',
properties: {
bar: { type: 'string' }
}
}
}
}
refResolver.addSchema(schema1)
try {
refResolver.addSchema(schema2)
assert.fail('should throw')
} catch (err) {
assert.equal(err.message, `There is already another schema with id "${subSchemaId}".`)
}
const resolvedSchema = refResolver.getSchema(subSchemaId)
assert.deepStrictEqual(resolvedSchema, schema1.definitions.subSchema)
assert.notDeepStrictEqual(resolvedSchema, schema2.definitions.subSchema)
})
test('should throw if there is the same anchor in the same schema', () => {
const refResolver = new RefResolver()
const subSchemaAnchor = '#subSchemaId'
const schema = {
$id: 'schemaId1',
definitions: {
subSchema1: {
$id: subSchemaAnchor,
type: 'object',
properties: {
foo: { type: 'string' }
}
},
subSchema2: {
$id: subSchemaAnchor,
type: 'object',
properties: {
bar: { type: 'string' }
}
}
}
}
try {
refResolver.addSchema(schema)
assert.fail('should throw')
} catch (err) {
assert.equal(err.message, 'There is already another anchor "#subSchemaId" in a schema "schemaId1".')
}
})

View File

@@ -0,0 +1,236 @@
'use strict'
const assert = require('node:assert/strict')
const { test } = require('node:test')
const { RefResolver } = require('../index.js')
test('should throw id schema not found', () => {
const refResolver = new RefResolver()
try {
refResolver.derefSchema('schemaId1')
} catch (err) {
assert.strictEqual(err.message, 'Schema with id "schemaId1" is not found.')
}
})
test('should throw id source schema has a key with a same key as ref schema, but diff value', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schemaId2 = 'schemaId2'
const schema1 = {
$id: schemaId1,
$ref: schemaId2,
properties: {
foo: { type: 'string' }
}
}
const schema2 = {
$id: schemaId2,
type: 'object',
properties: {
foo: { type: 'number' }
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
try {
refResolver.derefSchema('schemaId1')
assert.fail('should throw error')
} catch (err) {
assert.strictEqual(
err.message,
'Cannot resolve ref "schemaId2". Property "properties" is already exist in schema "schemaId1".'
)
}
})
test('should not throw id source schema has a key with a same key and value as ref schema', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schemaId2 = 'schemaId2'
const schema1 = {
$id: schemaId1,
$ref: schemaId2,
properties: {
foo: { type: 'string' }
}
}
const schema2 = {
$id: schemaId2,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
refResolver.derefSchema(schemaId1)
const derefSchema = refResolver.getDerefSchema(schemaId1)
assert.deepStrictEqual(derefSchema, {
$id: schemaId1,
type: 'object',
properties: {
foo: { type: 'string' }
}
})
})
test('should get deref schema from the cache', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schemaId2 = 'schemaId2'
const schema1 = {
$id: schemaId1,
$ref: schemaId2,
properties: {
foo: { type: 'string' }
}
}
const schema2 = {
$id: schemaId2,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
refResolver.derefSchema(schemaId1)
schema1.properties = {}
refResolver.derefSchema(schemaId1)
const derefSchema = refResolver.getDerefSchema(schemaId1)
assert.deepStrictEqual(derefSchema, {
$id: schemaId1,
type: 'object',
properties: {
foo: { type: 'string' }
}
})
})
test('should insert ref symbol', () => {
const refResolver = new RefResolver({
insertRefSymbol: true
})
const schemaId1 = 'schemaId1'
const schemaId2 = 'schemaId2'
const schema1 = {
$id: schemaId1,
$ref: schemaId2
}
const schema2 = {
$id: schemaId2,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
refResolver.derefSchema(schemaId1)
const derefSchema = refResolver.getDerefSchema(schemaId1)
assert.deepStrictEqual(derefSchema, {
$id: schemaId1,
type: 'object',
properties: {
foo: { type: 'string' }
},
[Symbol.for('json-schema-ref')]: schemaId2
})
})
test('should clone schema without refs', () => {
const refResolver = new RefResolver({
cloneSchemaWithoutRefs: true
})
const schemaId = 'schemaId2'
const schema = {
$id: schemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema)
refResolver.derefSchema(schemaId)
schema.properties = null
const derefSchema = refResolver.getDerefSchema(schemaId)
assert.deepStrictEqual(derefSchema, {
$id: schemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
})
})
test('should throw if target ref schema is not found', () => {
const inputSchema = {
$id: 'http://example.com/root.json',
definitions: {
A: { $id: '#foo' },
B: {
$id: 'other.json',
definitions: {
X: { $id: '#bar', type: 'string' },
Y: { $id: 't/inner.json' }
}
},
C: {
$id: 'urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f',
type: 'object'
}
}
}
const addresSchema = {
$id: 'relativeAddress', // Note: prefer always absolute URI like: http://mysite.com
type: 'object',
properties: {
zip: { $ref: 'urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f' },
city2: { $ref: '#foo' }
}
}
const refResolver = new RefResolver()
refResolver.addSchema(inputSchema)
refResolver.addSchema(addresSchema)
try {
refResolver.derefSchema('relativeAddress')
} catch (error) {
assert.strictEqual(
error.message,
'Cannot resolve ref "#foo". Ref "#foo" is not found in schema "relativeAddress".'
)
}
})

View File

@@ -0,0 +1,363 @@
'use strict'
const assert = require('node:assert/strict')
const { test } = require('node:test')
const { RefResolver } = require('../index.js')
test('should resolve reference', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schema1 = {
$id: schemaId1,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
const schemaId2 = 'schemaId2'
const schema2 = {
$id: schemaId2,
$ref: schemaId1
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
const derefSchema1 = refResolver.getDerefSchema(schemaId1)
assert.deepStrictEqual(derefSchema1, schema1)
const derefSchema2 = refResolver.getDerefSchema(schemaId2)
assert.deepStrictEqual(derefSchema2, {
$id: schemaId2,
type: 'object',
properties: {
foo: { type: 'string' }
}
})
})
test('should get deref schema by anchor', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schemaId2 = 'schemaId2'
const schema1 = {
$id: schemaId1,
definitions: {
$id: '#subschema',
type: 'object',
properties: {
foo: { type: 'string' },
bar: { $ref: schemaId2 }
}
}
}
const schema2 = {
$id: schemaId2,
type: 'object',
properties: {
baz: { type: 'string' }
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
const derefSubSchema = refResolver.getDerefSchema(schemaId1, '#subschema')
assert.deepStrictEqual(derefSubSchema, {
$id: '#subschema',
type: 'object',
properties: {
foo: { type: 'string' },
bar: {
type: 'object',
properties: {
baz: { type: 'string' }
}
}
}
})
})
test('should merge main and ref schemas', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schema1 = {
$id: schemaId1,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
const schemaId2 = 'schemaId2'
const schema2 = {
$id: schemaId2,
type: 'object',
properties: {
foo: { $ref: schemaId1 + '#/properties/foo' },
bar: { type: 'string' }
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
const derefSchema1 = refResolver.getDerefSchema(schemaId1)
assert.deepStrictEqual(derefSchema1, schema1)
const derefSchema2 = refResolver.getDerefSchema(schemaId2)
assert.deepStrictEqual(derefSchema2, {
$id: schemaId2,
type: 'object',
properties: {
foo: { type: 'string' },
bar: { type: 'string' }
}
})
})
test('should merge multiple nested schemas', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schema1 = {
$id: schemaId1,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
const schemaId2 = 'schemaId2'
const schema2 = {
$id: schemaId2,
type: 'object',
properties: {
foo: { $ref: schemaId1 + '#/properties/foo' },
bar: { type: 'string' }
}
}
const schemaId3 = 'schemaId3'
const schema3 = {
$id: schemaId3,
type: 'object',
properties: {
foo: { $ref: schemaId2 + '#/properties/foo' },
bar: { $ref: schemaId2 + '#/properties/bar' },
baz: { type: 'string' }
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
refResolver.addSchema(schema3)
const derefSchema3 = refResolver.getDerefSchema(schemaId3)
assert.deepStrictEqual(derefSchema3, {
$id: schemaId3,
type: 'object',
properties: {
foo: { type: 'string' },
bar: { type: 'string' },
baz: { type: 'string' }
}
})
})
test('should resolve schema with circular reference', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
$id: schemaId,
type: 'object',
properties: {
foo: { $ref: '#' }
}
}
refResolver.addSchema(schema)
const derefSchema = refResolver.getDerefSchema(schemaId)
const expectedSchema = {
$id: schemaId,
type: 'object',
properties: {
foo: {
type: 'object',
properties: {}
}
}
}
expectedSchema.properties.foo.properties.foo = expectedSchema.properties.foo
assert.deepStrictEqual(derefSchema, expectedSchema)
})
test('should resolve schema with cross circular reference', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schemaId2 = 'schemaId2'
const schema1 = {
$id: schemaId1,
type: 'object',
properties: {
foo: { $ref: schemaId2 }
}
}
const schema2 = {
$id: schemaId2,
type: 'object',
properties: {
bar: { $ref: schemaId1 }
}
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
const derefSchema1 = refResolver.getDerefSchema(schemaId1)
const derefSchema2 = refResolver.getDerefSchema(schemaId2)
const expectedSchema1 = {
$id: schemaId1,
type: 'object',
properties: {
foo: {
type: 'object',
properties: {
bar: {
type: 'object',
properties: {}
}
}
}
}
}
expectedSchema1.properties.foo.properties.bar.properties.foo = expectedSchema1.properties.foo
const expectedSchema2 = {
$id: schemaId2,
type: 'object',
properties: {
bar: {
type: 'object',
properties: {
foo: {
type: 'object',
properties: {}
}
}
}
}
}
expectedSchema2.properties.bar.properties.foo.properties.bar = expectedSchema2.properties.bar
assert.deepStrictEqual(derefSchema1, expectedSchema1)
assert.deepStrictEqual(derefSchema2, expectedSchema2)
})
test('should resolve nested multiple times refs', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schema1 = {
$id: schemaId1,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
const schemaId2 = 'schemaId2'
const schema2 = {
$id: schemaId2,
$ref: schemaId1,
required: ['foo']
}
const schemaId3 = 'schemaId3'
const schema3 = {
$id: schemaId3,
$ref: schemaId2,
additionalProperties: false
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
refResolver.addSchema(schema3)
// Don't switch the order of these two lines.
const derefSchema3 = refResolver.getDerefSchema(schemaId3)
const derefSchema2 = refResolver.getDerefSchema(schemaId2)
assert.deepStrictEqual(derefSchema2, {
$id: schemaId2,
type: 'object',
properties: {
foo: { type: 'string' }
},
required: ['foo']
})
assert.deepStrictEqual(derefSchema3, {
$id: schemaId3,
type: 'object',
properties: {
foo: { type: 'string' }
},
required: ['foo'],
additionalProperties: false
})
})
test('should resolve infinite ref chain', () => {
const refResolver = new RefResolver()
const schemaId1 = 'schemaId1'
const schemaId2 = 'schemaId2'
const schemaId3 = 'schemaId3'
const schema1 = {
$id: schemaId1,
$ref: schemaId2
}
const schema2 = {
$id: schemaId2,
$ref: schemaId3
}
const schema3 = {
$id: schemaId3,
$ref: schemaId1
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
refResolver.addSchema(schema3)
const derefSchema1 = refResolver.getDerefSchema(schemaId1)
const derefSchema2 = refResolver.getDerefSchema(schemaId2)
const derefSchema3 = refResolver.getDerefSchema(schemaId3)
assert.deepStrictEqual(derefSchema1, {
$id: schemaId1
})
assert.deepStrictEqual(derefSchema2, {
$id: schemaId2
})
assert.deepStrictEqual(derefSchema3, {
$id: schemaId3
})
})

View File

@@ -0,0 +1,158 @@
'use strict'
const assert = require('node:assert/strict')
const { test } = require('node:test')
const { RefResolver } = require('../index.js')
test('should return all nested schema dependencies', () => {
const refResolver = new RefResolver()
const schema1Id = 'schemaId1'
const schema1 = {
$id: schema1Id,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
const schema2Id = 'schemaId2'
const schema2 = {
$id: schema2Id,
$ref: schema1Id
}
const schema3Id = 'schemaId3'
const schema3 = {
$id: schema3Id,
$ref: schema2Id
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
refResolver.addSchema(schema3)
const schema1Deps = refResolver.getSchemaDependencies(schema1Id)
assert.deepStrictEqual(schema1Deps, {})
const schema2Deps = refResolver.getSchemaDependencies(schema2Id)
assert.deepStrictEqual(schema2Deps, { [schema1Id]: schema1 })
const schema3Deps = refResolver.getSchemaDependencies(schema3Id)
assert.deepStrictEqual(schema3Deps, {
[schema1Id]: schema1,
[schema2Id]: schema2
})
})
test('should resolve a dependency to a subschema', () => {
const refResolver = new RefResolver()
const schema1Id = 'schemaId1'
const subSchema1Id = 'subSchemaId1'
const schema1 = {
$id: schema1Id,
definitions: {
subSchema: {
$id: subSchema1Id,
type: 'object',
properties: {
bar: { type: 'string' }
}
}
}
}
const schema2Id = 'schemaId2'
const schema2 = {
$id: schema2Id,
$ref: subSchema1Id + '#/definitions/subSchema'
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
const schema1Deps = refResolver.getSchemaDependencies(schema1Id)
assert.deepStrictEqual(schema1Deps, {})
const schema2Deps = refResolver.getSchemaDependencies(schema2Id)
assert.deepStrictEqual(schema2Deps, { [subSchema1Id]: schema1.definitions.subSchema })
})
test('should resolve a dependency with a json path', () => {
const refResolver = new RefResolver()
const schema1Id = 'schemaId1'
const subSchema1Id = 'subSchemaId1'
const schema1 = {
$id: schema1Id,
definitions: {
subSchema: {
$id: subSchema1Id,
type: 'object',
properties: {
bar: { type: 'string' }
}
}
}
}
const schema2Id = 'schemaId2'
const schema2 = {
$id: schema2Id,
$ref: schema1Id + '#/definitions/subSchema'
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
const schema1Deps = refResolver.getSchemaDependencies(schema1Id)
assert.deepStrictEqual(schema1Deps, {})
const schema2Deps = refResolver.getSchemaDependencies(schema2Id)
assert.deepStrictEqual(schema2Deps, { [schema1Id]: schema1 })
})
test('should include dependency schema only once', () => {
const refResolver = new RefResolver()
const schema1Id = 'schemaId1'
const schema1 = {
$id: schema1Id,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
const schema2Id = 'schemaId2'
const schema2 = {
$id: schema2Id,
$ref: schema1Id
}
const schema3Id = 'schemaId3'
const schema3 = {
$id: schema3Id,
allOf: [
{ $ref: schema1Id },
{ $ref: schema2Id }
]
}
refResolver.addSchema(schema1)
refResolver.addSchema(schema2)
refResolver.addSchema(schema3)
const schema1Deps = refResolver.getSchemaDependencies(schema1Id)
assert.deepStrictEqual(schema1Deps, {})
const schema2Deps = refResolver.getSchemaDependencies(schema2Id)
assert.deepStrictEqual(schema2Deps, { [schema1Id]: schema1 })
const schema3Deps = refResolver.getSchemaDependencies(schema3Id)
assert.deepStrictEqual(schema3Deps, {
[schema1Id]: schema1,
[schema2Id]: schema2
})
})

View File

@@ -0,0 +1,87 @@
'use strict'
const assert = require('node:assert/strict')
const { test } = require('node:test')
const { RefResolver } = require('../index.js')
test('should return schema refs', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
$id: 'schemaId',
type: 'object',
properties: {
foo: { $ref: 'schemaId2#/definitions/foo' },
bar: { $ref: 'schemaId3#/definitions/bar' },
baz: {
type: 'object',
properties: {
qux: { $ref: 'schemaId4#/definitions/qux' }
}
}
}
}
refResolver.addSchema(schema)
const schemaRefs = refResolver.getSchemaRefs(schemaId)
assert.deepStrictEqual(schemaRefs, [
{ schemaId: 'schemaId2', jsonPointer: '#/definitions/foo' },
{ schemaId: 'schemaId3', jsonPointer: '#/definitions/bar' },
{ schemaId: 'schemaId4', jsonPointer: '#/definitions/qux' }
])
})
test('should return nested schema refs', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const subSchemaId = 'subSchemaId'
const schema = {
$id: 'schemaId',
$ref: 'schemaId2#/definitions/subschema',
definitions: {
subschema: {
$id: subSchemaId,
type: 'object',
properties: {
foo: { $ref: 'schemaId2#/definitions/foo' },
bar: { $ref: 'schemaId3#/definitions/bar' },
baz: {
type: 'object',
properties: {
qux: { $ref: 'schemaId4#/definitions/qux' }
}
}
}
}
}
}
refResolver.addSchema(schema)
const schemaRefs = refResolver.getSchemaRefs(schemaId)
assert.deepStrictEqual(schemaRefs, [
{ schemaId: 'schemaId2', jsonPointer: '#/definitions/subschema' }
])
const subSchemaRefs = refResolver.getSchemaRefs(subSchemaId)
assert.deepStrictEqual(subSchemaRefs, [
{ schemaId: 'schemaId2', jsonPointer: '#/definitions/foo' },
{ schemaId: 'schemaId3', jsonPointer: '#/definitions/bar' },
{ schemaId: 'schemaId4', jsonPointer: '#/definitions/qux' }
])
})
test('should throw is schema does not exist', () => {
const refResolver = new RefResolver()
try {
refResolver.getSchemaRefs('schemaId')
assert.fail('should throw error')
} catch (error) {
assert.equal(error.message, 'Schema with id "schemaId" is not found.')
}
})

View File

@@ -0,0 +1,222 @@
'use strict'
const assert = require('node:assert/strict')
const { test } = require('node:test')
const { RefResolver } = require('../index.js')
test('should get schema by schema id', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
$id: schemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema)
const resolvedSchema = refResolver.getSchema(schemaId)
assert.equal(resolvedSchema, schema)
})
test('should return null if schema was not found', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
$id: schemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema)
const resolvedSchema = refResolver.getSchema(schemaId, '#/definitions/missingSchema')
assert.equal(resolvedSchema, null)
})
test('should get a sub schema by sub schema id', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const subSchemaId = 'subSchemaId'
const schema = {
$id: schemaId,
definitions: {
subSchema: {
$id: subSchemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
refResolver.addSchema(schema)
const resolvedSchema = refResolver.getSchema(subSchemaId)
assert.equal(resolvedSchema, schema.definitions.subSchema)
})
test('should get a sub schema by schema json pointer', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const subSchemaId = 'subSchemaId'
const schema = {
$id: schemaId,
definitions: {
subSchema: {
$id: subSchemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
refResolver.addSchema(schema)
const jsonPointer = '#/definitions/subSchema'
const resolvedSchema = refResolver.getSchema(schemaId, jsonPointer)
assert.equal(resolvedSchema, schema.definitions.subSchema)
})
test('should get a sub schema by sub schema json pointer', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const subSchemaId = 'subSchemaId'
const schema = {
$id: schemaId,
definitions: {
subSchema: {
$id: subSchemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
}
}
refResolver.addSchema(schema)
const jsonPointer = '#/properties/foo'
const resolvedSchema = refResolver.getSchema(subSchemaId, jsonPointer)
assert.equal(resolvedSchema, schema.definitions.subSchema.properties.foo)
})
test('should handle null schema correctly', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
$id: schemaId,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema)
const resolvedSchema = refResolver.getSchema(schemaId)
assert.equal(resolvedSchema, schema)
})
test('should add a schema without root $id', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema, schemaId)
const resolvedSchema = refResolver.getSchema(schemaId)
assert.equal(resolvedSchema, schema)
})
test('root $id has higher priority than a schemaId argument', () => {
const refResolver = new RefResolver()
const schemaIdProperty = 'schemaId1'
const schemaIdArgument = 'schemaId2'
const schema = {
$id: schemaIdProperty,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema, schemaIdArgument)
const resolvedSchema = refResolver.getSchema(schemaIdProperty)
assert.equal(resolvedSchema, schema)
try {
refResolver.getSchema(schemaIdArgument)
assert.fail('should have thrown an error')
} catch (err) {
assert.equal(
err.message,
`Cannot resolve ref "${schemaIdArgument}#". Schema with id "${schemaIdArgument}" is not found.`
)
}
})
test('should not use a root $id if it is an anchor', () => {
const refResolver = new RefResolver()
const schemaIdProperty = '#schemaId1'
const schemaIdArgument = 'schemaId2'
const schema = {
$id: schemaIdProperty,
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema, schemaIdArgument)
try {
refResolver.getSchema(schemaIdProperty)
assert.fail('should have thrown an error')
} catch (err) {
assert.equal(
err.message,
`Cannot resolve ref "${schemaIdProperty}#". Schema with id "${schemaIdProperty}" is not found.`
)
}
const resolvedSchema2 = refResolver.getSchema(schemaIdArgument)
assert.equal(resolvedSchema2, schema)
const resolvedSchema3 = refResolver.getSchema(schemaIdArgument, schemaIdProperty)
assert.equal(resolvedSchema3, schema)
})
test('should return null if sub schema by json pointer is not found', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
$id: 'schemaId',
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema)
const schemaRefs = refResolver.getSchema(schemaId, '#/missingSchema')
assert.equal(schemaRefs, null)
})

View File

@@ -0,0 +1,28 @@
'use strict'
const assert = require('node:assert/strict')
const { test } = require('node:test')
const { RefResolver } = require('../index.js')
test('should return true if schema exists', () => {
const refResolver = new RefResolver()
const schemaId = 'schemaId'
const schema = {
$id: 'schemaId',
type: 'object',
properties: {
foo: { type: 'string' }
}
}
refResolver.addSchema(schema)
const hasSchema = refResolver.hasSchema(schemaId)
assert.strictEqual(hasSchema, true)
})
test('should return false if schema does not exist', () => {
const refResolver = new RefResolver()
const hasSchema = refResolver.hasSchema('schemaId')
assert.strictEqual(hasSchema, false)
})

View File

@@ -0,0 +1,68 @@
/**
* RefResolver class is used to resolve JSON references.
* @class
* @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 });
/**
* 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;
/**
* Returns the schema by the given schema id and jsonPointer.
* If jsonPointer is not provided, returns the root schema.
* @param {string} schemaId - The schema id of the schema to be returned.
* @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;
/**
* 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;
/**
* 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 }[];
/**
* 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 };
/**
* Dereferences the schema by the given schema id.
* @param {string} schemaId - The schema id of the schema to be dereferenced.
*/
derefSchema(schemaId: string): void;
/**
* Returns the dereferenced schema by the given schema id and jsonPointer.
* If jsonPointer is not provided, returns the dereferenced root schema.
* If the schema is not dereferenced yet, dereferences it first.
* @param {string} schemaId - The schema id of the schema to be returned.
* @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;
}
export { RefResolver };

View File

@@ -0,0 +1,23 @@
import { RefResolver } from '..'
import { expectType } from 'tsd'
const resolver = new RefResolver({
allowEqualDuplicates: true
})
expectType<void>(resolver.addSchema({}))
expectType<void>(resolver.addSchema({}, 'schemaId'))
expectType<any | null>(resolver.getSchema('schemaId'))
expectType<any | null>(resolver.getSchema('schemaId', 'jsonPointer'))
expectType<boolean>(resolver.hasSchema('schemaId'))
expectType<{ schemaId: string; jsonPointer: string }[]>(resolver.getSchemaRefs('schemaId'))
expectType<{ [key: string]: any }>(resolver.getSchemaDependencies('schemaId'))
expectType<void>(resolver.derefSchema('schemaId'))
expectType<any | null>(resolver.getDerefSchema('schemaId'))
expectType<any | null>(resolver.getDerefSchema('schemaId', 'jsonPointer'))