@zenwave360/json-schema-ref-parser-kmp 1.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ZenWave's Authors and Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,495 @@
1
+ JSON Schema $Ref Parser for JVM and Node.js
2
+ ===========================================
3
+
4
+ [![Maven Central](https://img.shields.io/maven-central/v/io.zenwave360.jsonrefparser/json-schema-ref-parser-kmp.svg?label=Maven%20Central&logo=apachemaven)](https://search.maven.org/artifact/io.zenwave360.jsonrefparser/json-schema-ref-parser-kmp)
5
+ [![build](https://github.com/ZenWave360/json-schema-ref-parser-kmp/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/ZenWave360/json-schema-ref-parser-kmp/actions/workflows/main.yml)
6
+ [![line coverage](https://raw.githubusercontent.com/ZenWave360/json-schema-ref-parser-kmp/badges/coverage.svg)](https://github.com/ZenWave360/json-schema-ref-parser-kmp/actions/workflows/main.yml)
7
+ [![branch coverage](https://raw.githubusercontent.com/ZenWave360/json-schema-ref-parser-kmp/badges/branches.svg)](https://github.com/ZenWave360/json-schema-ref-parser-kmp/actions/workflows/main.yml)
8
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/ZenWave360/json-schema-ref-parser-kmp/blob/main/LICENSE)
9
+
10
+ Parse, resolve, and dereference JSON Schema `$ref` pointers on the JVM and Node.js.
11
+
12
+ This library is the Kotlin Multiplatform evolution of [json-schema-ref-parser-jvm](https://github.com/ZenWave360/json-schema-ref-parser-jvm). It provides:
13
+
14
+ - `RefParser` as the primary API for Kotlin and Node.js integrations
15
+ - `JavaRefParser` as the blocking JVM facade for Java and Kotlin/JVM callers
16
+ - a JVM compatibility layer for existing `$RefParser` and `$Refs` users
17
+ - JS exports for Node.js runtimes
18
+
19
+ ## Project Status
20
+
21
+ This is the library to choose for new JVM and Node.js integrations.
22
+
23
+ - New users should start with `json-schema-ref-parser-kmp`.
24
+ - Existing `json-schema-ref-parser-jvm` users can migrate incrementally using the JVM compatibility layer.
25
+ - The `$RefParser` API remains available on the JVM for compatibility, but it is not the primary API going forward.
26
+
27
+ Current status:
28
+
29
+ - The Kotlin Multiplatform core is working and is intended to match the JVM implementation behavior.
30
+ - The main API has been reshaped around `RefParser`.
31
+ - Circular reference handling has been reworked in the new implementation and still benefits from broader real-world validation.
32
+
33
+ ## The Problem
34
+
35
+ 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.
36
+
37
+ This library handles all of that for you. It is a full [JSON Reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03) and [JSON Pointer](https://tools.ietf.org/html/rfc6901) implementation that crawls even complex schemas and returns a simple object tree with source locations and resolved ref tracking.
38
+
39
+ ## Features
40
+
41
+ - Parses JSON, YAML, and Avro schemas, or any mix of them
42
+ - Dereferences `$ref` pointers into a plain object tree
43
+ - Cross-file references: local files, remote URLs, and classpath resources on the JVM
44
+ - Object identity: two `$ref` pointers to the same target resolve to the same object instance
45
+ - Source locations for every parsed node
46
+ - Original ref tracking for resolved objects
47
+ - Merges `allOf` arrays into a single object
48
+ - Non-mutating, graph-safe JSON Merge Patch utility that powers trait composition in [asyncapi-parser-kmp](https://github.com/ZenWave360/asyncapi-parser-kmp)
49
+ - Authentication headers and query parameters for remote loading
50
+ - Circular reference detection with resolve, skip, and fail modes
51
+ - Missing reference handling with skip and fail modes
52
+ - JVM and Node.js support through Kotlin Multiplatform
53
+ - JVM compatibility layer for existing `json-schema-ref-parser-jvm` users
54
+
55
+ ## Quick Start
56
+
57
+ ### Kotlin with `RefParser`
58
+
59
+ ```kotlin
60
+ val doc = RefParser("path/to/openapi.yml")
61
+ .dereference()
62
+ .mergeAllOf()
63
+ .getParsedDocument()
64
+
65
+ val schema: Map<String, Any?> = doc.schema
66
+ ```
67
+
68
+ ### Java with `JavaRefParser`
69
+
70
+ For blocking callers on the JVM, use the `JavaRefParser` facade built on top of `RefParser`.
71
+
72
+ ```java
73
+ import io.zenwave360.jsonrefparser.JavaRefParser;
74
+ import io.zenwave360.jsonrefparser.model.ParsedDocument;
75
+ import java.io.File;
76
+ import java.util.Map;
77
+
78
+ File file = new File("src/main/resources/openapi.yml");
79
+
80
+ ParsedDocument doc = JavaRefParser.from(file)
81
+ .dereference()
82
+ .mergeAllOf()
83
+ .getParsedDocument();
84
+
85
+ Map<String, Object> schema = (Map<String, Object>) doc.getSchema();
86
+ ```
87
+
88
+ ### Node.js
89
+
90
+ ```js
91
+ import { dereferenceSchema } from "@zenwave360/json-schema-ref-parser-kmp";
92
+
93
+ const doc = await dereferenceSchema("file:///workspace/openapi.yml", true);
94
+ console.log(doc.schema);
95
+ ```
96
+
97
+ ## Installation
98
+
99
+ ### Java and Kotlin on the JVM
100
+
101
+ Gradle:
102
+
103
+ ```kotlin
104
+ dependencies {
105
+ implementation("io.zenwave360.jsonrefparser:json-schema-ref-parser-kmp-jvm:<version>")
106
+ }
107
+ ```
108
+
109
+ Maven:
110
+
111
+ ```xml
112
+ <dependency>
113
+ <groupId>io.zenwave360.jsonrefparser</groupId>
114
+ <artifactId>json-schema-ref-parser-kmp-jvm</artifactId>
115
+ <version>${json-schema-ref-parser-kmp-jvm.version}</version>
116
+ </dependency>
117
+ ```
118
+
119
+ ### Kotlin Multiplatform
120
+
121
+ ```kotlin
122
+ dependencies {
123
+ implementation("io.zenwave360.jsonrefparser:json-schema-ref-parser-kmp:<version>")
124
+ }
125
+ ```
126
+
127
+ ### Node.js
128
+
129
+ The Node.js API is available from the JS target exports:
130
+
131
+ - `parseSchemaText(input, baseUri?)`
132
+ - `dereferenceSchema(uri, mergeAllOf?)`
133
+ - `dereferenceSchemaText(input, baseUri?, mergeAllOf?)`
134
+
135
+ 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.
136
+
137
+ ## Usage
138
+
139
+ ### `RefParser` Primary API
140
+
141
+ `RefParser` is the main suspend-first API for Kotlin and Node.js integrations. Blocking callers on the JVM should use `JavaRefParser`.
142
+
143
+ ### Dereference
144
+
145
+ Resolves all `$ref` pointers and replaces them inline, including references to external files and remote URLs.
146
+
147
+ ```kotlin
148
+ val doc = RefParser("path/to/schema.yml")
149
+ .dereference()
150
+ .getParsedDocument()
151
+ ```
152
+
153
+ ### Merge `allOf`
154
+
155
+ After dereferencing, merges every `allOf` array into its parent object, combining fields such as `properties` and `required`.
156
+
157
+ ```kotlin
158
+ val doc = RefParser("path/to/schema.yml")
159
+ .dereference()
160
+ .mergeAllOf()
161
+ .getParsedDocument()
162
+ ```
163
+
164
+ ### Circular references
165
+
166
+ By default, circular references are resolved by preserving object identity. You can also skip them or fail fast.
167
+
168
+ ```kotlin
169
+ val doc = RefParser(
170
+ uri = "path/to/schema.yml",
171
+ options = RefParserOptions(onCircular = OnCircular.SKIP),
172
+ ).dereference().getParsedDocument()
173
+
174
+ println(doc.hasCircularRefs) // true
175
+ ```
176
+
177
+ ### Missing references
178
+
179
+ ```kotlin
180
+ val doc = RefParser(
181
+ uri = "path/to/schema.yml",
182
+ options = RefParserOptions(onMissing = OnMissing.SKIP),
183
+ ).dereference().getParsedDocument()
184
+ ```
185
+
186
+ ### Authentication for remote files
187
+
188
+ ```kotlin
189
+ val doc = RefParser(
190
+ uri = "path/to/schema.yml",
191
+ auth = listOf(
192
+ AuthenticationValue(
193
+ key = "Authorization",
194
+ value = "Bearer <token>",
195
+ urlMatcher = { url -> url.contains("api.example.com") },
196
+ )
197
+ ),
198
+ ).dereference().getParsedDocument()
199
+ ```
200
+
201
+ ### Loader configuration
202
+
203
+ Replace the loader chain completely:
204
+
205
+ ```kotlin
206
+ val doc = RefParser("classpath:/schemas/openapi.yml")
207
+ .withLoaders(
208
+ ClasspathLoader(pluginClassLoader),
209
+ FileLoader(),
210
+ HttpLoader(),
211
+ )
212
+ .dereference()
213
+ .getParsedDocument()
214
+ ```
215
+
216
+ Patch only the default chain, replacing matching loader types and preserving the rest:
217
+
218
+ ```kotlin
219
+ val doc = RefParser("classpath:/schemas/openapi.yml")
220
+ .withDefaultLoaders(
221
+ ClasspathLoader(pluginClassLoader),
222
+ )
223
+ .dereference()
224
+ .getParsedDocument()
225
+ ```
226
+
227
+ ### Classpath loading on the JVM
228
+
229
+ ```kotlin
230
+ val doc = RefParser("classpath:/schemas/openapi.yml")
231
+ .dereference()
232
+ .getParsedDocument()
233
+ ```
234
+
235
+ ### Source locations
236
+
237
+ Every node in the parsed document carries its original file and line and column range, even after dereferencing across multiple files.
238
+
239
+ ```kotlin
240
+ val doc = RefParser("path/to/schema.yml")
241
+ .dereference()
242
+ .getParsedDocument()
243
+
244
+ val location = doc.locations["/info"]
245
+ println("${location?.file}:${location?.line}:${location?.column}")
246
+ ```
247
+
248
+ ### Original ref tracking
249
+
250
+ After dereferencing, you can look up which `$ref` string a given object came from.
251
+
252
+ ```kotlin
253
+ val doc = RefParser("path/to/schema.yml")
254
+ .dereference()
255
+ .getParsedDocument()
256
+
257
+ val ref = doc.getOriginalRef(someSchemaObject)
258
+ println(ref?.refString) // e.g. "#/components/schemas/Pet"
259
+ ```
260
+
261
+ ### Loading from a string
262
+
263
+ Useful in tests or when you already have the document text in memory.
264
+
265
+ ```kotlin
266
+ val yaml = """
267
+ type: object
268
+ properties:
269
+ name:
270
+ type: string
271
+ """.trimIndent()
272
+
273
+ val doc = RefParser.fromText(yaml).dereference().getParsedDocument()
274
+ ```
275
+
276
+ ### Java on the JVM
277
+
278
+ Blocking JVM callers should use the JVM facade:
279
+
280
+ ```java
281
+ import io.zenwave360.jsonrefparser.JavaRefParser;
282
+ import io.zenwave360.jsonrefparser.model.OnCircular;
283
+ import io.zenwave360.jsonrefparser.model.OnMissing;
284
+ import io.zenwave360.jsonrefparser.model.ParsedDocument;
285
+ import io.zenwave360.jsonrefparser.model.RefParserOptions;
286
+ import java.io.File;
287
+
288
+ File file = new File("src/main/resources/openapi.yml");
289
+
290
+ ParsedDocument doc = JavaRefParser.from(file)
291
+ .withOptions(new RefParserOptions(OnCircular.SKIP, OnMissing.FAIL))
292
+ .dereference()
293
+ .mergeAllOf()
294
+ .getParsedDocument();
295
+
296
+ System.out.println(doc.getSchema());
297
+ ```
298
+
299
+ Patch only the default classpath loader while keeping the default file and HTTP loaders:
300
+
301
+ ```java
302
+ import io.zenwave360.jsonrefparser.JavaRefParser;
303
+ import java.net.URI;
304
+
305
+ var doc = JavaRefParser.from(URI.create("classpath:catalog/service/asyncapi.yml"))
306
+ .withResourceClassLoader(pluginClassLoader)
307
+ .dereference()
308
+ .getParsedDocument();
309
+ ```
310
+
311
+ Patch the default loader chain explicitly:
312
+
313
+ ```java
314
+ import io.zenwave360.jsonrefparser.JavaRefParser;
315
+ import io.zenwave360.jsonrefparser.io.ClasspathLoader;
316
+ import java.util.Arrays;
317
+
318
+ var doc = JavaRefParser.from("classpath:catalog/service/asyncapi.yml")
319
+ .withDefaultLoaders(Arrays.asList(
320
+ new ClasspathLoader(pluginClassLoader)
321
+ ))
322
+ .dereference()
323
+ .getParsedDocument();
324
+ ```
325
+
326
+ Replace the loader chain completely:
327
+
328
+ ```java
329
+ import io.zenwave360.jsonrefparser.JavaRefParser;
330
+ import io.zenwave360.jsonrefparser.io.ClasspathLoader;
331
+ import io.zenwave360.jsonrefparser.io.FileLoader;
332
+ import io.zenwave360.jsonrefparser.io.HttpLoader;
333
+ import java.util.Arrays;
334
+
335
+ var doc = JavaRefParser.from("classpath:catalog/service/asyncapi.yml")
336
+ .withLoaders(Arrays.asList(
337
+ new ClasspathLoader(pluginClassLoader),
338
+ new FileLoader(),
339
+ new HttpLoader()
340
+ ))
341
+ .dereference()
342
+ .getParsedDocument();
343
+ ```
344
+
345
+ ### `$Ref` Compatibility API on the JVM
346
+
347
+ The JVM module still ships the legacy compatibility API for existing `json-schema-ref-parser-jvm` users:
348
+
349
+ - `$RefParser`
350
+ - `$Refs`
351
+ - `$Ref`
352
+ - `$RefParserOptions`
353
+
354
+ Use this when you want a low-friction migration path from the old JVM library. For new JVM code, prefer `JavaRefParser`.
355
+
356
+ ```java
357
+ import static io.zenwave360.jsonrefparser.$RefParserOptions.OnCircular.SKIP;
358
+
359
+ import io.zenwave360.jsonrefparser.$RefParser;
360
+ import io.zenwave360.jsonrefparser.$RefParserOptions;
361
+ import io.zenwave360.jsonrefparser.$Refs;
362
+ import java.io.File;
363
+
364
+ File file = new File("src/main/resources/openapi.yml");
365
+
366
+ $Refs refs = new $RefParser(file)
367
+ .withOptions(new $RefParserOptions().withOnCircular(SKIP))
368
+ .dereference()
369
+ .mergeAllOf()
370
+ .getRefs();
371
+
372
+ Object schema = refs.schema();
373
+ ```
374
+
375
+ ### Source locations with `$Refs`
376
+
377
+ ```java
378
+ import io.zenwave360.jsonrefparser.$RefParser;
379
+ import java.io.File;
380
+
381
+ File file = new File("src/main/resources/openapi.yml");
382
+
383
+ var range = new $RefParser(file)
384
+ .parse()
385
+ .getRefs()
386
+ .getJsonLocationRange("$.info");
387
+ ```
388
+
389
+ ### Node.js
390
+
391
+ The JS target exports plain object APIs designed for ESM runtimes in Node.js.
392
+
393
+ Dereference a file:
394
+
395
+ ```js
396
+ import { dereferenceSchema } from "@zenwave360/json-schema-ref-parser-kmp";
397
+
398
+ const doc = await dereferenceSchema("file:///workspace/openapi.yml", true);
399
+
400
+ console.log(doc.hasCircularRefs);
401
+ console.log(doc.locations["/info"]);
402
+ ```
403
+
404
+ Dereference in-memory text:
405
+
406
+ ```js
407
+ import { dereferenceSchemaText } from "@zenwave360/json-schema-ref-parser-kmp";
408
+
409
+ const yaml = `
410
+ type: object
411
+ properties:
412
+ pet:
413
+ $ref: "#/definitions/Pet"
414
+ definitions:
415
+ Pet:
416
+ type: object
417
+ properties:
418
+ name:
419
+ type: string
420
+ `;
421
+
422
+ const doc = await dereferenceSchemaText(yaml, "memory://pet.yml", true);
423
+ console.log(doc.schema);
424
+ ```
425
+
426
+ Parse without dereferencing:
427
+
428
+ ```js
429
+ import { parseSchemaText } from "@zenwave360/json-schema-ref-parser-kmp";
430
+
431
+ const doc = parseSchemaText("{\"type\":\"object\"}", "memory://schema.json");
432
+ console.log(doc.locations[""]);
433
+ ```
434
+
435
+ ### Browsers and Web Workers
436
+
437
+ The JS artifact also loads in a browser or a Web Worker. The Node.js `fs` module is resolved only when a
438
+ file is actually read, so loading the library never requires Node.js. In a browser, `FetchLoader` reads
439
+ `http(s)://` documents with the global `fetch`, and reaching a `file:` or `classpath:` URI throws
440
+ `CapabilityUnavailableException` instead of a generic error.
441
+
442
+ Supply your own loader when content is only reachable through the embedding application:
443
+
444
+ ```kotlin
445
+ val workspaceLoader = object : DocumentLoader {
446
+ override fun canLoad(uri: String) = uri.startsWith("workspace://")
447
+ override suspend fun load(uri: String): String = readFromHost(uri)
448
+ }
449
+
450
+ val doc = RefParser("workspace://specs/openapi.yml")
451
+ .withLoaders(workspaceLoader)
452
+ .dereference()
453
+ .getParsedDocument()
454
+ ```
455
+
456
+ Ask what is available before depending on it; asking never throws:
457
+
458
+ ```kotlin
459
+ RefParserPlatform.name // "jvm", "node" or "browser"
460
+ RefParserPlatform.isAvailable(RefParserPlatform.FILESYSTEM) // false in a browser
461
+ ```
462
+
463
+ ```js
464
+ import { refParserPlatform } from "@zenwave360/json-schema-ref-parser-kmp";
465
+
466
+ refParserPlatform(); // { name: "browser", capabilities: ["jsonrefparser.http"] }
467
+ ```
468
+
469
+ The build runs `jsBrowserTest` (Karma with a headless Chromium browser) as part of `check`, so a
470
+ module-level Node.js import fails the build. Set `CHROME_BIN` if Chrome, Chromium or Edge is not found
471
+ in a standard location.
472
+
473
+ ## JSON Merge Patch
474
+
475
+ The generic core module contains a non-mutating, graph-safe JSON Merge Patch utility:
476
+
477
+ ```kotlin
478
+ import io.zenwave360.jsonrefparser.merge.JsonMergePatch
479
+ import io.zenwave360.jsonrefparser.merge.jsonMergePatch
480
+
481
+ val effective = JsonMergePatch.apply(target, patch)
482
+ val equivalent = jsonMergePatch(target, patch)
483
+ ```
484
+
485
+ 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.
486
+
487
+ This is the same utility that powers trait composition in [asyncapi-parser-kmp](https://github.com/ZenWave360/asyncapi-parser-kmp).
488
+
489
+ ## Release
490
+
491
+ Maven Central publishing is handled by GitHub Actions. Repository prerequisites and the release sequence are documented in [RELEASING.md](RELEASING.md).
492
+
493
+ ## License
494
+
495
+ JSON Schema $Ref Parser KMP is free and open-source under the [MIT license](LICENSE).
@@ -0,0 +1,6 @@
1
+ type Nullable<T> = T | null | undefined
2
+ declare function KtSingleton<T>(): T & (abstract new() => any);
3
+ export declare function parseSchemaText(input: string, baseUri?: string): Nullable<any>;
4
+ export declare function refParserPlatform(): Nullable<any>;
5
+ export declare function dereferenceSchema(uri: string, mergeAllOf?: boolean): Promise<Nullable<any>>;
6
+ export declare function dereferenceSchemaText(input: string, baseUri?: string, mergeAllOf?: boolean): Promise<Nullable<any>>;