json-schema-library 11.2.0 → 11.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-schema-library",
3
- "version": "11.2.0",
3
+ "version": "11.3.0",
4
4
  "description": "Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -39,7 +39,7 @@
39
39
  "test:7:ci": "DISABLE_LOG=true mocha -R json 'src/tests/spec/draft07.spec.ts' > test-result-spec7.json; exit 0",
40
40
  "test:inspect": "NODE_OPTIONS='--inspect-brk' mocha 'src/**/*.test.ts'",
41
41
  "test:spec": "mocha 'src/tests/spec/*.spec.ts'",
42
- "test:unit": "mocha 'src/**/*.test.ts' 'bowtie/**/*.test.ts'",
42
+ "test:unit": "mocha 'src/**/*.test.ts' 'bowtie/*.test.ts'",
43
43
  "test:unit:ci": "DISABLE_LOG=true mocha -R json 'src/**/*.test.ts' -R json > test-result-unit.json; exit 0"
44
44
  },
45
45
  "repository": {
@@ -2,6 +2,9 @@ import { compileSchema } from "./compileSchema";
2
2
  import { strict as assert } from "assert";
3
3
  import { draftEditor } from "./draftEditor";
4
4
  import { SchemaNode } from "./SchemaNode";
5
+ import { draft04 } from "./draft04";
6
+ import { draft07 } from "./draft07";
7
+ import { draft2020 } from "./draft2020";
5
8
 
6
9
  describe("compileSchema vocabulary", () => {
7
10
  let root: SchemaNode;
@@ -33,6 +36,54 @@ describe("compileSchema vocabulary", () => {
33
36
  });
34
37
  });
35
38
 
39
+ describe("compileSchema draft-version", () => {
40
+ it("should select last draft", () => {
41
+ const node = compileSchema({}, { drafts: [draft04, draft07, draft2020] });
42
+ assert(node.getDraftVersion(), "draft-2020");
43
+ });
44
+
45
+ it("should select draft specified by $schema", () => {
46
+ const node = compileSchema(
47
+ { $schema: "http://json-schema.org/draft-07/schema#" },
48
+ { drafts: [draft04, draft07, draft2020] }
49
+ );
50
+ assert(node.getDraftVersion(), "draft-07");
51
+ });
52
+
53
+ it("should select fallback draft specified by `draft`", () => {
54
+ const node = compileSchema(
55
+ {},
56
+ {
57
+ draft: "http://json-schema.org/draft-07/schema#",
58
+ drafts: [draft04, draft07, draft2020]
59
+ }
60
+ );
61
+ assert(node.getDraftVersion(), "draft-07");
62
+ });
63
+
64
+ it("should prefer `$schema` over `draft` options", () => {
65
+ const node = compileSchema(
66
+ { $schema: "http://json-schema.org/draft-04/schema#" },
67
+ {
68
+ draft: "http://json-schema.org/draft-07/schema#",
69
+ drafts: [draft04, draft07, draft2020]
70
+ }
71
+ );
72
+ assert(node.getDraftVersion(), "draft-04");
73
+ });
74
+
75
+ it("should always fallback to last draft if nothing matches", () => {
76
+ const node = compileSchema(
77
+ { $schema: "http://json-schema.org/draft-A/schema#" },
78
+ {
79
+ draft: "http://json-schema.org/draft-B/schema#",
80
+ drafts: [draft04, draft07, draft2020]
81
+ }
82
+ );
83
+ assert(node.getDraftVersion(), "draft-2020");
84
+ });
85
+ });
86
+
36
87
  describe("compileSchema vocabulary", () => {
37
88
  it("should add remote schema on compile", () => {
38
89
  const remote = compileSchema({
@@ -150,6 +201,7 @@ describe("compileSchema `schemaLocation`", () => {
150
201
  $defs: { asset: { type: "string" } }
151
202
  });
152
203
 
204
+ assert(node.if && node.then && node.properties && node.$defs);
153
205
  assert.deepEqual(node.schemaLocation, "#");
154
206
  assert.deepEqual(node.if.schemaLocation, "#/if");
155
207
  assert.deepEqual(node.then.schemaLocation, "#/then");
@@ -25,15 +25,62 @@ import sanitizeErrors from "./utils/sanitizeErrors";
25
25
  const { REGEX_FLAGS } = settings;
26
26
 
27
27
  export type CompileOptions = {
28
+ /**
29
+ * List of drafts to support.
30
+ *
31
+ * Drafts are selected by testing the passed `schema.$schema` for a matching id, which
32
+ * is tested by each draft's `Draft.$schemaRegEx`. In case no draft matches `schema.$schema`
33
+ * the last draft in the list will be used.
34
+ *
35
+ * @default [draft04, draft06, draft07, draft2019, draft2020]
36
+ *
37
+ * @example
38
+ * import { draft04, draft07, draft2020 } from "json-schema-library"
39
+ * compileSchema({ $schema: "draft-04" }, { drafts: [draft04, draft07, draft2020] })
40
+ */
28
41
  drafts?: Draft[];
42
+ /**
43
+ * Fallback _draft_ version in case no _draft_ is specified by `schema.$schema`.
44
+ *
45
+ * Drafts are selected by given `schema.$schema` or the last draft from `drafts` as a fallback.
46
+ * Specifying `draft` will workthe same as a specifying `schema.$schema` in case no $schema is
47
+ * defined. When no match can be found, the last _draft_ from `drafts` will be used.
48
+ *
49
+ * @example
50
+ * // uses draft-04
51
+ * compileSchema({ $schema: "draft-04" }, { drafts: [draft04, draft07, draft2020] })
52
+ *
53
+ * // uses draft-2020-12
54
+ * compileSchema({}, { drafts: [draft04, draft07, draft2020] })
55
+ *
56
+ * // uses draft-07
57
+ * compileSchema({}, { draft: "draft-07", drafts: [draft04, draft07, draft2020] })
58
+
59
+ * // uses draft-04
60
+ * compileSchema({ $schema: "draft-04" }, { draft: "draft-07", drafts: [draft04, draft07, draft2020] })
61
+ *
62
+ * // uses draft-2020
63
+ * compileSchema({ $schema: "draft-04" }, { draft: "draft-07", drafts: [draft2020] })
64
+ */
65
+ draft?: string;
66
+ /**
67
+ * Set node and its remote schemata as remote schemata for this node and schema to resolve $ref
68
+ */
29
69
  remote?: SchemaNode;
70
+ /**
71
+ * Enables `format`-keyword assertions when this is set tor `true` or sets assertion as defined by
72
+ * the given meta-schema. Set to `false` to deactivate format validation.
73
+ *
74
+ * @default true
75
+ */
30
76
  formatAssertion?: boolean | "meta-schema" | undefined;
77
+ /** Set default options for all `node.getData` requests */
31
78
  getDataDefaultOptions?: TemplateOptions;
32
- /** set to true to throw an Error on errors in input schema. Defaults to false */
79
+ /** Set to true to throw an Error on errors in input schema. Defaults to false */
33
80
  throwOnInvalidSchema?: boolean;
34
- /** set to true to collect unknown keywords of input schema in `node.schemaAnnotations`. Defaults to false */
81
+ /** Set to true to collect unknown keywords of input schema in `node.schemaAnnotations`. Defaults to false */
35
82
  withSchemaAnnotations?: boolean;
36
- /** set to true to throw an Error when encountering an unresolvable ref */
83
+ /** Set to true to throw an Error when encountering an unresolvable ref */
37
84
  throwOnInvalidRef?: boolean;
38
85
  };
39
86
 
@@ -51,7 +98,7 @@ function getDraft(drafts: Draft[], $schema: string) {
51
98
  export function compileSchema(schema: JsonSchema | BooleanSchema, options: CompileOptions = {}) {
52
99
  let formatAssertion = options.formatAssertion ?? true;
53
100
  const drafts = options.drafts ?? defaultDrafts;
54
- const draft = getDraft(drafts, isJsonSchema(schema) ? schema.$schema : undefined);
101
+ const draft = getDraft(drafts, isJsonSchema(schema) ? (options.draft ?? schema.$schema) : undefined);
55
102
  const node: SchemaNode & { schemaErrors?: JsonError[]; schemaAnnotations: JsonAnnotation[] } = {
56
103
  evaluationPath: "#",
57
104
  lastIdPointer: "#",
package/src/draft04.ts CHANGED
@@ -61,8 +61,8 @@ import { uniqueItemsKeyword } from "./keywords/uniqueItems";
61
61
  */
62
62
  export const draft04 = sanitizeKeywords({
63
63
  version: "draft-04",
64
- $schemaRegEx: "draft-04",
65
- $schema: "http://json-schema.org/draft-04/schema",
64
+ $schemaRegEx: "draft-?0?4",
65
+ $schema: "http://json-schema.org/draft-04/schema#",
66
66
  errors,
67
67
  formats,
68
68
  methods: {
package/src/draft06.ts CHANGED
@@ -62,8 +62,8 @@ import { uniqueItemsKeyword } from "./keywords/uniqueItems";
62
62
  */
63
63
  export const draft06 = sanitizeKeywords({
64
64
  version: "draft-06",
65
- $schemaRegEx: "draft-06",
66
- $schema: "http://json-schema.org/draft-06/schema",
65
+ $schemaRegEx: "draft-?0?6",
66
+ $schema: "http://json-schema.org/draft-06/schema#",
67
67
  errors,
68
68
  formats,
69
69
  methods: {
package/src/draft07.ts CHANGED
@@ -53,8 +53,8 @@ import { uniqueItemsKeyword } from "./keywords/uniqueItems";
53
53
  */
54
54
  export const draft07 = sanitizeKeywords({
55
55
  version: "draft-07",
56
- $schemaRegEx: "draft-07",
57
- $schema: "http://json-schema.org/draft-07/schema",
56
+ $schemaRegEx: "draft-?0?7",
57
+ $schema: "http://json-schema.org/draft-07/schema#",
58
58
  errors,
59
59
  formats,
60
60
  methods: {
@@ -1,12 +1,10 @@
1
1
  import { Keyword, JsonSchemaValidatorParams, ValidationPath } from "../../Keyword";
2
2
  import { resolveUri } from "../../utils/resolveUri";
3
3
  import splitRef from "../../utils/splitRef";
4
- import { omit } from "../../utils/omit";
5
- import { isObject } from "../../utils/isObject";
6
4
  import { validateNode } from "../../validateNode";
7
5
  import { isSchemaNode, JsonError, SchemaNode } from "../../types";
8
6
  import { get, split } from "@sagold/json-pointer";
9
- import { reduceRef } from "../../keywords/$ref";
7
+ import { reduceRef, compileNext } from "../../keywords/$ref";
10
8
 
11
9
  export const $refKeyword: Keyword = {
12
10
  id: "$ref",
@@ -132,14 +130,6 @@ function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode
132
130
  return nextNode;
133
131
  }
134
132
 
135
- function compileNext(referencedNode: SchemaNode, evaluationPath = referencedNode.evaluationPath) {
136
- const referencedSchema = isObject(referencedNode.schema)
137
- ? omit(referencedNode.schema, "$id")
138
- : referencedNode.schema;
139
-
140
- return referencedNode.compileSchema(referencedSchema, `${evaluationPath}/$ref`, referencedNode.schemaLocation);
141
- }
142
-
143
133
  export default function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode | JsonError {
144
134
  if ($ref == null) {
145
135
  return node;
@@ -147,11 +137,11 @@ export default function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode
147
137
 
148
138
  // resolve $ref by json-evaluationPath
149
139
  if (node.context.refs[$ref]) {
150
- return compileNext(node.context.refs[$ref], node.evaluationPath);
140
+ return compileNext(node.context.refs[$ref], node);
151
141
  }
152
142
  // resolve $ref from $anchor
153
143
  if (node.context.anchors[$ref]) {
154
- return compileNext(node.context.anchors[$ref], node.evaluationPath);
144
+ return compileNext(node.context.anchors[$ref], node);
155
145
  }
156
146
 
157
147
  // check for remote-host + pointer pair to switch rootSchema
@@ -171,7 +161,7 @@ export default function getRef(node: SchemaNode, $ref = node?.$ref): SchemaNode
171
161
  const $ref = fragments[0];
172
162
  // this is a reference to remote-host root node
173
163
  if (node.context.remotes[$ref]) {
174
- return compileNext(node.context.remotes[$ref], node.evaluationPath);
164
+ return compileNext(node.context.remotes[$ref], node);
175
165
  }
176
166
  if ($ref[0] === "#") {
177
167
  // @todo there is a bug joining multiple fragments to e.g. #/base#/examples/0
package/src/draft2019.ts CHANGED
@@ -60,7 +60,7 @@ import { uniqueItemsKeyword } from "./keywords/uniqueItems";
60
60
  */
61
61
  export const draft2019 = sanitizeKeywords({
62
62
  version: "draft-2019-09",
63
- $schemaRegEx: "draft[/-]2019-09",
63
+ $schemaRegEx: "draft[/-]2019-?(09)?",
64
64
  $schema: "https://json-schema.org/draft/2019-09/schema",
65
65
  errors,
66
66
  formats,
package/src/draft2020.ts CHANGED
@@ -70,7 +70,7 @@ import { uniqueItemsKeyword } from "./keywords/uniqueItems";
70
70
  */
71
71
  export const draft2020 = sanitizeKeywords({
72
72
  version: "draft-2020-12",
73
- $schemaRegEx: "draft[/-]2020-12",
73
+ $schemaRegEx: "draft[/-]2020-?(12)?",
74
74
  $schema: "https://json-schema.org/draft/2020-12/schema",
75
75
  errors,
76
76
  formats,
@@ -8,6 +8,7 @@ import { validateNode } from "../validateNode";
8
8
  import { get, split } from "@sagold/json-pointer";
9
9
  import { mergeNode } from "../mergeNode";
10
10
  import { pick } from "../utils/pick";
11
+ import settings from "src/settings";
11
12
 
12
13
  export const $refKeyword: Keyword = {
13
14
  id: "$ref",
@@ -177,14 +178,12 @@ function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode
177
178
  return getRef(node, refInCurrentScope);
178
179
  }
179
180
 
180
- const PROPERTIES_TO_MERGE = ["title", "description", "options", "readOnly", "writeOnly"];
181
-
182
- function compileNext(referencedNode: SchemaNode, sourceNode: SchemaNode) {
181
+ export function compileNext(referencedNode: SchemaNode, sourceNode: SchemaNode) {
183
182
  let referencedSchema = referencedNode.schema;
184
183
  if (isObject(referencedNode.schema)) {
185
184
  referencedSchema = {
186
185
  ...omit(referencedNode.schema, "$id"),
187
- ...pick(sourceNode.schema, ...PROPERTIES_TO_MERGE)
186
+ ...pick(sourceNode.schema, ...settings.PROPERTIES_TO_MERGE)
188
187
  };
189
188
  }
190
189
  return referencedNode.compileSchema(
@@ -483,7 +483,7 @@ describe("keyword : oneof-fuzzy : validate", () => {
483
483
  required: ["type", "children"],
484
484
  properties: {
485
485
  type: {
486
- options: { hidden: true },
486
+ "x-options": { hidden: true },
487
487
  type: "string",
488
488
  const: "parent"
489
489
  },
@@ -500,7 +500,7 @@ describe("keyword : oneof-fuzzy : validate", () => {
500
500
  required: ["type"],
501
501
  properties: {
502
502
  type: {
503
- options: { hidden: true },
503
+ "x-options": { hidden: true },
504
504
  type: "string",
505
505
  const: "one"
506
506
  }
@@ -512,7 +512,7 @@ describe("keyword : oneof-fuzzy : validate", () => {
512
512
  required: ["type"],
513
513
  properties: {
514
514
  type: {
515
- options: { hidden: true },
515
+ "x-options": { hidden: true },
516
516
  type: "string",
517
517
  const: "two"
518
518
  }
package/src/settings.ts CHANGED
@@ -17,5 +17,31 @@ export default {
17
17
  "patternProperties",
18
18
  "propertyDependencies"
19
19
  ],
20
- REGEX_FLAGS: "u"
20
+ REGEX_FLAGS: "u",
21
+ /**
22
+ * properties to keep from a $ref-schema when resolving a $ref (recursively)
23
+ * this allows to overwrite specified properties locally on a $ref-definition
24
+ *
25
+ * - draft 2019-09
26
+ * - draft 2020-12
27
+ *
28
+ * @example
29
+ * {
30
+ * title: "custom component",
31
+ * $ref: "#/$defs/component",
32
+ *
33
+ * $defs: {
34
+ * component: {
35
+ * title: "component",
36
+ * type: "object"
37
+ * }
38
+ * }
39
+ * }
40
+ * // results in
41
+ * {
42
+ * title: "custom component"
43
+ * type: "object"
44
+ * }
45
+ */
46
+ PROPERTIES_TO_MERGE: ["title", "description", "options", "x-options", "readOnly", "writeOnly"]
21
47
  };