npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zenwave360/json-schema-ref-parser-kmp

v0.9.24

Published

JSON Schema $ref parser for Kotlin Multiplatform (JVM, Node.js and browsers)

Readme

JSON Schema $Ref Parser for JVM and Node.js

Maven Central build line coverage branch coverage License

Parse, resolve, and dereference JSON Schema $ref pointers on the JVM and Node.js.

This library is the Kotlin Multiplatform evolution of json-schema-ref-parser-jvm. It provides:

  • RefParser as the primary API for Kotlin and Node.js integrations
  • JavaRefParser as the blocking JVM facade for Java and Kotlin/JVM callers
  • a JVM compatibility layer for existing $RefParser and $Refs users
  • JS exports for Node.js runtimes

Project Status

This is the library to choose for new JVM and Node.js integrations.

  • New users should start with json-schema-ref-parser-kmp.
  • Existing json-schema-ref-parser-jvm users can migrate incrementally using the JVM compatibility layer.
  • The $RefParser API remains available on the JVM for compatibility, but it is not the primary API going forward.

Current status:

  • The Kotlin Multiplatform core is working and is intended to match the JVM implementation behavior.
  • The main API has been reshaped around RefParser.
  • Circular reference handling has been reworked in the new implementation and still benefits from broader real-world validation.

The Problem

If you work with JSON Schema, OpenAPI, or AsyncAPI specifications, you know the pain: schemas spread across multiple files, $ref pointers everywhere, and yet another ad hoc parser seems inevitable.

This library handles all of that for you. It is a full JSON Reference and JSON Pointer implementation that crawls even complex schemas and returns a simple object tree with source locations and resolved ref tracking.

Features

  • Parses JSON, YAML, and Avro schemas, or any mix of them
  • Dereferences $ref pointers into a plain object tree
  • Cross-file references: local files, remote URLs, and classpath resources on the JVM
  • Object identity: two $ref pointers to the same target resolve to the same object instance
  • Source locations for every parsed node
  • Original ref tracking for resolved objects
  • Merges allOf arrays into a single object
  • Non-mutating, graph-safe JSON Merge Patch utility that powers trait composition in asyncapi-parser-kmp
  • Authentication headers and query parameters for remote loading
  • Circular reference detection with resolve, skip, and fail modes
  • Missing reference handling with skip and fail modes
  • JVM and Node.js support through Kotlin Multiplatform
  • JVM compatibility layer for existing json-schema-ref-parser-jvm users

Quick Start

Kotlin with RefParser

val doc = RefParser("path/to/openapi.yml")
    .dereference()
    .mergeAllOf()
    .getParsedDocument()

val schema: Map<String, Any?> = doc.schema

Java with JavaRefParser

For blocking callers on the JVM, use the JavaRefParser facade built on top of RefParser.

import io.zenwave360.jsonrefparser.JavaRefParser;
import io.zenwave360.jsonrefparser.model.ParsedDocument;
import java.io.File;
import java.util.Map;

File file = new File("src/main/resources/openapi.yml");

ParsedDocument doc = JavaRefParser.from(file)
        .dereference()
        .mergeAllOf()
        .getParsedDocument();

Map<String, Object> schema = (Map<String, Object>) doc.getSchema();

Node.js

import { dereferenceSchema } from "@zenwave360/json-schema-ref-parser-kmp";

const doc = await dereferenceSchema("file:///workspace/openapi.yml", true);
console.log(doc.schema);

Installation

Java and Kotlin on the JVM

Gradle:

dependencies {
    implementation("io.zenwave360.jsonrefparser:json-schema-ref-parser-kmp-jvm:<version>")
}

Maven:

<dependency>
  <groupId>io.zenwave360.jsonrefparser</groupId>
  <artifactId>json-schema-ref-parser-kmp-jvm</artifactId>
  <version>${json-schema-ref-parser-kmp-jvm.version}</version>
</dependency>

Kotlin Multiplatform

dependencies {
    implementation("io.zenwave360.jsonrefparser:json-schema-ref-parser-kmp:<version>")
}

Node.js

The Node.js API is available from the JS target exports:

  • parseSchemaText(input, baseUri?)
  • dereferenceSchema(uri, mergeAllOf?)
  • dereferenceSchemaText(input, baseUri?, mergeAllOf?)

The npm package name is @zenwave360/json-schema-ref-parser-kmp. npm publishing is not enabled yet, so this repository currently publishes Maven Central artifacts only.

Usage

RefParser Primary API

RefParser is the main suspend-first API for Kotlin and Node.js integrations. Blocking callers on the JVM should use JavaRefParser.

Dereference

