@hyperjump/json-schema 1.7.3 → 1.9.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 +61 -56
- package/annotations/annotated-instance.d.ts +4 -82
- package/annotations/annotated-instance.js +31 -33
- package/annotations/index.d.ts +7 -3
- package/annotations/index.js +8 -108
- package/draft-04/additionalItems.js +1 -1
- package/draft-04/items.js +1 -1
- package/lib/core.js +8 -50
- package/lib/experimental.d.ts +6 -7
- package/lib/experimental.js +6 -2
- package/lib/index.d.ts +5 -2
- package/lib/instance.d.ts +24 -23
- package/lib/instance.js +133 -38
- package/lib/keywords/additionalProperties.js +12 -7
- package/lib/keywords/allOf.js +7 -1
- package/lib/keywords/comment.js +4 -3
- package/lib/keywords/const.js +1 -1
- package/lib/keywords/contentEncoding.js +12 -2
- package/lib/keywords/contentMediaType.js +12 -2
- package/lib/keywords/contentSchema.js +8 -3
- package/lib/keywords/default.js +12 -2
- package/lib/keywords/dependentRequired.js +12 -3
- package/lib/keywords/dependentSchemas.js +12 -3
- package/lib/keywords/deprecated.js +12 -2
- package/lib/keywords/description.js +12 -2
- package/lib/keywords/enum.js +5 -2
- package/lib/keywords/examples.js +12 -2
- package/lib/keywords/format.js +12 -2
- package/lib/keywords/items.js +9 -6
- package/lib/keywords/maxContains.js +6 -2
- package/lib/keywords/maxItems.js +3 -1
- package/lib/keywords/maxLength.js +3 -1
- package/lib/keywords/minContains.js +6 -2
- package/lib/keywords/minItems.js +3 -1
- package/lib/keywords/minLength.js +3 -1
- package/lib/keywords/oneOf.js +0 -4
- package/lib/keywords/pattern.js +3 -1
- package/lib/keywords/patternProperties.js +18 -9
- package/lib/keywords/prefixItems.js +21 -6
- package/lib/keywords/properties.js +16 -7
- package/lib/keywords/propertyDependencies.js +18 -6
- package/lib/keywords/propertyNames.js +13 -5
- package/lib/keywords/readOnly.js +12 -2
- package/lib/keywords/title.js +12 -2
- package/lib/keywords/unevaluatedItems.js +14 -7
- package/lib/keywords/unevaluatedProperties.js +15 -8
- package/lib/keywords/uniqueItems.js +1 -1
- package/lib/keywords/unknown.js +12 -2
- package/lib/keywords/validation.js +24 -27
- package/lib/keywords/writeOnly.js +12 -2
- package/lib/keywords.js +5 -0
- package/lib/output.js +43 -0
- package/lib/schema.js +4 -2
- package/openapi-3-0/discriminator.js +12 -2
- package/openapi-3-0/example.js +12 -2
- package/openapi-3-0/externalDocs.js +12 -2
- package/openapi-3-0/index.js +2 -2
- package/openapi-3-0/nullable.js +8 -2
- package/openapi-3-0/xml.js +12 -2
- package/openapi-3-1/index.js +2 -2
- package/package.json +3 -3
- package/bundle/README.md +0 -134
- package/lib/keywords/meta-data.js +0 -7
package/README.md
CHANGED
|
@@ -518,28 +518,23 @@ These are available from the `@hyperjump/json-schema/experimental` export.
|
|
|
518
518
|
is needed for compiling sub-schemas. The `parentSchema` parameter is
|
|
519
519
|
primarily useful for looking up the value of an adjacent keyword that
|
|
520
520
|
might effect this one.
|
|
521
|
-
* interpret: (compiledKeywordValue: any, instance:
|
|
521
|
+
* interpret: (compiledKeywordValue: any, instance: JsonNode, ast: AST, dynamicAnchors: object, quiet: boolean, schemaLocation: string) => boolean
|
|
522
522
|
|
|
523
523
|
This function takes the value returned by the `compile` function and
|
|
524
524
|
the instance value that is being validated and returns whether the
|
|
525
525
|
value is valid or not. The other parameters are only needed for
|
|
526
526
|
validating sub-schemas.
|
|
527
|
-
* collectEvaluatedProperties?: (compiledKeywordValue: any, instance:
|
|
527
|
+
* collectEvaluatedProperties?: (compiledKeywordValue: any, instance: JsonNode, ast: AST, dynamicAnchors: object) => Set\<string> | false
|
|
528
528
|
|
|
529
529
|
If the keyword is an applicator, it will need to implement this
|
|
530
530
|
function for `unevaluatedProperties` to work as expected.
|
|
531
|
-
* collectEvaluatedItems?: (compiledKeywordValue: A, instance:
|
|
531
|
+
* collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonNode, ast: AST, dynamicAnchors: object) => Set\<number> | false
|
|
532
532
|
|
|
533
533
|
If the keyword is an applicator, it will need to implement this
|
|
534
534
|
function for `unevaluatedItems` to work as expected.
|
|
535
535
|
* collectExternalIds?: (visited: Set\<string>, parentSchema: Browser, schema: Browser) => Set\<string>
|
|
536
536
|
If the keyword is an applicator, it will need to implement this
|
|
537
537
|
function to work properly with the [bundle](#bundling) feature.
|
|
538
|
-
* annotation?: (compiledKeywordValue: any) => any
|
|
539
|
-
|
|
540
|
-
If the keyword is an annotation, it will need to implement this
|
|
541
|
-
function to work with the [annotation](#annotations-experimental)
|
|
542
|
-
functions.
|
|
543
538
|
* **defineVocabulary**: (id: string, keywords: { [keyword: string]: string }) => void
|
|
544
539
|
|
|
545
540
|
Define a vocabulary that maps keyword name to keyword URIs defined using
|
|
@@ -566,6 +561,10 @@ These are available from the `@hyperjump/json-schema/experimental` export.
|
|
|
566
561
|
`$vocabulary` keyword in the meta-schema. The only time you would need to
|
|
567
562
|
load a dialect manually is if you're creating a distinct version of JSON
|
|
568
563
|
Schema rather than creating a dialect of an existing version of JSON Schema.
|
|
564
|
+
* **unloadDialect**: (dialectId: string) => void
|
|
565
|
+
|
|
566
|
+
Remove a dialect. You shouldn't need to use this function. It's called for
|
|
567
|
+
you when you call `unregisterSchema`.
|
|
569
568
|
* **Validation**: Keyword
|
|
570
569
|
|
|
571
570
|
A Keyword object that represents a "validate" operation. You would use this
|
|
@@ -603,68 +602,69 @@ These are available from the `@hyperjump/json-schema/experimental` export.
|
|
|
603
602
|
|
|
604
603
|
Return a compiled schema. This is useful if you're creating tooling for
|
|
605
604
|
something other than validation.
|
|
606
|
-
* **interpret**: (schema: CompiledSchema, instance:
|
|
605
|
+
* **interpret**: (schema: CompiledSchema, instance: JsonNode, outputFormat: OutputFormat = BASIC) => OutputUnit
|
|
607
606
|
|
|
608
607
|
A curried function for validating an instance against a compiled schema.
|
|
609
608
|
This can be useful for creating custom output formats.
|
|
610
609
|
|
|
611
|
-
* **OutputFormat**: **FLAG** | **BASIC**
|
|
610
|
+
* **OutputFormat**: **FLAG** | **BASIC**
|
|
612
611
|
|
|
613
612
|
In addition to the `FLAG` output format in the Stable API, the Experimental
|
|
614
|
-
API includes support for the `BASIC
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
messages as needed.
|
|
613
|
+
API includes support for the `BASIC` format as specified in the 2019-09
|
|
614
|
+
specification (with some minor customizations). This implementation doesn't
|
|
615
|
+
include annotations or human readable error messages. The output can be
|
|
616
|
+
processed to create human readable error messages as needed.
|
|
619
617
|
|
|
620
618
|
## Instance API (experimental)
|
|
621
619
|
|
|
622
620
|
These functions are available from the
|
|
623
621
|
`@hyperjump/json-schema/instance/experimental` export.
|
|
624
622
|
|
|
625
|
-
This library uses
|
|
626
|
-
|
|
627
|
-
set of functions for working with InstanceDocuments.
|
|
623
|
+
This library uses JsonNode objects to represent instances. You'll work with
|
|
624
|
+
these objects if you create a custom keyword.
|
|
628
625
|
|
|
629
626
|
This API uses generators to iterate over arrays and objects. If you like using
|
|
630
627
|
higher order functions like `map`/`filter`/`reduce`, see
|
|
631
628
|
[`@hyperjump/pact`](https://github.com/hyperjump-io/pact) for utilities for
|
|
632
629
|
working with generators and async generators.
|
|
633
630
|
|
|
634
|
-
* **
|
|
631
|
+
* **fromJs**: (value: any, uri?: string) => JsonNode
|
|
635
632
|
|
|
636
|
-
Construct
|
|
637
|
-
* **get**: (url: string,
|
|
633
|
+
Construct a JsonNode from a JavaScript value.
|
|
634
|
+
* **get**: (url: string, instance: JsonNode) => JsonNode
|
|
638
635
|
|
|
639
|
-
Apply a same-resource reference to a
|
|
640
|
-
* **uri**: (
|
|
636
|
+
Apply a same-resource reference to a JsonNode.
|
|
637
|
+
* **uri**: (instance: JsonNode) => string
|
|
641
638
|
|
|
642
|
-
Returns a URI for the value the
|
|
643
|
-
* **value**: (
|
|
639
|
+
Returns a URI for the value the JsonNode represents.
|
|
640
|
+
* **value**: (instance: JsonNode) => any
|
|
644
641
|
|
|
645
|
-
Returns the value the
|
|
646
|
-
* **has**: (key: string,
|
|
642
|
+
Returns the value the JsonNode represents.
|
|
643
|
+
* **has**: (key: string, instance: JsonNode) => boolean
|
|
647
644
|
|
|
648
|
-
|
|
649
|
-
|
|
645
|
+
Returns whether or not "key" is a property name in a JsonNode that
|
|
646
|
+
represents an object.
|
|
647
|
+
* **typeOf**: (instance: JsonNode) => string
|
|
650
648
|
|
|
651
|
-
|
|
652
|
-
|
|
649
|
+
The JSON type of the JsonNode. In addition to the standard JSON types,
|
|
650
|
+
there's also the `property` type that indicates a property name/value pair
|
|
651
|
+
in an object.
|
|
652
|
+
* **step**: (key: string, instance: JsonNode) => JsonType
|
|
653
653
|
|
|
654
|
-
Similar to `
|
|
655
|
-
* **iter**: (
|
|
654
|
+
Similar to indexing into a object or array using the `[]` operator.
|
|
655
|
+
* **iter**: (instance: JsonNode) => Generator\<JsonNode>
|
|
656
656
|
|
|
657
|
-
Iterate over the items in the array that the
|
|
658
|
-
* **entries**: (
|
|
657
|
+
Iterate over the items in the array that the JsonNode represents.
|
|
658
|
+
* **entries**: (instance: JsonNode) => Generator\<[JsonNode, JsonNode]>
|
|
659
659
|
|
|
660
|
-
Similar to `Object.entries`, but yields
|
|
661
|
-
* **values**: (
|
|
660
|
+
Similar to `Object.entries`, but yields JsonNodes for keys and values.
|
|
661
|
+
* **values**: (instance: JsonNode) => Generator\<JsonNode>
|
|
662
662
|
|
|
663
|
-
Similar to `Object.values`, but yields
|
|
664
|
-
* **keys**: (
|
|
663
|
+
Similar to `Object.values`, but yields JsonNodes for values.
|
|
664
|
+
* **keys**: (instance: JsonNode) => Generator\<JsonNode>
|
|
665
665
|
|
|
666
|
-
Similar to `Object.keys
|
|
667
|
-
* **length**: (
|
|
666
|
+
Similar to `Object.keys`, but yields JsonNodes for keys.
|
|
667
|
+
* **length**: (instance: JsonNode) => number
|
|
668
668
|
|
|
669
669
|
Similar to `Array.prototype.length`.
|
|
670
670
|
|
|
@@ -674,12 +674,13 @@ module provides utilities for working with JSON documents annotated with JSON
|
|
|
674
674
|
Schema.
|
|
675
675
|
|
|
676
676
|
### Usage
|
|
677
|
-
An annotated JSON document is represented as
|
|
678
|
-
|
|
679
|
-
|
|
677
|
+
An annotated JSON document is represented as a
|
|
678
|
+
(JsonNode)[#/instance-api-experimental] AST. You can use this AST to traverse
|
|
679
|
+
the data structure and get annotations for the values it represents.
|
|
680
680
|
|
|
681
681
|
```javascript
|
|
682
|
-
import {
|
|
682
|
+
import { registerSchema } from "@hyperjump/json-schema/draft/2020-12";
|
|
683
|
+
import { annotate } from "@hyperjump/json-schema/annotations/experimental";
|
|
683
684
|
import * as AnnotatedInstance from "@hyperjump/json-schema/annotated-instance/experimental";
|
|
684
685
|
|
|
685
686
|
|
|
@@ -732,13 +733,14 @@ const unknowns = AnnotatedInstance.annotation(instance, "unknown", dialectId); /
|
|
|
732
733
|
const types = AnnotatedInstance.annotation(instance, "type", dialectId); // => []
|
|
733
734
|
|
|
734
735
|
// Get the title of each of the properties in the object
|
|
735
|
-
for (const [
|
|
736
|
-
|
|
736
|
+
for (const [propertyNameNode, propertyInstance] of AnnotatedInstance.entries(instance)) {
|
|
737
|
+
const propertyName = AnnotatedInstance.value(propertyName);
|
|
738
|
+
console.log(propertyName, AnnotatedInstance.annotation(propertyInstance, "title", dialectId));
|
|
737
739
|
}
|
|
738
740
|
|
|
739
741
|
// List all locations in the instance that are deprecated
|
|
740
742
|
for (const deprecated of AnnotatedInstance.annotatedWith(instance, "deprecated", dialectId)) {
|
|
741
|
-
if (AnnotatedInstance.annotation(
|
|
743
|
+
if (AnnotatedInstance.annotation(deprecated, "deprecated", dialectId)[0]) {
|
|
742
744
|
logger.warn(`The value at '${deprecated.pointer}' has been deprecated.`); // => (Example) "WARN: The value at '/name' has been deprecated."
|
|
743
745
|
}
|
|
744
746
|
}
|
|
@@ -748,13 +750,18 @@ for (const deprecated of AnnotatedInstance.annotatedWith(instance, "deprecated",
|
|
|
748
750
|
These are available from the `@hyperjump/json-schema/annotations/experimental`
|
|
749
751
|
export.
|
|
750
752
|
|
|
751
|
-
* **annotate**: (schemaUri: string, instance: any, outputFormat: OutputFormat =
|
|
753
|
+
* **annotate**: (schemaUri: string, instance: any, outputFormat: OutputFormat = BASIC) => Promise\<JsonNode>
|
|
752
754
|
|
|
753
755
|
Annotate an instance using the given schema. The function is curried to
|
|
754
756
|
allow compiling the schema once and applying it to multiple instances. This
|
|
755
757
|
may throw an [InvalidSchemaError](#api) if there is a problem with the
|
|
756
758
|
schema or a ValidationError if the instance doesn't validate against the
|
|
757
759
|
schema.
|
|
760
|
+
* **interpret**: (compiledSchema: CompiledSchema, instance: JsonNode, outputFormat: OutputFormat = BASIC) => JsonNode
|
|
761
|
+
|
|
762
|
+
Annotate a JsonNode object rather than a plain JavaScript value. This might
|
|
763
|
+
be useful when building tools on top of the annotation functionality, but
|
|
764
|
+
you probably don't need it.
|
|
758
765
|
* **ValidationError**: Error & { output: OutputUnit }
|
|
759
766
|
The `output` field contains an `OutputUnit` with information about the
|
|
760
767
|
error.
|
|
@@ -765,15 +772,13 @@ These are available from the
|
|
|
765
772
|
following functions are available in addition to the functions available in the
|
|
766
773
|
[Instance API](#instance-api-experimental).
|
|
767
774
|
|
|
768
|
-
* **annotation**: (instance:
|
|
775
|
+
* **annotation**: (instance: JsonNode, keyword: string, dialect?: string): any[];
|
|
769
776
|
|
|
770
|
-
Get the annotations for a
|
|
771
|
-
|
|
772
|
-
* **annotatedWith**: (instance: AnnotatedInstance, keyword: string, dialectId?: string) => AnnotatedInstance[]
|
|
777
|
+
Get the annotations for a keyword for the value represented by the JsonNode.
|
|
778
|
+
* **annotatedWith**: (instance: JsonNode, keyword: string, dialect?: string): JsonNode[];
|
|
773
779
|
|
|
774
|
-
Get
|
|
775
|
-
|
|
776
|
-
* **annotate**: (instance: AnnotatedInstance, keywordId: string, value: any) => AnnotatedInstance
|
|
780
|
+
Get all JsonNodes that are annotated with the given keyword.
|
|
781
|
+
* **setAnnotation**: (instance: JsonNode, keywordId: string, value: any) => JsonNode
|
|
777
782
|
|
|
778
783
|
Add an annotation to an instance. This is used internally, you probably
|
|
779
784
|
don't need it.
|
|
@@ -1,83 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export const setAnnotation: (keywordUri: string, schemaLocation: string, value: string) => void;
|
|
2
|
+
export const annotation: <A>(instance: JsonNode, keyword: string, dialectUri?: string) => A[];
|
|
3
|
+
export const annotatedWith: (instance: JsonNode, keyword: string, dialectUri?: string) => JsonNode[];
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
export const annotate: (instance: AnnotatedJsonDocument, keyword: string, value: string) => AnnotatedJsonDocument;
|
|
6
|
-
export const annotation: <A>(instance: AnnotatedJsonDocument, keyword: string, dialectId?: string) => A[];
|
|
7
|
-
export const annotatedWith: (instance: AnnotatedJsonDocument, keyword: string, dialectId?: string) => AnnotatedJsonDocument[];
|
|
8
|
-
export const nil: AnnotatedJsonDocument<undefined>;
|
|
9
|
-
export const cons: (instance: Json, id?: string) => AnnotatedJsonDocument;
|
|
10
|
-
export const get: (uri: string, context?: AnnotatedJsonDocument) => AnnotatedJsonDocument;
|
|
11
|
-
export const uri: (doc: AnnotatedJsonDocument) => string;
|
|
12
|
-
export const value: <A extends Json>(doc: AnnotatedJsonDocument<A>) => A;
|
|
13
|
-
export const has: (key: string, doc: AnnotatedJsonDocument<JsonObject>) => boolean;
|
|
14
|
-
export const typeOf: (
|
|
15
|
-
(doc: AnnotatedJsonDocument, type: "null") => doc is AnnotatedJsonDocument<null>
|
|
16
|
-
) & (
|
|
17
|
-
(doc: AnnotatedJsonDocument, type: "boolean") => doc is AnnotatedJsonDocument<boolean>
|
|
18
|
-
) & (
|
|
19
|
-
(doc: AnnotatedJsonDocument, type: "object") => doc is AnnotatedJsonDocument<JsonObject>
|
|
20
|
-
) & (
|
|
21
|
-
(doc: AnnotatedJsonDocument, type: "array") => doc is AnnotatedJsonDocument<Json[]>
|
|
22
|
-
) & (
|
|
23
|
-
(doc: AnnotatedJsonDocument, type: "number" | "integer") => doc is AnnotatedJsonDocument<number>
|
|
24
|
-
) & (
|
|
25
|
-
(doc: AnnotatedJsonDocument, type: "string") => doc is AnnotatedJsonDocument<string>
|
|
26
|
-
) & (
|
|
27
|
-
(doc: AnnotatedJsonDocument, type: JsonType) => boolean
|
|
28
|
-
) & (
|
|
29
|
-
(doc: AnnotatedJsonDocument) => (type: JsonType) => boolean
|
|
30
|
-
);
|
|
31
|
-
export const step: (key: string, doc: AnnotatedJsonDocument<JsonObject | Json[]>) => AnnotatedJsonDocument<typeof doc.value>;
|
|
32
|
-
export const entries: (doc: AnnotatedJsonDocument<JsonObject>) => [string, AnnotatedJsonDocument][];
|
|
33
|
-
export const keys: (doc: AnnotatedJsonDocument<JsonObject>) => string[];
|
|
34
|
-
export const map: (
|
|
35
|
-
<A>(fn: MapFn<A>, doc: AnnotatedJsonDocument<Json[]>) => A[]
|
|
36
|
-
) & (
|
|
37
|
-
<A>(fn: MapFn<A>) => (doc: AnnotatedJsonDocument<Json[]>) => A[]
|
|
38
|
-
);
|
|
39
|
-
export const forEach: (
|
|
40
|
-
(fn: ForEachFn, doc: AnnotatedJsonDocument<Json[]>) => void
|
|
41
|
-
) & (
|
|
42
|
-
(fn: ForEachFn) => (doc: AnnotatedJsonDocument<Json[]>) => void
|
|
43
|
-
);
|
|
44
|
-
export const filter: (
|
|
45
|
-
(fn: FilterFn, doc: AnnotatedJsonDocument<Json[]>) => AnnotatedJsonDocument[]
|
|
46
|
-
) & (
|
|
47
|
-
(fn: FilterFn) => (doc: AnnotatedJsonDocument<Json[]>) => AnnotatedJsonDocument[]
|
|
48
|
-
);
|
|
49
|
-
export const reduce: (
|
|
50
|
-
<A>(fn: ReduceFn<A>, acc: A, doc: AnnotatedJsonDocument<Json[]>) => A
|
|
51
|
-
) & (
|
|
52
|
-
<A>(fn: ReduceFn<A>) => (acc: A, doc: AnnotatedJsonDocument<Json[]>) => A
|
|
53
|
-
) & (
|
|
54
|
-
<A>(fn: ReduceFn<A>) => (acc: A) => (doc: AnnotatedJsonDocument<Json[]>) => A
|
|
55
|
-
);
|
|
56
|
-
export const every: (
|
|
57
|
-
(fn: FilterFn, doc: AnnotatedJsonDocument<Json[]>) => boolean
|
|
58
|
-
) & (
|
|
59
|
-
(fn: FilterFn) => (doc: AnnotatedJsonDocument<Json[]>) => boolean
|
|
60
|
-
);
|
|
61
|
-
export const some: (
|
|
62
|
-
(fn: FilterFn, doc: AnnotatedJsonDocument<Json[]>) => boolean
|
|
63
|
-
) & (
|
|
64
|
-
(fn: FilterFn) => (doc: AnnotatedJsonDocument<Json[]>) => boolean
|
|
65
|
-
);
|
|
66
|
-
export const length: (doc: AnnotatedJsonDocument<Json[] | string>) => number;
|
|
67
|
-
|
|
68
|
-
type MapFn<A> = (element: AnnotatedJsonDocument, index: number) => A;
|
|
69
|
-
type ForEachFn = (element: AnnotatedJsonDocument, index: number) => void;
|
|
70
|
-
type FilterFn = (element: AnnotatedJsonDocument, index: number) => boolean;
|
|
71
|
-
type ReduceFn<A> = (accumulator: A, currentValue: AnnotatedJsonDocument, index: number) => A;
|
|
72
|
-
|
|
73
|
-
export type AnnotatedJsonDocument<A extends Json | undefined = Json> = {
|
|
74
|
-
id: string;
|
|
75
|
-
pointer: string;
|
|
76
|
-
instance: Json;
|
|
77
|
-
value: A;
|
|
78
|
-
annotations: {
|
|
79
|
-
[pointer: string]: {
|
|
80
|
-
[keyword: string]: unknown[]
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
5
|
+
export * from "../lib/instance.js";
|
|
@@ -1,50 +1,48 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import * as JsonPointer from "@hyperjump/json-pointer";
|
|
2
|
+
import * as Instance from "../lib/instance.js";
|
|
3
3
|
import { getKeywordId } from "../lib/keywords.js";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
const defaultDialectId = "https://json-schema.org/validation";
|
|
7
7
|
|
|
8
|
-
export const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
value: instance
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
export const annotation = (instance, keyword, dialectId = defaultDialectId) => {
|
|
17
|
-
const keywordId = getKeywordId(keyword, dialectId);
|
|
18
|
-
return instance.annotations[instance.pointer]?.[keywordId] || [];
|
|
8
|
+
export const setAnnotation = (node, keywordUri, schemaLocation, value) => {
|
|
9
|
+
if (!(keywordUri in node.annotations)) {
|
|
10
|
+
node.annotations[keywordUri] = {};
|
|
11
|
+
}
|
|
12
|
+
node.annotations[keywordUri][schemaLocation] = value;
|
|
19
13
|
};
|
|
20
14
|
|
|
21
|
-
export const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
export const annotation = (node, keyword, dialect = defaultDialectId) => {
|
|
16
|
+
const keywordUri = getKeywordId(keyword, dialect);
|
|
17
|
+
|
|
18
|
+
let currentNode = node.root;
|
|
19
|
+
const errors = Object.keys(node.root.errors);
|
|
20
|
+
for (let segment of JsonPointer.pointerSegments(node.pointer)) {
|
|
21
|
+
segment = segment === "-" && currentNode.typeOf() === "array" ? currentNode.length() : segment;
|
|
22
|
+
currentNode = Instance.step(segment, currentNode);
|
|
23
|
+
errors.push(...Object.keys(currentNode.errors));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const annotations = [];
|
|
27
|
+
for (const schemaLocation in node.annotations[keywordUri]) {
|
|
28
|
+
if (!errors.some((error) => schemaLocation.startsWith(error))) {
|
|
29
|
+
annotations.unshift(node.annotations[keywordUri][schemaLocation]);
|
|
33
30
|
}
|
|
34
|
-
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return annotations;
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
export const annotatedWith = (instance, keyword, dialectId = defaultDialectId) => {
|
|
38
|
-
const
|
|
37
|
+
const nodes = [];
|
|
39
38
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
instances.push(get(`#${instancePointer}`, instance));
|
|
39
|
+
for (const node of Instance.allNodes(instance)) {
|
|
40
|
+
if (annotation(node, keyword, dialectId).length > 0) {
|
|
41
|
+
nodes.push(node);
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
return
|
|
45
|
+
return nodes;
|
|
48
46
|
};
|
|
49
47
|
|
|
50
|
-
export * from "../lib/instance.js";
|
|
48
|
+
export * from "../lib/instance.js";
|
package/annotations/index.d.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import type { OutputFormat, OutputUnit } from "../lib/index.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { CompiledSchema } from "../lib/experimental.js";
|
|
3
|
+
import type { JsonNode } from "../lib/json-node.js";
|
|
4
|
+
import type { Json } from "@hyperjump/json-pointer";
|
|
3
5
|
|
|
4
6
|
|
|
5
7
|
export const annotate: (
|
|
6
|
-
(schemaUrl: string, value:
|
|
8
|
+
(schemaUrl: string, value: Json, outputFormat?: OutputFormat) => Promise<JsonNode>
|
|
7
9
|
) & (
|
|
8
10
|
(schemaUrl: string) => Promise<Annotator>
|
|
9
11
|
);
|
|
10
12
|
|
|
11
|
-
export type Annotator = (value:
|
|
13
|
+
export type Annotator = (value: Json, outputFormat?: OutputFormat) => JsonNode;
|
|
14
|
+
|
|
15
|
+
export const interpret: (compiledSchema: CompiledSchema, value: JsonNode, outputFormat?: OutputFormat) => JsonNode;
|
|
12
16
|
|
|
13
17
|
export class ValidationError extends Error {
|
|
14
18
|
public output: OutputUnit;
|
package/annotations/index.js
CHANGED
|
@@ -1,123 +1,23 @@
|
|
|
1
|
-
import { subscribe, unsubscribe } from "../lib/pubsub.js";
|
|
2
|
-
import * as Instance from "./annotated-instance.js";
|
|
3
1
|
import { ValidationError } from "./validation-error.js";
|
|
4
|
-
import { getSchema,
|
|
2
|
+
import { getSchema, compile, interpret as validate, BASIC } from "../lib/experimental.js";
|
|
3
|
+
import * as Instance from "../lib/instance.js";
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
export const annotate = async (schemaUri, json = undefined, outputFormat = undefined) => {
|
|
8
|
-
loadKeywordSupport();
|
|
9
7
|
const schema = await getSchema(schemaUri);
|
|
10
8
|
const compiled = await compile(schema);
|
|
11
|
-
const interpretAst = (json, outputFormat) => interpret(compiled, Instance.
|
|
9
|
+
const interpretAst = (json, outputFormat) => interpret(compiled, Instance.fromJs(json), outputFormat);
|
|
12
10
|
|
|
13
11
|
return json === undefined ? interpretAst : interpretAst(json, outputFormat);
|
|
14
12
|
};
|
|
15
13
|
|
|
16
|
-
const interpret = ({ ast, schemaUri }, instance, outputFormat = BASIC) => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
const result = validate({ ast, schemaUri }, instance, outputFormat);
|
|
22
|
-
if (!result.valid) {
|
|
23
|
-
throw new ValidationError(result);
|
|
24
|
-
}
|
|
25
|
-
} finally {
|
|
26
|
-
unsubscribe("result", subscriptionToken);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return output[0];
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const outputHandler = (output) => {
|
|
33
|
-
let isPassing = true;
|
|
34
|
-
const instanceStack = [];
|
|
35
|
-
|
|
36
|
-
return (message, resultNode) => {
|
|
37
|
-
if (message === "result.start") {
|
|
38
|
-
instanceStack.push(output[0]);
|
|
39
|
-
isPassing = true;
|
|
40
|
-
} else if (message === "result" && isPassing) {
|
|
41
|
-
output[0] = Instance.get(resultNode.instanceLocation, output[0]);
|
|
42
|
-
|
|
43
|
-
if (resultNode.valid) {
|
|
44
|
-
const keywordHandler = getKeyword(resultNode.keyword);
|
|
45
|
-
if (keywordHandler?.annotation) {
|
|
46
|
-
const annotation = keywordHandler.annotation(resultNode.ast);
|
|
47
|
-
output[0] = Instance.annotate(output[0], resultNode.keyword, annotation);
|
|
48
|
-
}
|
|
49
|
-
} else {
|
|
50
|
-
output[0] = instanceStack[instanceStack.length - 1];
|
|
51
|
-
isPassing = false;
|
|
52
|
-
}
|
|
53
|
-
} else if (message === "result.end") {
|
|
54
|
-
instanceStack.pop();
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const identity = (a) => a;
|
|
60
|
-
|
|
61
|
-
const loadKeywordSupport = () => {
|
|
62
|
-
const title = getKeyword("https://json-schema.org/keyword/title");
|
|
63
|
-
if (title) {
|
|
64
|
-
title.annotation = identity;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const description = getKeyword("https://json-schema.org/keyword/description");
|
|
68
|
-
if (description) {
|
|
69
|
-
description.annotation = identity;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const _default = getKeyword("https://json-schema.org/keyword/default");
|
|
73
|
-
if (_default) {
|
|
74
|
-
_default.annotation = identity;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const deprecated = getKeyword("https://json-schema.org/keyword/deprecated");
|
|
78
|
-
if (deprecated) {
|
|
79
|
-
deprecated.annotation = identity;
|
|
14
|
+
export const interpret = ({ ast, schemaUri }, instance, outputFormat = BASIC) => {
|
|
15
|
+
const result = validate({ ast, schemaUri }, instance, outputFormat);
|
|
16
|
+
if (!result.valid) {
|
|
17
|
+
throw new ValidationError(result);
|
|
80
18
|
}
|
|
81
19
|
|
|
82
|
-
|
|
83
|
-
if (readOnly) {
|
|
84
|
-
readOnly.annotation = identity;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const writeOnly = getKeyword("https://json-schema.org/keyword/writeOnly");
|
|
88
|
-
if (writeOnly) {
|
|
89
|
-
writeOnly.annotation = identity;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const examples = getKeyword("https://json-schema.org/keyword/examples");
|
|
93
|
-
if (examples) {
|
|
94
|
-
examples.annotation = identity;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const format = getKeyword("https://json-schema.org/keyword/format");
|
|
98
|
-
if (format) {
|
|
99
|
-
format.annotation = identity;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const contentMediaType = getKeyword("https://json-schema.org/keyword/contentMediaType");
|
|
103
|
-
if (contentMediaType) {
|
|
104
|
-
contentMediaType.annotation = identity;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const contentEncoding = getKeyword("https://json-schema.org/keyword/contentEncoding");
|
|
108
|
-
if (contentEncoding) {
|
|
109
|
-
contentEncoding.annotation = identity;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const contentSchema = getKeyword("https://json-schema.org/keyword/contentSchema");
|
|
113
|
-
if (contentSchema) {
|
|
114
|
-
contentSchema.annotation = identity;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const unknown = getKeyword("https://json-schema.org/keyword/unknown");
|
|
118
|
-
if (unknown) {
|
|
119
|
-
unknown.annotation = identity;
|
|
120
|
-
}
|
|
20
|
+
return instance;
|
|
121
21
|
};
|
|
122
22
|
|
|
123
23
|
export { ValidationError } from "./validation-error.js";
|
|
@@ -27,7 +27,7 @@ const interpret = ([numberOfItems, additionalItems], instance, ast, dynamicAncho
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const collectEvaluatedItems = (keywordValue, instance, ast, dynamicAnchors) => {
|
|
30
|
-
if (!interpret(keywordValue, instance, ast, dynamicAnchors)) {
|
|
30
|
+
if (!interpret(keywordValue, instance, ast, dynamicAnchors, true)) {
|
|
31
31
|
return false;
|
|
32
32
|
}
|
|
33
33
|
|
package/draft-04/items.js
CHANGED
|
@@ -35,7 +35,7 @@ const interpret = (items, instance, ast, dynamicAnchors, quiet) => {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
const collectEvaluatedItems = (items, instance, ast, dynamicAnchors) => {
|
|
38
|
-
return interpret(items, instance, ast, dynamicAnchors) && (typeof items === "string"
|
|
38
|
+
return interpret(items, instance, ast, dynamicAnchors, true) && (typeof items === "string"
|
|
39
39
|
? collectSet(range(0, Instance.length(instance)))
|
|
40
40
|
: collectSet(range(0, items.length)));
|
|
41
41
|
};
|