@hyperjump/json-schema 0.20.0 → 0.21.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/README.md CHANGED
@@ -88,6 +88,43 @@ JsonSchema.setMetaOutputFormat(JsonSchema.FLAG);
88
88
  JsonSchema.setShouldMetaValidate(false);
89
89
  ```
90
90
 
91
+ ### Media Types
92
+ JSV has a plugin system for adding support for different media types. By default
93
+ it's configured to accept schemas that have the `application/schema+json`
94
+ Content-Type (web) or a `.schema.json` file extension (filesystem). If for
95
+ example, you want to fetch schemas that are written in YAML, you can add a
96
+ MediaTypePlugin to support that.
97
+
98
+ * **Core.addMediaTypePlugin**: (contentType: string, plugin: MediaTypePlugin) => void
99
+
100
+ Add a custom media type handler to support things like YAML or to change the
101
+ way JSON is supported.
102
+ * **MediaTypePlugin**: object
103
+
104
+ * parse: (response: Response) => string -- Given a fetch Response object,
105
+ parse the body of the request
106
+ * matcher: (path) => boolean -- Given a filesystem path, return whether or
107
+ not the file should be considered a member of this media type
108
+
109
+ ```javascript
110
+ const JsonSchema = require("@hyperjump/json-schema");
111
+
112
+
113
+ // Add support for JSON Schemas written in YAML
114
+ JsonSchema.addMediaTypePlugin("application/schema+yaml", {
115
+ parse: async (response) => YAML.parse(await response.text()),
116
+ matcher: (path) => path.endsWith(".schema.yaml")
117
+ });
118
+
119
+ // Example: Fetch schema with Content-Type: application/schema+yaml from the web
120
+ const schema = await JsonSchema.get("http://example.com/schemas/string");
121
+
122
+ // Example: Fetch from file with JSON Schema YAML file extension
123
+ const schema = await JsonSchema.get("file:///path/to/my/schemas/string.schema.yaml");
124
+
125
+ // Then validate against your schema like normal
126
+ ```
127
+
91
128
  ## TypeScript
92
129
  Although the package is written in JavaScript, type definitions are included for
93
130
  TypeScript support. The following example shows the types you might want to
@@ -2090,6 +2090,88 @@ define(['exports'], (function (exports) { 'use strict';
2090
2090
 
2091
2091
  var instance = { nil: nil$1, cons, uri: uri$1, value: value$1, has: has$1, typeOf: typeOf$1, step: step$1, entries: entries$3, keys: keys$1, map: map$4, filter: filter$1, reduce: reduce$3, every: every$1, some: some$1, length: length$1 };
2092
2092
 
2093
+ var entries$2 = async (doc) => Object.entries(await doc);
2094
+
2095
+ const curry$7 = justCurryIt;
2096
+
2097
+
2098
+ var map$3 = curry$7(async (fn, doc) => (await doc).map(fn));
2099
+
2100
+ const curry$6 = justCurryIt;
2101
+
2102
+
2103
+ var reduce$2 = curry$6(async (fn, acc, doc) => {
2104
+ return (await doc).reduce(async (acc, item) => fn(await acc, item), acc);
2105
+ });
2106
+
2107
+ const curry$5 = justCurryIt;
2108
+ const reduce$1 = reduce$2;
2109
+
2110
+
2111
+ var filter = curry$5(async (fn, doc, options = {}) => {
2112
+ return reduce$1(async (acc, item) => {
2113
+ return (await fn(item)) ? acc.concat([item]) : acc;
2114
+ }, [], doc, options);
2115
+ });
2116
+
2117
+ const curry$4 = justCurryIt;
2118
+ const map$2 = map$3;
2119
+
2120
+
2121
+ var some = curry$4(async (fn, doc) => {
2122
+ const results = await map$2(fn, doc);
2123
+ return (await Promise.all(results))
2124
+ .some((a) => a);
2125
+ });
2126
+
2127
+ const curry$3 = justCurryIt;
2128
+ const map$1 = map$3;
2129
+
2130
+
2131
+ var every = curry$3(async (fn, doc) => {
2132
+ const results = await map$1(fn, doc);
2133
+ return (await Promise.all(results))
2134
+ .every((a) => a);
2135
+ });
2136
+
2137
+ const curry$2 = justCurryIt;
2138
+
2139
+
2140
+ var pipeline$1 = curry$2((fns, doc) => {
2141
+ return fns.reduce(async (acc, fn) => fn(await acc), doc);
2142
+ });
2143
+
2144
+ var all = (doc) => Promise.all(doc);
2145
+
2146
+ const pipeline = pipeline$1;
2147
+ const entries$1 = entries$2;
2148
+ const reduce = reduce$2;
2149
+
2150
+
2151
+ var allValues = (doc) => {
2152
+ return pipeline([
2153
+ entries$1,
2154
+ reduce(async (acc, [propertyName, propertyValue]) => {
2155
+ acc[propertyName] = await propertyValue;
2156
+ return acc;
2157
+ }, {})
2158
+ ], doc);
2159
+ };
2160
+
2161
+ var lib$2 = {
2162
+ entries: entries$2,
2163
+ map: map$3,
2164
+ filter: filter,
2165
+ reduce: reduce$2,
2166
+ some: some,
2167
+ every: every,
2168
+ pipeline: pipeline$1,
2169
+ all: all,
2170
+ allValues: allValues
2171
+ };
2172
+
2173
+ var fetch_browser = fetch;
2174
+
2093
2175
  var contentType = {};
2094
2176
 
2095
2177
  /*!
@@ -2144,7 +2226,7 @@ define(['exports'], (function (exports) { 'use strict';
2144
2226
  */
2145
2227
 
2146
2228
  contentType.format = format$1;
2147
- contentType.parse = parse;
2229
+ contentType.parse = parse$1;
2148
2230
 
2149
2231
  /**
2150
2232
  * Format object to media type.
@@ -2195,7 +2277,7 @@ define(['exports'], (function (exports) { 'use strict';
2195
2277
  * @public
2196
2278
  */
2197
2279
 
2198
- function parse (string) {
2280
+ function parse$1 (string) {
2199
2281
  if (!string) {
2200
2282
  throw new TypeError('argument string is required')
2201
2283
  }
@@ -2313,97 +2395,47 @@ define(['exports'], (function (exports) { 'use strict';
2313
2395
  this.type = type;
2314
2396
  }
2315
2397
 
2316
- var entries$2 = async (doc) => Object.entries(await doc);
2317
-
2318
- const curry$7 = justCurryIt;
2319
-
2320
-
2321
- var map$3 = curry$7(async (fn, doc) => (await doc).map(fn));
2322
-
2323
- const curry$6 = justCurryIt;
2324
-
2325
-
2326
- var reduce$2 = curry$6(async (fn, acc, doc) => {
2327
- return (await doc).reduce(async (acc, item) => fn(await acc, item), acc);
2328
- });
2329
-
2330
- const curry$5 = justCurryIt;
2331
- const reduce$1 = reduce$2;
2332
-
2333
-
2334
- var filter = curry$5(async (fn, doc, options = {}) => {
2335
- return reduce$1(async (acc, item) => {
2336
- return (await fn(item)) ? acc.concat([item]) : acc;
2337
- }, [], doc, options);
2338
- });
2339
-
2340
- const curry$4 = justCurryIt;
2341
- const map$2 = map$3;
2342
-
2343
-
2344
- var some = curry$4(async (fn, doc) => {
2345
- const results = await map$2(fn, doc);
2346
- return (await Promise.all(results))
2347
- .some((a) => a);
2348
- });
2349
-
2350
- const curry$3 = justCurryIt;
2351
- const map$1 = map$3;
2352
-
2353
-
2354
- var every = curry$3(async (fn, doc) => {
2355
- const results = await map$1(fn, doc);
2356
- return (await Promise.all(results))
2357
- .every((a) => a);
2358
- });
2359
-
2360
- const curry$2 = justCurryIt;
2361
-
2362
-
2363
- var pipeline$1 = curry$2((fns, doc) => {
2364
- return fns.reduce(async (acc, fn) => fn(await acc), doc);
2365
- });
2398
+ const contentTypeParser = contentType;
2366
2399
 
2367
- var all = (doc) => Promise.all(doc);
2368
2400
 
2369
- const pipeline = pipeline$1;
2370
- const entries$1 = entries$2;
2371
- const reduce = reduce$2;
2401
+ const mediaTypePlugins = {};
2372
2402
 
2403
+ const addPlugin = (contentType, plugin) => {
2404
+ mediaTypePlugins[contentType] = plugin;
2405
+ };
2373
2406
 
2374
- var allValues = (doc) => {
2375
- return pipeline([
2376
- entries$1,
2377
- reduce(async (acc, [propertyName, propertyValue]) => {
2378
- acc[propertyName] = await propertyValue;
2379
- return acc;
2380
- }, {})
2381
- ], doc);
2407
+ const parse = (response) => {
2408
+ const contentType = contentTypeParser.parse(response.headers.get("content-type")).type;
2409
+ if (!(contentType in mediaTypePlugins)) {
2410
+ throw Error(`${response.url} is not a schema. Found a document with media type: ${contentType}`);
2411
+ }
2412
+ return mediaTypePlugins[contentType].parse(response);
2382
2413
  };
2383
2414
 
2384
- var lib$2 = {
2385
- entries: entries$2,
2386
- map: map$3,
2387
- filter: filter,
2388
- reduce: reduce$2,
2389
- some: some,
2390
- every: every,
2391
- pipeline: pipeline$1,
2392
- all: all,
2393
- allValues: allValues
2415
+ const getContentType = (path) => {
2416
+ for (const contentType in mediaTypePlugins) {
2417
+ if (mediaTypePlugins[contentType].matcher(path)) {
2418
+ return contentType;
2419
+ }
2420
+ }
2421
+
2422
+ return "application/octet-stream";
2394
2423
  };
2395
2424
 
2396
- var fetch_browser = fetch;
2425
+ var mediaTypes = { addPlugin, parse, getContentType };
2397
2426
 
2398
- const contentTypeParser = contentType;
2399
2427
  const curry$1 = justCurryIt;
2400
2428
  const Pact$a = lib$2;
2401
2429
  const JsonPointer = lib$3;
2402
2430
  const { jsonTypeOf, resolveUrl: resolveUrl$1, urlFragment, pathRelative } = common$1;
2403
2431
  const fetch$1 = fetch_browser;
2404
2432
  const Reference$1 = reference;
2433
+ const MediaTypes$1 = mediaTypes;
2405
2434
 
2406
2435
 
2436
+ const core201909Id = "https://json-schema.org/draft/2019-09/vocab/core";
2437
+ const core202012Id = "https://json-schema.org/draft/2020-12/vocab/core";
2438
+
2407
2439
  // Config
2408
2440
  const config = {};
2409
2441
  const dialectJsonSchemaVersion = {};
@@ -2416,10 +2448,8 @@ define(['exports'], (function (exports) { 'use strict';
2416
2448
  };
2417
2449
 
2418
2450
  const getConfig = (dialectId, key) => {
2419
- const jsonSchemaVersion = dialectJsonSchemaVersion[dialectId] || dialectId;
2420
- if (jsonSchemaVersion in config) {
2421
- return config[jsonSchemaVersion][key];
2422
- }
2451
+ const jsonSchemaVersion = dialectJsonSchemaVersion[dialectId];
2452
+ return config[jsonSchemaVersion]?.[key];
2423
2453
  };
2424
2454
 
2425
2455
  // Schema Management
@@ -2428,6 +2458,7 @@ define(['exports'], (function (exports) { 'use strict';
2428
2458
 
2429
2459
  const add$1 = (schema, url = "", defaultSchemaVersion = "") => {
2430
2460
  schema = JSON.parse(JSON.stringify(schema));
2461
+ const externalId = resolveUrl$1(url, "");
2431
2462
 
2432
2463
  // Dialect / JSON Schema Version
2433
2464
  const dialectId = resolveUrl$1(schema["$schema"] || defaultSchemaVersion, "");
@@ -2436,35 +2467,43 @@ define(['exports'], (function (exports) { 'use strict';
2436
2467
  }
2437
2468
  delete schema["$schema"];
2438
2469
 
2439
- let jsonSchemaVersion;
2440
- if (schema.$vocabulary?.["https://json-schema.org/draft/2019-09/vocab/core"] === true) {
2441
- jsonSchemaVersion = "https://json-schema.org/draft/2019-09/vocab/core";
2442
- } else if (schema.$vocabulary?.["https://json-schema.org/draft/2020-12/vocab/core"] === true) {
2443
- jsonSchemaVersion = "https://json-schema.org/draft/2020-12/vocab/core";
2444
- } else {
2445
- jsonSchemaVersion = dialectJsonSchemaVersion[dialectId] || dialectId;
2470
+ // JSON Schema version
2471
+ if (!(dialectId in dialectJsonSchemaVersion)) {
2472
+ if (schema?.$vocabulary?.[core201909Id] === true && dialectId === getSchemaIdentifier(schema, externalId, core201909Id)[0]) {
2473
+ dialectJsonSchemaVersion[dialectId] = core201909Id;
2474
+ } else if (schema?.$vocabulary?.[core202012Id] === true && dialectId === getSchemaIdentifier(schema, externalId, core202012Id)[0]) {
2475
+ dialectJsonSchemaVersion[dialectId] = core202012Id;
2476
+ } else if (dialectId === getSchemaIdentifier(schema, externalId, dialectId)[0]) {
2477
+ dialectJsonSchemaVersion[dialectId] = dialectId;
2478
+ } else if (!(dialectId in schemaStore)) {
2479
+ throw Error(`Couldn't determine JSON Schema version for dialect: '${dialectId}'`);
2480
+ } else {
2481
+ const metaSchema = schemaStore[dialectId];
2482
+ if (metaSchema.vocabulary[core201909Id] === true) {
2483
+ dialectJsonSchemaVersion[dialectId] = core201909Id;
2484
+ } else if (metaSchema.vocabulary[core202012Id] === true) {
2485
+ dialectJsonSchemaVersion[dialectId] = core202012Id;
2486
+ } else {
2487
+ dialectJsonSchemaVersion[dialectId] = dialectJsonSchemaVersion[metaSchema.dialectId];
2488
+ }
2489
+ }
2446
2490
  }