Resolves all $ref pointers and replaces them inline, including references to external files and remote URLs.

val doc = RefParser("path/to/schema.yml")
    .dereference()
    .getParsedDocument()

Merge allOf

After dereferencing, merges every allOf array into its parent object, combining fields such as properties and required.

val doc = RefParser("path/to/schema.yml")
    .dereference()
    .mergeAllOf()
    .getParsedDocument()

Circular references

By default, circular references are resolved by preserving object identity. You can also skip them or fail fast.

val doc = RefParser(
    uri = "path/to/schema.yml",
    options = RefParserOptions(onCircular = OnCircular.SKIP),
).dereference().getParsedDocument()

println(doc.hasCircularRefs) // true

Missing references

val doc = RefParser(
    uri = "path/to/schema.yml",
    options = RefParserOptions(onMissing = OnMissing.SKIP),
).dereference().getParsedDocument()

Authentication for remote files

val doc = RefParser(
    uri = "path/to/schema.yml",
    auth = listOf(
        AuthenticationValue(
            key = "Authorization",
            value = "Bearer <token>",
            urlMatcher = { url -> url.contains("api.example.com") },
        )
    ),
).dereference().getParsedDocument()

Loader configuration

Replace the loader chain completely:

val doc = RefParser("classpath:/schemas/openapi.yml")
    .withLoaders(
        ClasspathLoader(pluginClassLoader),
        FileLoader(),
        HttpLoader(),
    )
    .dereference()
    .getParsedDocument()

Patch only the default chain, replacing matching loader types and preserving the rest:

val doc = RefParser("classpath:/schemas/openapi.yml")
    .withDefaultLoaders(
        ClasspathLoader(pluginClassLoader),
    )
    .dereference()
    .getParsedDocument()

Classpath loading on the JVM

val doc = RefParser("classpath:/schemas/openapi.yml")
    .dereference()
    .getParsedDocument()

Source locations

Every node in the parsed document carries its original file and line and column range, even after dereferencing across multiple files.

val doc = RefParser("path/to/schema.yml")
    .dereference()
    .getParsedDocument()

val location = doc.locations["/info"]
println("${location?.file}:${location?.line}:${location?.column}")

Original ref tracking

After dereferencing, you can look up which $ref string a given object came from.

val doc = RefParser("path/to/schema.yml")
    .dereference()
    .getParsedDocument()

val ref = doc.getOriginalRef(someSchemaObject)
println(ref?.refString) // e.g. "#/components/schemas/Pet"

Loading from a string

Useful in tests or when you already have the document text in memory.

val yaml = """
    type: object
    properties:
      name:
        type: string
""".trimIndent()

val doc = RefParser.fromText(yaml).dereference().getParsedDocument()

Java on the JVM

Blocking JVM callers should use the JVM facade:

import io.zenwave360.jsonrefparser.JavaRefParser;
import io.zenwave360.jsonrefparser.model.OnCircular;
import io.zenwave360.jsonrefparser.model.OnMissing;
import io.zenwave360.jsonrefparser.model.ParsedDocument;
import io.zenwave360.jsonrefparser.model.RefParserOptions;
import java.io.File;

File file = new File("src/main/resources/openapi.yml");

ParsedDocument doc = JavaRefParser.from(file)
        .withOptions(new RefParserOptions(OnCircular.SKIP, OnMissing.FAIL))
        .dereference()
        .mergeAllOf()
        .getParsedDocument();

System.out.println(doc.getSchema());

Patch only the default classpath loader while keeping the default file and HTTP loaders:

import io.zenwave360.jsonrefparser.JavaRefParser;
import java.net.URI;

var doc = JavaRefParser.from(URI.create("classpath:catalog/service/asyncapi.yml"))
        .withResourceClassLoader(pluginClassLoader)
        .dereference()
        .getParsedDocument();

Patch the default loader chain explicitly:

import io.zenwave360.jsonrefparser.JavaRefParser;
import io.zenwave360.jsonrefparser.io.ClasspathLoader;
import java.util.Arrays;

var doc = JavaRefParser.from("classpath:catalog/service/asyncapi.yml")
        .withDefaultLoaders(Arrays.asList(
                new ClasspathLoader(pluginClassLoader)
        ))
        .dereference()
        .getParsedDocument();

Replace the loader chain completely:

import io.zenwave360.jsonrefparser.JavaRefParser;
import io.zenwave360.jsonrefparser.io.ClasspathLoader;
import io.zenwave360.jsonrefparser.io.FileLoader;
import io.zenwave360.jsonrefparser.io.HttpLoader;
import java.util.Arrays;