2447
2491
 
2448
- // Identifier
2449
- const baseToken = getConfig(jsonSchemaVersion, "baseToken");
2450
- const anchorToken = getConfig(jsonSchemaVersion, "anchorToken");
2451
- const externalId = resolveUrl$1(url, "");
2452
- if (!externalId && !resolveUrl$1(schema[baseToken] || "", "")) {
2492
+ // Internal Identifier
2493
+ const [id, fragment] = getSchemaIdentifier(schema, externalId, dialectJsonSchemaVersion[dialectId]);
2494
+ if (!id) {
2453
2495
  throw Error("Couldn't determine an identifier for the schema");
2454
2496
  }
2455
- const internalUrl = resolveUrl$1(externalId, schema[baseToken] || "");
2456
- const id = resolveUrl$1(internalUrl, "");
2457
- const fragment = urlFragment(internalUrl);
2497
+ const baseToken = getConfig(dialectId, "baseToken");
2458
2498
  delete schema[baseToken];
2459
- if (fragment && baseToken === anchorToken) {
2460
- schema[anchorToken] = anchorToken !== baseToken ? encodeURI(fragment) : `#${encodeURI(fragment)}`;
2461
- }
2462
2499
  if (externalId) {
2463
2500
  schemaStoreAlias[externalId] = id;
2464
2501
  }
2465
2502
 
2466
- // JSON Schema version
2467
- dialectJsonSchemaVersion[id] = jsonSchemaVersion;
2503
+ const anchorToken = getConfig(dialectId, "anchorToken");
2504
+ if (fragment && baseToken === anchorToken) {
2505
+ schema[anchorToken] = anchorToken !== baseToken ? encodeURI(fragment) : `#${encodeURI(fragment)}`;
2506
+ }
2468
2507
 
2469
2508
  // recursiveAnchor
2470
2509
  const dynamicAnchors = {};
@@ -2500,6 +2539,12 @@ define(['exports'], (function (exports) { 'use strict';
2500
2539
  return id;
2501
2540
  };
2502
2541
 
2542
+ const getSchemaIdentifier = (schema, externalId, jsonSchemaVersion) => {
2543
+ const baseToken = config[jsonSchemaVersion]?.["baseToken"];
2544
+ const internalUrl = resolveUrl$1(externalId, schema[baseToken] || "");
2545
+ return [resolveUrl$1(internalUrl, ""), urlFragment(internalUrl)];
2546
+ };
2547
+
2503
2548
  const processSchema = (subject, id, dialectId, pointer, anchors, dynamicAnchors) => {
2504
2549
  if (jsonTypeOf(subject, "object")) {
2505
2550
  const embeddedSchemaDialectId = typeof subject.$schema === "string" ? resolveUrl$1(subject.$schema, "") : dialectId;
@@ -2577,14 +2622,7 @@ define(['exports'], (function (exports) { 'use strict';
2577
2622
  throw Error(`Failed to retrieve schema with id: ${id}`);
2578
2623
  }
2579
2624
 
2580
- if (response.headers.has("content-type")) {
2581
- const contentType = contentTypeParser.parse(response.headers.get("content-type")).type;
2582
- if (contentType !== "application/schema+json") {
2583
- throw Error(`${id} is not a schema. Found a document with media type: ${contentType}`);
2584
- }
2585
- }
2586
-
2587
- add$1(await response.json(), id);
2625
+ add$1(await MediaTypes$1.parse(response), id);
2588
2626
  }
2589
2627
 
2590
2628
  const storedSchema = getStoredSchema(id);
@@ -2727,6 +2765,7 @@ define(['exports'], (function (exports) { 'use strict';
2727
2765
  const Instance$E = instance;
2728
2766
  const Schema$R = schema$5;
2729
2767
  const InvalidSchemaError$2 = invalidSchemaError;
2768
+ const MediaTypes = mediaTypes;
2730
2769
 
2731
2770
 
2732
2771
  const FLAG = "FLAG", BASIC = "BASIC", DETAILED = "DETAILED", VERBOSE = "VERBOSE";
@@ -2734,6 +2773,11 @@ define(['exports'], (function (exports) { 'use strict';
2734
2773
  let metaOutputFormat = DETAILED;
2735
2774
  let shouldMetaValidate = true;
2736
2775
 
2776
+ MediaTypes.addPlugin("application/schema+json", {
2777
+ parse: async (response) => await response.json(),
2778
+ matcher: (path) => path.endsWith(".schema.json")
2779
+ });
2780
+
2737
2781
  const validate$2 = async (schema, value = undefined, outputFormat = undefined) => {
2738
2782
  const compiled = await compile$O(schema);
2739
2783
  const interpretAst = (value, outputFormat) => interpret$O(compiled, Instance$E.cons(value), outputFormat);
@@ -2913,7 +2957,8 @@ define(['exports'], (function (exports) { 'use strict';
2913
2957
  validate: validate$2, compile: compile$O, interpret: interpret$O,
2914
2958
  setMetaOutputFormat, setShouldMetaValidate, FLAG, BASIC, DETAILED, VERBOSE,
2915
2959
  add, getKeyword, hasKeyword, defineVocabulary,
2916
- compileSchema, interpretSchema, collectEvaluatedProperties: collectEvaluatedProperties$e, collectEvaluatedItems: collectEvaluatedItems$f
2960
+ compileSchema, interpretSchema, collectEvaluatedProperties: collectEvaluatedProperties$e, collectEvaluatedItems: collectEvaluatedItems$f,
2961
+ addMediaTypePlugin: MediaTypes.addPlugin
2917
2962
  };
2918
2963
 
2919
2964
  const Schema$Q = schema$5;
@@ -5627,6 +5672,7 @@ define(['exports'], (function (exports) { 'use strict';
5627
5672
  interpret: Core.interpret,
5628
5673
  setMetaOutputFormat: Core.setMetaOutputFormat,
5629
5674
  setShouldMetaValidate: Core.setShouldMetaValidate,
5675
+ addMediaTypePlugin: Core.addMediaTypePlugin,
5630
5676
  FLAG: Core.FLAG,
5631
5677
  BASIC: Core.BASIC,
5632
5678
  DETAILED: Core.DETAILED,