var doc = JavaRefParser.from("classpath:catalog/service/asyncapi.yml")
        .withLoaders(Arrays.asList(
                new ClasspathLoader(pluginClassLoader),
                new FileLoader(),
                new HttpLoader()
        ))
        .dereference()
        .getParsedDocument();

$Ref Compatibility API on the JVM

The JVM module still ships the legacy compatibility API for existing json-schema-ref-parser-jvm users:

  • $RefParser
  • $Refs
  • $Ref
  • $RefParserOptions

Use this when you want a low-friction migration path from the old JVM library. For new JVM code, prefer JavaRefParser.

import static io.zenwave360.jsonrefparser.$RefParserOptions.OnCircular.SKIP;

import io.zenwave360.jsonrefparser.$RefParser;
import io.zenwave360.jsonrefparser.$RefParserOptions;
import io.zenwave360.jsonrefparser.$Refs;
import java.io.File;

File file = new File("src/main/resources/openapi.yml");

$Refs refs = new $RefParser(file)
        .withOptions(new $RefParserOptions().withOnCircular(SKIP))
        .dereference()
        .mergeAllOf()
        .getRefs();

Object schema = refs.schema();

Source locations with $Refs

import io.zenwave360.jsonrefparser.$RefParser;
import java.io.File;

File file = new File("src/main/resources/openapi.yml");

var range = new $RefParser(file)
        .parse()
        .getRefs()
        .getJsonLocationRange("$.info");

Node.js

The JS target exports plain object APIs designed for ESM runtimes in Node.js.

Dereference a file:

import { dereferenceSchema } from "@zenwave360/json-schema-ref-parser-kmp";

const doc = await dereferenceSchema("file:///workspace/openapi.yml", true);

console.log(doc.hasCircularRefs);
console.log(doc.locations["/info"]);

Dereference in-memory text:

import { dereferenceSchemaText } from "@zenwave360/json-schema-ref-parser-kmp";

const yaml = `
type: object
properties:
  pet:
    $ref: "#/definitions/Pet"
definitions:
  Pet:
    type: object
    properties:
      name:
        type: string
`;

const doc = await dereferenceSchemaText(yaml, "memory://pet.yml", true);
console.log(doc.schema);

Parse without dereferencing:

import { parseSchemaText } from "@zenwave360/json-schema-ref-parser-kmp";

const doc = parseSchemaText("{\"type\":\"object\"}", "memory://schema.json");
console.log(doc.locations[""]);

Browsers and Web Workers

The JS artifact also loads in a browser or a Web Worker. The Node.js fs module is resolved only when a file is actually read, so loading the library never requires Node.js. In a browser, FetchLoader reads http(s):// documents with the global fetch, and reaching a file: or classpath: URI throws CapabilityUnavailableException instead of a generic error.

Supply your own loader when content is only reachable through the embedding application:

val workspaceLoader = object : DocumentLoader {
    override fun canLoad(uri: String) = uri.startsWith("workspace://")
    override suspend fun load(uri: String): String = readFromHost(uri)
}

val doc = RefParser("workspace://specs/openapi.yml")
    .withLoaders(workspaceLoader)
    .dereference()
    .getParsedDocument()

Ask what is available before depending on it; asking never throws:

RefParserPlatform.name                                         // "jvm", "node" or "browser"
RefParserPlatform.isAvailable(RefParserPlatform.FILESYSTEM)    // false in a browser
import { refParserPlatform } from "@zenwave360/json-schema-ref-parser-kmp";

refParserPlatform(); // { name: "browser", capabilities: ["jsonrefparser.http"] }

The build runs jsBrowserTest (Karma with a headless Chromium browser) as part of check, so a module-level Node.js import fails the build. Set CHROME_BIN if Chrome, Chromium or Edge is not found in a standard location.

JSON Merge Patch

The generic core module contains a non-mutating, graph-safe JSON Merge Patch utility:

import io.zenwave360.jsonrefparser.merge.JsonMergePatch
import io.zenwave360.jsonrefparser.merge.jsonMergePatch

val effective = JsonMergePatch.apply(target, patch)
val equivalent = jsonMergePatch(target, patch)

It implements RFC 7396 semantics: object members merge recursively, null removes an object member, and arrays/scalars replace atomically. The result does not alias mutable maps or lists from either input. Circular and shared parser graphs terminate safely.

This is the same utility that powers trait composition in asyncapi-parser-kmp.

Release

Maven Central publishing is handled by GitHub Actions. Repository prerequisites and the release sequence are documented in RELEASING.md.

License

JSON Schema $Ref Parser KMP is free and open-source under the MIT license.