@hyperjump/json-schema
Advanced tools
Comparing version 1.8.0 to 1.9.0
@@ -1,83 +0,5 @@ | ||
import type { JsonType } from "../lib/common.js"; | ||
import type { Json, JsonObject } from "@hyperjump/json-pointer"; | ||
export const setAnnotation: (keywordUri: string, schemaLocation: string, value: string) => void; | ||
export const annotation: <A>(instance: JsonNode, keyword: string, dialectUri?: string) => A[]; | ||
export const annotatedWith: (instance: JsonNode, keyword: string, dialectUri?: string) => JsonNode[]; | ||
export const annotate: (instance: AnnotatedJsonDocument, keyword: string, value: string) => AnnotatedJsonDocument; | ||
export const annotation: <A>(instance: AnnotatedJsonDocument, keyword: string, dialectId?: string) => A[]; | ||
export const annotatedWith: (instance: AnnotatedJsonDocument, keyword: string, dialectId?: string) => AnnotatedJsonDocument[]; | ||
export const nil: AnnotatedJsonDocument<undefined>; | ||
export const cons: (instance: Json, id?: string) => AnnotatedJsonDocument; | ||
export const get: (uri: string, context?: AnnotatedJsonDocument) => AnnotatedJsonDocument; | ||
export const uri: (doc: AnnotatedJsonDocument) => string; | ||
export const value: <A extends Json>(doc: AnnotatedJsonDocument<A>) => A; | ||
export const has: (key: string, doc: AnnotatedJsonDocument<JsonObject>) => boolean; | ||
export const typeOf: ( | ||
(doc: AnnotatedJsonDocument, type: "null") => doc is AnnotatedJsonDocument<null> | ||
) & ( | ||
(doc: AnnotatedJsonDocument, type: "boolean") => doc is AnnotatedJsonDocument<boolean> | ||
) & ( | ||
(doc: AnnotatedJsonDocument, type: "object") => doc is AnnotatedJsonDocument<JsonObject> | ||
) & ( | ||
(doc: AnnotatedJsonDocument, type: "array") => doc is AnnotatedJsonDocument<Json[]> | ||
) & ( | ||
(doc: AnnotatedJsonDocument, type: "number" | "integer") => doc is AnnotatedJsonDocument<number> | ||
) & ( | ||
(doc: AnnotatedJsonDocument, type: "string") => doc is AnnotatedJsonDocument<string> | ||
) & ( | ||
(doc: AnnotatedJsonDocument, type: JsonType) => boolean | ||
) & ( | ||
(doc: AnnotatedJsonDocument) => (type: JsonType) => boolean | ||
); | ||
export const step: (key: string, doc: AnnotatedJsonDocument<JsonObject | Json[]>) => AnnotatedJsonDocument<typeof doc.value>; | ||
export const entries: (doc: AnnotatedJsonDocument<JsonObject>) => [string, AnnotatedJsonDocument][]; | ||
export const keys: (doc: AnnotatedJsonDocument<JsonObject>) => string[]; | ||
export const map: ( | ||
<A>(fn: MapFn<A>, doc: AnnotatedJsonDocument<Json[]>) => A[] | ||
) & ( | ||
<A>(fn: MapFn<A>) => (doc: AnnotatedJsonDocument<Json[]>) => A[] | ||
); | ||
export const forEach: ( | ||
(fn: ForEachFn, doc: AnnotatedJsonDocument<Json[]>) => void | ||
) & ( | ||
(fn: ForEachFn) => (doc: AnnotatedJsonDocument<Json[]>) => void | ||
); | ||
export const filter: ( | ||
(fn: FilterFn, doc: AnnotatedJsonDocument<Json[]>) => AnnotatedJsonDocument[] | ||
) & ( | ||
(fn: FilterFn) => (doc: AnnotatedJsonDocument<Json[]>) => AnnotatedJsonDocument[] | ||
); | ||
export const reduce: ( | ||
<A>(fn: ReduceFn<A>, acc: A, doc: AnnotatedJsonDocument<Json[]>) => A | ||
) & ( | ||
<A>(fn: ReduceFn<A>) => (acc: A, doc: AnnotatedJsonDocument<Json[]>) => A | ||
) & ( | ||
<A>(fn: ReduceFn<A>) => (acc: A) => (doc: AnnotatedJsonDocument<Json[]>) => A | ||
); | ||
export const every: ( | ||
(fn: FilterFn, doc: AnnotatedJsonDocument<Json[]>) => boolean | ||
) & ( | ||
(fn: FilterFn) => (doc: AnnotatedJsonDocument<Json[]>) => boolean | ||
); | ||
export const some: ( | ||
(fn: FilterFn, doc: AnnotatedJsonDocument<Json[]>) => boolean | ||
) & ( | ||
(fn: FilterFn) => (doc: AnnotatedJsonDocument<Json[]>) => boolean | ||
); | ||
export const length: (doc: AnnotatedJsonDocument<Json[] | string>) => number; | ||
type MapFn<A> = (element: AnnotatedJsonDocument, index: number) => A; | ||
type ForEachFn = (element: AnnotatedJsonDocument, index: number) => void; | ||
type FilterFn = (element: AnnotatedJsonDocument, index: number) => boolean; | ||
type ReduceFn<A> = (accumulator: A, currentValue: AnnotatedJsonDocument, index: number) => A; | ||
export type AnnotatedJsonDocument<A extends Json | undefined = Json> = { | ||
id: string; | ||
pointer: string; | ||
instance: Json; | ||
value: A; | ||
annotations: { | ||
[pointer: string]: { | ||
[keyword: string]: unknown[] | ||
} | ||
} | ||
}; | ||
export * from "../lib/instance.js"; |
@@ -1,3 +0,3 @@ | ||
import { toAbsoluteIri } from "@hyperjump/uri"; | ||
import { nil as nilInstance, get } from "../lib/instance.js"; | ||
import * as JsonPointer from "@hyperjump/json-pointer"; | ||
import * as Instance from "../lib/instance.js"; | ||
import { getKeywordId } from "../lib/keywords.js"; | ||
@@ -8,44 +8,42 @@ | ||
export const nil = { ...nilInstance, annotations: {} }; // eslint-disable-line import/export | ||
export const cons = (instance, id = undefined) => ({ // eslint-disable-line import/export | ||
...nil, | ||
id: id ? toAbsoluteIri(id) : "", | ||
instance, | ||
value: instance | ||
}); | ||
export const annotation = (instance, keyword, dialectId = defaultDialectId) => { | ||
const keywordId = getKeywordId(keyword, dialectId); | ||
return instance.annotations[instance.pointer]?.[keywordId] || []; | ||
export const setAnnotation = (node, keywordUri, schemaLocation, value) => { | ||
if (!(keywordUri in node.annotations)) { | ||
node.annotations[keywordUri] = {}; | ||
} | ||
node.annotations[keywordUri][schemaLocation] = value; | ||
}; | ||
export const annotate = (instance, keyword, value) => { | ||
return Object.freeze({ | ||
...instance, | ||
annotations: { | ||
...instance.annotations, | ||
[instance.pointer]: { | ||
...instance.annotations[instance.pointer], | ||
[keyword]: [ | ||
value, | ||
...instance.annotations[instance.pointer]?.[keyword] || [] | ||
] | ||
} | ||
export const annotation = (node, keyword, dialect = defaultDialectId) => { | ||
const keywordUri = getKeywordId(keyword, dialect); | ||
let currentNode = node.root; | ||
const errors = Object.keys(node.root.errors); | ||
for (let segment of JsonPointer.pointerSegments(node.pointer)) { | ||
segment = segment === "-" && currentNode.typeOf() === "array" ? currentNode.length() : segment; | ||
currentNode = Instance.step(segment, currentNode); | ||
errors.push(...Object.keys(currentNode.errors)); | ||
} | ||
const annotations = []; | ||
for (const schemaLocation in node.annotations[keywordUri]) { | ||
if (!errors.some((error) => schemaLocation.startsWith(error))) { | ||
annotations.unshift(node.annotations[keywordUri][schemaLocation]); | ||
} | ||
}); | ||
} | ||
return annotations; | ||
}; | ||
export const annotatedWith = (instance, keyword, dialectId = defaultDialectId) => { | ||
const instances = []; | ||
const nodes = []; | ||
const keywordId = getKeywordId(keyword, dialectId); | ||
for (const instancePointer in instance.annotations) { | ||
if (keywordId in instance.annotations[instancePointer]) { | ||
instances.push(get(`#${instancePointer}`, instance)); | ||
for (const node of Instance.allNodes(instance)) { | ||
if (annotation(node, keyword, dialectId).length > 0) { | ||
nodes.push(node); | ||
} | ||
} | ||
return instances; | ||
return nodes; | ||
}; | ||
export * from "../lib/instance.js"; // eslint-disable-line import/export | ||
export * from "../lib/instance.js"; |
import type { OutputFormat, OutputUnit } from "../lib/index.js"; | ||
import type { AnnotatedJsonDocument } from "./annotated-instance.js"; | ||
import type { CompiledSchema } from "../lib/experimental.js"; | ||
import type { JsonNode } from "../lib/json-node.js"; | ||
import type { Json } from "@hyperjump/json-pointer"; | ||
export const annotate: ( | ||
(schemaUrl: string, value: unknown, outputFormat?: OutputFormat) => Promise<Annotator> | ||
(schemaUrl: string, value: Json, outputFormat?: OutputFormat) => Promise<JsonNode> | ||
) & ( | ||
@@ -11,4 +13,6 @@ (schemaUrl: string) => Promise<Annotator> | ||
export type Annotator = (value: unknown, outputFormat?: OutputFormat) => AnnotatedJsonDocument; | ||
export type Annotator = (value: Json, outputFormat?: OutputFormat) => JsonNode; | ||
export const interpret: (compiledSchema: CompiledSchema, value: JsonNode, outputFormat?: OutputFormat) => JsonNode; | ||
export class ValidationError extends Error { | ||
@@ -15,0 +19,0 @@ public output: OutputUnit; |
@@ -1,12 +0,10 @@ | ||
import { subscribe, unsubscribe } from "../lib/pubsub.js"; | ||
import * as Instance from "./annotated-instance.js"; | ||
import { ValidationError } from "./validation-error.js"; | ||
import { getSchema, getKeyword, compile, interpret as validate, BASIC } from "../lib/experimental.js"; | ||
import { getSchema, compile, interpret as validate, BASIC } from "../lib/experimental.js"; | ||
import * as Instance from "../lib/instance.js"; | ||
export const annotate = async (schemaUri, json = undefined, outputFormat = undefined) => { | ||
loadKeywordSupport(); | ||
const schema = await getSchema(schemaUri); | ||
const compiled = await compile(schema); | ||
const interpretAst = (json, outputFormat) => interpret(compiled, Instance.cons(json), outputFormat); | ||
const interpretAst = (json, outputFormat) => interpret(compiled, Instance.fromJs(json), outputFormat); | ||
@@ -16,109 +14,11 @@ return json === undefined ? interpretAst : interpretAst(json, outputFormat); | ||
const interpret = ({ ast, schemaUri }, instance, outputFormat = BASIC) => { | ||
const output = [instance]; | ||
const subscriptionToken = subscribe("result", outputHandler(output)); | ||
try { | ||
const result = validate({ ast, schemaUri }, instance, outputFormat); | ||
if (!result.valid) { | ||
throw new ValidationError(result); | ||
} | ||
} finally { | ||
unsubscribe("result", subscriptionToken); | ||
export const interpret = ({ ast, schemaUri }, instance, outputFormat = BASIC) => { | ||
const result = validate({ ast, schemaUri }, instance, outputFormat); | ||
if (!result.valid) { | ||
throw new ValidationError(result); | ||
} | ||
return output[0]; | ||
return instance; | ||
}; | ||
const outputHandler = (output) => { | ||
let isPassing = true; | ||
const instanceStack = []; | ||
return (message, resultNode) => { | ||
if (message === "result.start") { | ||
instanceStack.push(output[0]); | ||
isPassing = true; | ||
} else if (message === "result" && isPassing) { | ||
output[0] = Instance.get(resultNode.instanceLocation, output[0]); | ||
if (resultNode.valid) { | ||
const keywordHandler = getKeyword(resultNode.keyword); | ||
if (keywordHandler?.annotation) { | ||
const annotation = keywordHandler.annotation(resultNode.ast); | ||
output[0] = Instance.annotate(output[0], resultNode.keyword, annotation); | ||
} | ||
} else { | ||
output[0] = instanceStack[instanceStack.length - 1]; | ||
isPassing = false; | ||
} | ||
} else if (message === "result.end") { | ||
instanceStack.pop(); | ||
} | ||
}; | ||
}; | ||
const identity = (a) => a; | ||
const loadKeywordSupport = () => { | ||
const title = getKeyword("https://json-schema.org/keyword/title"); | ||
if (title) { | ||
title.annotation = identity; | ||
} | ||
const description = getKeyword("https://json-schema.org/keyword/description"); | ||
if (description) { | ||
description.annotation = identity; | ||
} | ||
const _default = getKeyword("https://json-schema.org/keyword/default"); | ||
if (_default) { | ||
_default.annotation = identity; | ||
} | ||
const deprecated = getKeyword("https://json-schema.org/keyword/deprecated"); | ||
if (deprecated) { | ||
deprecated.annotation = identity; | ||
} | ||
const readOnly = getKeyword("https://json-schema.org/keyword/readOnly"); | ||
if (readOnly) { | ||
readOnly.annotation = identity; | ||
} | ||
const writeOnly = getKeyword("https://json-schema.org/keyword/writeOnly"); | ||
if (writeOnly) { | ||
writeOnly.annotation = identity; | ||
} | ||
const examples = getKeyword("https://json-schema.org/keyword/examples"); | ||
if (examples) { | ||
examples.annotation = identity; | ||
} | ||
const format = getKeyword("https://json-schema.org/keyword/format"); | ||
if (format) { | ||
format.annotation = identity; | ||
} | ||
const contentMediaType = getKeyword("https://json-schema.org/keyword/contentMediaType"); | ||
if (contentMediaType) { | ||
contentMediaType.annotation = identity; | ||
} | ||
const contentEncoding = getKeyword("https://json-schema.org/keyword/contentEncoding"); | ||
if (contentEncoding) { | ||
contentEncoding.annotation = identity; | ||
} | ||
const contentSchema = getKeyword("https://json-schema.org/keyword/contentSchema"); | ||
if (contentSchema) { | ||
contentSchema.annotation = identity; | ||
} | ||
const unknown = getKeyword("https://json-schema.org/keyword/unknown"); | ||
if (unknown) { | ||
unknown.annotation = identity; | ||
} | ||
}; | ||
export { ValidationError } from "./validation-error.js"; |
@@ -30,3 +30,3 @@ import { pipe, drop, every } from "@hyperjump/pact"; | ||
const collectEvaluatedItems = (keywordValue, instance, ast, dynamicAnchors) => { | ||
if (!interpret(keywordValue, instance, ast, dynamicAnchors)) { | ||
if (!interpret(keywordValue, instance, ast, dynamicAnchors, true)) { | ||
return false; | ||
@@ -33,0 +33,0 @@ } |
@@ -38,3 +38,3 @@ import { pipe, asyncMap, asyncCollectArray, every, zip, take, range, collectSet } from "@hyperjump/pact"; | ||
const collectEvaluatedItems = (items, instance, ast, dynamicAnchors) => { | ||
return interpret(items, instance, ast, dynamicAnchors) && (typeof items === "string" | ||
return interpret(items, instance, ast, dynamicAnchors, true) && (typeof items === "string" | ||
? collectSet(range(0, Instance.length(instance))) | ||
@@ -41,0 +41,0 @@ : collectSet(range(0, items.length))); |
import curry from "just-curry-it"; | ||
import { resolveIri, toAbsoluteIri } from "@hyperjump/uri"; | ||
import { subscribe, unsubscribe } from "./pubsub.js"; | ||
import { subscribe } from "./pubsub.js"; | ||
import { | ||
@@ -14,5 +14,6 @@ setMetaSchemaOutputFormat, | ||
import Validation from "./keywords/validation.js"; | ||
import { toOutputFormat } from "./output.js"; | ||
export const FLAG = "FLAG", BASIC = "BASIC", DETAILED = "DETAILED", VERBOSE = "VERBOSE"; | ||
export const FLAG = "FLAG", BASIC = "BASIC"; | ||
setMetaSchemaOutputFormat(FLAG); | ||
@@ -23,3 +24,3 @@ | ||
const compiled = await compile(schema); | ||
const interpretAst = (value, outputFormat) => interpret(compiled, Instance.cons(value), outputFormat); | ||
const interpretAst = (value, outputFormat) => interpret(compiled, Instance.fromJs(value), outputFormat); | ||
@@ -35,50 +36,7 @@ return value === undefined ? interpretAst : interpretAst(value, outputFormat); | ||
export const interpret = curry(({ ast, schemaUri }, value, outputFormat = FLAG) => { | ||
if (![FLAG, BASIC, DETAILED, VERBOSE].includes(outputFormat)) { | ||
throw Error(`The '${outputFormat}' error format is not supported`); | ||
} | ||
const output = []; | ||
const subscriptionToken = subscribe("result", outputHandler(outputFormat, output)); | ||
try { | ||
Validation.interpret(schemaUri, value, ast, {}); | ||
} finally { | ||
unsubscribe("result", subscriptionToken); | ||
} | ||
return output[0]; | ||
export const interpret = curry(({ ast, schemaUri }, instance, outputFormat = FLAG) => { | ||
Validation.interpret(schemaUri, instance, ast, {}); | ||
return toOutputFormat(instance, outputFormat); | ||
}); | ||
const outputHandler = (outputFormat, output) => { | ||
const resultStack = []; | ||
return (message, keywordResult) => { | ||
if (message === "result") { | ||
const { keyword, absoluteKeywordLocation, instanceLocation, valid } = keywordResult; | ||
const result = { keyword, absoluteKeywordLocation, instanceLocation, valid, errors: [] }; | ||
resultStack.push(result); | ||
} else if (message === "result.start") { | ||
resultStack.push(message); | ||
} else if (message === "result.end") { | ||
const result = resultStack.pop(); | ||
while (resultStack[resultStack.length - 1] !== "result.start") { | ||
const topResult = resultStack.pop(); | ||
const errors = [topResult]; | ||
if (outputFormat === BASIC) { | ||
errors.push(...topResult.errors); | ||
delete topResult.errors; | ||
} | ||
if (outputFormat === VERBOSE || (outputFormat !== FLAG && !topResult.valid)) { | ||
result.errors.unshift(...errors); | ||
} | ||
} | ||
resultStack[resultStack.length - 1] = result; | ||
output[0] = result; | ||
} | ||
}; | ||
}; | ||
const metaValidators = {}; | ||
@@ -97,3 +55,3 @@ subscribe("validate.metaValidate", async (_message, schema) => { | ||
// Interpret | ||
const schemaInstance = Instance.cons(schema.document.root, schema.document.baseUri); | ||
const schemaInstance = Instance.fromJs(schema.document.root, schema.document.baseUri); | ||
const metaResults = metaValidators[schema.document.dialectId](schemaInstance, getMetaSchemaOutputFormat()); | ||
@@ -100,0 +58,0 @@ if (!metaResults.valid) { |
import type { Browser, Document } from "@hyperjump/browser"; | ||
import type { Validator, OutputUnit, OutputFormat, SchemaObject } from "./index.js"; | ||
import type { JsonDocument } from "./instance.js"; | ||
import type { JsonNode } from "./instance.js"; | ||
// Compile/interpret | ||
export const compile: (url: string) => Promise<CompiledSchema>; | ||
export const compile: (schema: Browser<SchemaDocument>) => Promise<CompiledSchema>; | ||
export const interpret: ( | ||
@@ -35,4 +35,2 @@ (compiledSchema: CompiledSchema, value: unknown, outputFormat?: OutputFormat) => OutputUnit | ||
export const BASIC: "BASIC"; | ||
export const DETAILED: "DETAILED"; | ||
export const VERBOSE: "VERBOSE"; | ||
@@ -73,5 +71,5 @@ // Schema | ||
compile: (schema: Browser<SchemaDocument>, ast: AST, parentSchema: Browser<SchemaDocument>) => Promise<A>; | ||
interpret: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors) => boolean; | ||
collectEvaluatedProperties?: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors, isTop?: boolean) => Set<string> | false; | ||
collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors, isTop?: boolean) => Set<number> | false; | ||
interpret: (compiledKeywordValue: A, instance: JsonNode, ast: AST, dynamicAnchors: Anchors, quiet: boolean, schemaLocation: string) => boolean; | ||
collectEvaluatedProperties?: (compiledKeywordValue: A, instance: JsonNode, ast: AST, dynamicAnchors: Anchors, isTop?: boolean) => Set<string> | false; | ||
collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonNode, ast: AST, dynamicAnchors: Anchors, isTop?: boolean) => Set<number> | false; | ||
collectExternalIds?: (visited: Set<string>, parentSchema: Browser<SchemaDocument>, schema: Browser<SchemaDocument>) => Promise<Set<string>>; | ||
@@ -78,0 +76,0 @@ annotation?: <B>(compiledKeywordValue: A) => B; |
@@ -1,2 +0,2 @@ | ||
export { compile, interpret, BASIC, DETAILED, VERBOSE } from "./core.js"; | ||
export { compile, interpret, BASIC } from "./core.js"; | ||
export { | ||
@@ -3,0 +3,0 @@ addKeyword, getKeyword, getKeywordByName, getKeywordName, getKeywordId, |
@@ -0,1 +1,4 @@ | ||
import type { Json } from "@hyperjump/json-pointer"; | ||
export type SchemaFragment = string | number | boolean | null | SchemaObject | SchemaFragment[]; | ||
@@ -15,3 +18,3 @@ export type SchemaObject = { | ||
export const validate: ( | ||
(url: string, value: unknown, outputFormat?: OutputFormat) => Promise<OutputUnit> | ||
(url: string, value: Json, outputFormat?: OutputFormat) => Promise<OutputUnit> | ||
) & ( | ||
@@ -21,3 +24,3 @@ (url: string) => Promise<Validator> | ||
export type Validator = (value: unknown, outputFormat?: OutputFormat) => OutputUnit; | ||
export type Validator = (value: Json, outputFormat?: OutputFormat) => OutputUnit; | ||
@@ -24,0 +27,0 @@ export type OutputUnit = { |
@@ -1,29 +0,30 @@ | ||
import type { JsonType } from "./common.js"; | ||
import type { Json, JsonObject } from "@hyperjump/json-pointer"; | ||
import type { Json } from "@hyperjump/json-pointer"; | ||
export const nil: JsonDocument<undefined>; | ||
export const cons: (instance: Json, id?: string) => JsonDocument; | ||
export const get: (url: string, context?: JsonDocument) => JsonDocument; | ||
export const uri: (doc: JsonDocument) => string; | ||
export const value: <A extends Json>(doc: JsonDocument<A>) => A; | ||
export const has: (key: string, doc: JsonDocument<JsonObject>) => boolean; | ||
export const typeOf: (doc: JsonDocument) => JsonType; | ||
export const step: (key: string, doc: JsonDocument<JsonObject | Json[]>) => JsonDocument<typeof doc.value>; | ||
export const iter: (doc: JsonDocument<JsonObject>) => Generator<JsonDocument>; | ||
export const keys: (doc: JsonDocument<JsonObject>) => Generator<string>; | ||
export const values: (doc: JsonDocument<JsonObject>) => Generator<JsonDocument>; | ||
export const entries: (doc: JsonDocument<JsonObject>) => Generator<[string, JsonDocument]>; | ||
export const length: (doc: JsonDocument<Json[] | string>) => number; | ||
export const fromJs: (value: Json, uri?: string) => JsonNode; | ||
type MapFn<A> = (element: JsonDocument, index: number) => A; | ||
type ForEachFn = (element: JsonDocument, index: number) => void; | ||
type FilterFn = (element: JsonDocument, index: number) => boolean; | ||
type ReduceFn<A> = (accumulator: A, currentValue: JsonDocument, index: number) => A; | ||
export const get: (url: string, context: JsonNode) => JsonNode | undefined; | ||
export const uri: (node: JsonNode) => string; | ||
export const value: <A>(node: JsonNode) => A; | ||
export const has: (key: string, node: JsonNode) => boolean; | ||
export const typeOf: (node: JsonNode) => JsonType; | ||
export const step: (key: string, node: JsonNode) => JsonNode; | ||
export const iter: (node: JsonNode) => Generator<JsonNode>; | ||
export const keys: (node: JsonNode) => Generator<JsonNode>; | ||
export const values: (node: JsonNode) => Generator<JsonNode>; | ||
export const entries: (node: JsonNode) => Generator<[JsonNode, JsonNode]>; | ||
export const length: (node: JsonNode) => number; | ||
export type JsonDocument<A extends Json | undefined = Json> = { | ||
id: string; | ||
export const allNodes: (node) => Generator<JsonNode>; | ||
export type JsonNode = { | ||
baseUri: string; | ||
pointer: string; | ||
instance: Json; | ||
value: A; | ||
type: JsonNodeType; | ||
children: JsonNode[]; | ||
parent: JsonNode; | ||
root: JsonNode; | ||
valid: boolean; | ||
}; | ||
type JsonNodeType = "object" | "array" | "string" | "number" | "boolean" | "null" | "property"; |
@@ -1,63 +0,158 @@ | ||
import { append as pointerAppend, get as pointerGet } from "@hyperjump/json-pointer"; | ||
import * as JsonPointer from "@hyperjump/json-pointer"; | ||
import { reduce } from "@hyperjump/pact"; | ||
import { toAbsoluteIri } from "@hyperjump/uri"; | ||
import { jsonTypeOf, uriFragment } from "./common.js"; | ||
import { Reference } from "@hyperjump/browser/jref"; | ||
import { toAbsoluteUri, uriFragment } from "./common.js"; | ||
export const nil = { id: undefined, pointer: "", instance: undefined, value: undefined }; | ||
export const cons = (instance, id = undefined) => ({ | ||
...nil, | ||
id: id ? toAbsoluteIri(id) : "", | ||
instance, | ||
value: instance | ||
}); | ||
export const fromJs = (value, uri = "", pointer = "", parent = undefined) => { | ||
const jsType = typeof value; | ||
export const get = (url, instance = nil) => { | ||
if (!url.startsWith("#")) { | ||
throw Error(`No JSON document found at '${url.split("#")[0]}'`); | ||
switch (jsType) { | ||
case "number": | ||
case "string": | ||
case "boolean": | ||
return cons(uri, pointer, value, jsType, [], parent); | ||
case "object": | ||
if (value === null) { | ||
return cons(uri, pointer, value, "null", [], parent); | ||
} else if (Array.isArray(value)) { | ||
const arrayNode = cons(uri, pointer, value, "array", [], parent); | ||
arrayNode.children = value.map((item, index) => { | ||
return fromJs(item, uri, JsonPointer.append(index, pointer), arrayNode); | ||
}); | ||
return arrayNode; | ||
} else if (Object.getPrototypeOf(value) === Object.prototype) { | ||
const objectNode = cons(uri, pointer, value, "object", [], parent); | ||
objectNode.children = Object.entries(value).map((entry) => { | ||
const propertyPointer = JsonPointer.append(entry[0], pointer); | ||
const propertyNode = cons(uri, propertyPointer, undefined, "property", [], objectNode); | ||
propertyNode.children = entry.map((property) => fromJs(property, uri, propertyPointer, propertyNode)); | ||
return propertyNode; | ||
}); | ||
return objectNode; | ||
} else if (value instanceof Reference) { | ||
return fromJs(value.toJSON(), uri, pointer, parent); | ||
} | ||
default: | ||
const type = jsType === "object" ? Object.getPrototypeOf(value).constructor.name || "anonymous" : jsType; | ||
throw Error(`Not a JSON compatible type: ${type}`); | ||
} | ||
}; | ||
const pointer = uriFragment(url); | ||
return { | ||
...instance, | ||
const cons = (baseUri, pointer, value, type, children, parent) => { | ||
const node = { | ||
baseUri: baseUri ? toAbsoluteIri(baseUri) : "", | ||
pointer: pointer, | ||
value: pointerGet(pointer, instance.instance) | ||
value: value, | ||
type: type, | ||
children: children, | ||
parent: parent, | ||
valid: true, | ||
errors: {}, | ||
annotations: {} | ||
}; | ||
node.root = parent?.root ?? node; | ||
return node; | ||
}; | ||
export const uri = (doc) => `${doc.id || ""}#${encodeURI(doc.pointer)}`; | ||
export const value = (doc) => doc.value instanceof Reference ? doc.value.toJSON() : doc.value; | ||
export const has = (key, doc) => key in value(doc); | ||
export const typeOf = (doc) => jsonTypeOf(value(doc)); | ||
export const get = (uri, instance) => { | ||
const schemaId = toAbsoluteUri(uri); | ||
if (schemaId !== instance.baseUri && schemaId !== "") { | ||
throw Error(`Reference '${uri}' is not local to '${instance.baseUri}'`); | ||
} | ||
export const step = (key, doc) => ({ | ||
...doc, | ||
pointer: pointerAppend(key, doc.pointer), | ||
value: value(doc)[key] | ||
}); | ||
const pointer = uriFragment(uri); | ||
return reduce((node, segment) => { | ||
segment = segment === "-" && typeOf(node) === "array" ? length(node) : segment; | ||
return step(segment, node); | ||
}, instance.root, JsonPointer.pointerSegments(pointer)); | ||
}; | ||
export const iter = function* (doc) { | ||
for (let index = 0; index < value(doc).length; index++) { | ||
yield step(index, doc); | ||
export const uri = (node) => `${node.baseUri}#${encodeURI(node.pointer)}`; | ||
export const value = (node) => node.value; | ||
export const typeOf = (node) => node.type; | ||
export const has = (key, node) => key in node.value; | ||
export const step = (key, node) => { | ||
if (node.type !== "object" && node.type !== "array") { | ||
return; | ||
} | ||
switch (node.type) { | ||
case "object": | ||
const property = node.children.find((propertyNode) => { | ||
return value(propertyNode.children[0]) === key; | ||
}); | ||
return property?.children[1]; | ||
case "array": | ||
const index = parseInt(key, 10); | ||
return node.children[index]; | ||
default: | ||
return; | ||
} | ||
}; | ||
export const keys = function* (doc) { | ||
for (const key in value(doc)) { | ||
yield key; | ||
export const iter = function* (node) { | ||
if (node.type !== "array") { | ||
return; | ||
} | ||
yield* node.children; | ||
}; | ||
export const values = function* (doc) { | ||
for (const key in value(doc)) { | ||
yield step(key, doc); | ||
export const keys = function* (node) { | ||
if (node.type !== "object") { | ||
return; | ||
} | ||
for (const property of node.children) { | ||
yield property.children[0]; | ||
} | ||
}; | ||
export const entries = function* (doc) { | ||
for (const key in value(doc)) { | ||
yield [key, step(key, doc)]; | ||
export const values = function* (node) { | ||
if (node.type !== "object") { | ||
return; | ||
} | ||
for (const property of node.children) { | ||
yield property.children[1]; | ||
} | ||
}; | ||
export const length = (doc) => value(doc).length; | ||
export const entries = function* (node) { | ||
if (node.type !== "object") { | ||
return; | ||
} | ||
for (const property of node.children) { | ||
yield property.children; | ||
} | ||
}; | ||
export const length = (node) => { | ||
if (node.type !== "array") { | ||
return; | ||
} | ||
return node.children.length; | ||
}; | ||
export const allNodes = function* (node) { | ||
yield node; | ||
switch (typeOf(node)) { | ||
case "object": | ||
for (const child of values(node)) { | ||
yield* allNodes(child); | ||
} | ||
break; | ||
case "array": | ||
for (const child of iter(node)) { | ||
yield* allNodes(child); | ||
} | ||
break; | ||
} | ||
}; |
@@ -1,2 +0,2 @@ | ||
import { concat, join, empty, map, filter, every, pipe } from "@hyperjump/pact"; | ||
import { concat, join, empty, map, pipe } from "@hyperjump/pact"; | ||
import * as Browser from "@hyperjump/browser"; | ||
@@ -39,7 +39,11 @@ import * as Instance from "../instance.js"; | ||
return pipe( | ||
Instance.entries(instance), | ||
filter(([propertyName]) => !isDefinedProperty.test(propertyName)), | ||
every(([, property]) => Validation.interpret(additionalProperties, property, ast, dynamicAnchors, quiet)) | ||
); | ||
let isValid = true; | ||
for (const [propertyNameNode, property] of Instance.entries(instance)) { | ||
const propertyName = Instance.value(propertyNameNode); | ||
if (!isDefinedProperty.test(propertyName) && !Validation.interpret(additionalProperties, property, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -53,3 +57,4 @@ | ||
const evaluatedPropertyNames = new Set(); | ||
for (const [propertyName, property] of Instance.entries(instance)) { | ||
for (const [propertyNameNode, property] of Instance.entries(instance)) { | ||
const propertyName = Instance.value(propertyNameNode); | ||
if (!isDefinedProperty.test(propertyName)) { | ||
@@ -56,0 +61,0 @@ if (!Validation.interpret(additionalProperties, property, ast, dynamicAnchors, true)) { |
@@ -15,3 +15,9 @@ import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact"; | ||
const interpret = (allOf, instance, ast, dynamicAnchors, quiet) => { | ||
return allOf.every((schemaUrl) => Validation.interpret(schemaUrl, instance, ast, dynamicAnchors, quiet)); | ||
let isValid = true; | ||
for (const schemaUri of allOf) { | ||
if (!Validation.interpret(schemaUri, instance, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -18,0 +24,0 @@ |
@@ -1,4 +0,5 @@ | ||
import metaData from "./meta-data.js"; | ||
const id = "https://json-schema.org/keyword/comment"; | ||
const compile = () => undefined; | ||
const interpret = () => true; | ||
export default { id: "https://json-schema.org/keyword/comment", ...metaData }; | ||
export default { id, compile, interpret }; |
@@ -1,2 +0,2 @@ | ||
import jsonStringify from "fastest-stable-stringify"; | ||
import jsonStringify from "json-stringify-deterministic"; | ||
import * as Browser from "@hyperjump/browser"; | ||
@@ -3,0 +3,0 @@ import * as Instance from "../instance.js"; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/contentEncoding", ...metaData }; | ||
const id = "https://json-schema.org/keyword/contentEncoding"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (contentEncoding, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, contentEncoding); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/contentMediaType", ...metaData }; | ||
const id = "https://json-schema.org/keyword/contentMediaType"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (contentMediaType, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, contentMediaType); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,2 +0,3 @@ | ||
import * as Schema from "../schema.js"; | ||
import { canonicalUri } from "../schema.js"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
@@ -6,5 +7,9 @@ | ||
const compile = (contentSchema) => Schema.canonicalUri(contentSchema); | ||
const interpret = () => true; | ||
const compile = (contentSchema) => canonicalUri(contentSchema); | ||
const interpret = (contentSchema, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, contentSchema); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/default", ...metaData }; | ||
const id = "https://json-schema.org/keyword/default"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (value, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, value); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -15,7 +15,16 @@ import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact"; | ||
const interpret = (dependentRequired, instance) => { | ||
return Instance.typeOf(instance) !== "object" || dependentRequired.every(([propertyName, required]) => { | ||
return !Instance.has(propertyName, instance) || required.every((key) => Instance.has(key, instance)); | ||
}); | ||
if (Instance.typeOf(instance) !== "object") { | ||
return true; | ||
} | ||
let isValid = true; | ||
for (const [propertyName, required] of dependentRequired) { | ||
if (Instance.has(propertyName, instance) && !required.every((key) => Instance.has(key, instance))) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -16,5 +16,14 @@ import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact"; | ||
const interpret = (dependentSchemas, instance, ast, dynamicAnchors, quiet) => { | ||
return Instance.typeOf(instance) !== "object" || dependentSchemas.every(([propertyName, dependentSchema]) => { | ||
return !Instance.has(propertyName, instance) || Validation.interpret(dependentSchema, instance, ast, dynamicAnchors, quiet); | ||
}); | ||
if (Instance.typeOf(instance) !== "object") { | ||
return true; | ||
} | ||
let isValid = true; | ||
for (const [propertyName, dependentSchema] of dependentSchemas) { | ||
if (Instance.has(propertyName, instance) && !Validation.interpret(dependentSchema, instance, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -21,0 +30,0 @@ |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/deprecated", ...metaData }; | ||
const id = "https://json-schema.org/keyword/deprecated"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (deprecated, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, deprecated); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/description", ...metaData }; | ||
const id = "https://json-schema.org/keyword/description"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (description, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, description); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,2 +0,2 @@ | ||
import jsonStringify from "fastest-stable-stringify"; | ||
import jsonStringify from "json-stringify-deterministic"; | ||
import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact"; | ||
@@ -16,4 +16,7 @@ import * as Browser from "@hyperjump/browser"; | ||
const interpret = (enum_, instance) => enum_.some((enumValue) => jsonStringify(Instance.value(instance)) === enumValue); | ||
const interpret = (enum_, instance) => { | ||
const instanceValue = jsonStringify(Instance.value(instance)); | ||
return enum_.some((enumValue) => instanceValue === enumValue); | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/examples", ...metaData }; | ||
const id = "https://json-schema.org/keyword/examples"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (examples, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, examples); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/format", ...metaData }; | ||
const id = "https://json-schema.org/keyword/format"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (format, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, format); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,2 +0,2 @@ | ||
import { pipe, drop, every } from "@hyperjump/pact"; | ||
import { drop } from "@hyperjump/pact"; | ||
import * as Browser from "@hyperjump/browser"; | ||
@@ -22,7 +22,10 @@ import * as Instance from "../instance.js"; | ||
return pipe( | ||
Instance.iter(instance), | ||
drop(numberOfPrefixItems), | ||
every((item) => Validation.interpret(items, item, ast, dynamicAnchors, quiet)) | ||
); | ||
let isValid = true; | ||
for (const item of drop(numberOfPrefixItems, Instance.iter(instance))) { | ||
if (!Validation.interpret(items, item, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -29,0 +32,0 @@ |
@@ -1,4 +0,8 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
export default { id: "https://json-schema.org/keyword/maxContains", ...metaData }; | ||
const id = "https://json-schema.org/keyword/maxContains"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = () => true; | ||
export default { id, compile, interpret }; |
@@ -8,4 +8,6 @@ import * as Browser from "@hyperjump/browser"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (maxItems, instance) => Instance.typeOf(instance) !== "array" || Instance.length(instance) <= maxItems; | ||
const interpret = (maxItems, instance) => { | ||
return Instance.typeOf(instance) !== "array" || Instance.length(instance) <= maxItems; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -8,4 +8,6 @@ import * as Browser from "@hyperjump/browser"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (maxLength, instance) => Instance.typeOf(instance) !== "string" || [...Instance.value(instance)].length <= maxLength; | ||
const interpret = (maxLength, instance) => { | ||
return Instance.typeOf(instance) !== "string" || [...Instance.value(instance)].length <= maxLength; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,8 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
export default { id: "https://json-schema.org/keyword/minContains", ...metaData }; | ||
const id = "https://json-schema.org/keyword/minContains"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = () => true; | ||
export default { id, compile, interpret }; |
@@ -8,4 +8,6 @@ import * as Browser from "@hyperjump/browser"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (minItems, instance) => Instance.typeOf(instance) !== "array" || Instance.length(instance) >= minItems; | ||
const interpret = (minItems, instance) => { | ||
return Instance.typeOf(instance) !== "array" || Instance.length(instance) >= minItems; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -8,4 +8,6 @@ import * as Browser from "@hyperjump/browser"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (minLength, instance) => Instance.typeOf(instance) !== "string" || [...Instance.value(instance)].length >= minLength; | ||
const interpret = (minLength, instance) => { | ||
return Instance.typeOf(instance) !== "string" || [...Instance.value(instance)].length >= minLength; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -20,6 +20,2 @@ import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact"; | ||
} | ||
if (validCount > 1) { | ||
break; | ||
} | ||
} | ||
@@ -26,0 +22,0 @@ |
@@ -8,4 +8,6 @@ import * as Browser from "@hyperjump/browser"; | ||
const compile = (schema) => new RegExp(Browser.value(schema), "u"); | ||
const interpret = (pattern, instance) => Instance.typeOf(instance) !== "string" || pattern.test(Instance.value(instance)); | ||
const interpret = (pattern, instance) => { | ||
return Instance.typeOf(instance) !== "string" || pattern.test(Instance.value(instance)); | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,2 +0,2 @@ | ||
import { pipe, asyncMap, asyncCollectArray, filter, every } from "@hyperjump/pact"; | ||
import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact"; | ||
import * as Browser from "@hyperjump/browser"; | ||
@@ -19,9 +19,17 @@ import * as Instance from "../instance.js"; | ||
const interpret = (patternProperties, instance, ast, dynamicAnchors, quiet) => { | ||
return Instance.typeOf(instance) !== "object" || patternProperties.every(([pattern, schemaUrl]) => { | ||
return pipe( | ||
Instance.entries(instance), | ||
filter(([propertyName]) => pattern.test(propertyName)), | ||
every(([, propertyValue]) => Validation.interpret(schemaUrl, propertyValue, ast, dynamicAnchors, quiet)) | ||
); | ||
}); | ||
if (Instance.typeOf(instance) !== "object") { | ||
return true; | ||
} | ||
let isValid = true; | ||
for (const [pattern, schemaUri] of patternProperties) { | ||
for (const [propertyNameNode, propertyValue] of Instance.entries(instance)) { | ||
const propertyName = Instance.value(propertyNameNode); | ||
if (pattern.test(propertyName) && !Validation.interpret(schemaUri, propertyValue, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -36,3 +44,4 @@ | ||
for (const [pattern, propertySchema] of patternProperties) { | ||
for (const [propertyName, property] of Instance.entries(instance)) { | ||
for (const [propertyNameNode, property] of Instance.entries(instance)) { | ||
const propertyName = Instance.value(propertyNameNode); | ||
if (pattern.test(propertyName)) { | ||
@@ -39,0 +48,0 @@ if (!Validation.interpret(propertySchema, property, ast, dynamicAnchors, true)) { |
@@ -1,2 +0,2 @@ | ||
import { pipe, asyncMap, asyncCollectArray, every, zip, take } from "@hyperjump/pact"; | ||
import { pipe, asyncMap, asyncCollectArray, zip } from "@hyperjump/pact"; | ||
import * as Browser from "@hyperjump/browser"; | ||
@@ -16,7 +16,22 @@ import * as Instance from "../instance.js"; | ||
const interpret = (prefixItems, instance, ast, dynamicAnchors, quiet) => { | ||
return Instance.typeOf(instance) !== "array" || pipe( | ||
zip(prefixItems, Instance.iter(instance)), | ||
take(Instance.length(instance)), | ||
every(([prefixItem, item]) => Validation.interpret(prefixItem, item, ast, dynamicAnchors, quiet)) | ||
); | ||
if (Instance.typeOf(instance) !== "array") { | ||
return true; | ||
} | ||
let isValid = true; | ||
let index = 0; | ||
const instanceLength = Instance.length(instance); | ||
for (const [schemaUri, item] of zip(prefixItems, Instance.iter(instance))) { | ||
if (index >= instanceLength) { | ||
break; | ||
} | ||
if (!Validation.interpret(schemaUri, item, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
index++; | ||
} | ||
return isValid; | ||
}; | ||
@@ -23,0 +38,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { pipe, asyncMap, asyncCollectObject, filter, every } from "@hyperjump/pact"; | ||
import { pipe, asyncMap, asyncCollectObject } from "@hyperjump/pact"; | ||
import * as Browser from "@hyperjump/browser"; | ||
@@ -16,7 +16,15 @@ import * as Instance from "../instance.js"; | ||
const interpret = (properties, instance, ast, dynamicAnchors, quiet) => { | ||
return Instance.typeOf(instance) !== "object" || pipe( | ||
Instance.entries(instance), | ||
filter(([propertyName]) => propertyName in properties), | ||
every(([propertyName, property]) => Validation.interpret(properties[propertyName], property, ast, dynamicAnchors, quiet)) | ||
); | ||
if (Instance.typeOf(instance) !== "object") { | ||
return true; | ||
} | ||
let isValid = true; | ||
for (const [propertyNameNode, property] of Instance.entries(instance)) { | ||
const propertyName = Instance.value(propertyNameNode); | ||
if (propertyName in properties && !Validation.interpret(properties[propertyName], property, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -30,3 +38,4 @@ | ||
const evaluatedPropertyNames = new Set(); | ||
for (const [propertyName, property] of Instance.entries(instance)) { | ||
for (const [propertyNameNode, property] of Instance.entries(instance)) { | ||
const propertyName = Instance.value(propertyNameNode); | ||
if (propertyName in properties) { | ||
@@ -33,0 +42,0 @@ if (!Validation.interpret(properties[propertyName], property, ast, dynamicAnchors, true)) { |
@@ -24,8 +24,20 @@ import { pipe, asyncMap, asyncCollectObject } from "@hyperjump/pact"; | ||
const interpret = (propertyDependencies, instance, ast, dynamicAnchors, quiet) => { | ||
return Instance.typeOf(instance) !== "object" || Object.entries(propertyDependencies).every(([propertyName, valueMappings]) => { | ||
const propertyValue = Instance.value(instance)[propertyName]; | ||
return !Instance.has(propertyName, instance) | ||
|| !(propertyValue in valueMappings) | ||
|| Validation.interpret(valueMappings[propertyValue], instance, ast, dynamicAnchors, quiet); | ||
}); | ||
if (Instance.typeOf(instance) !== "object") { | ||
return true; | ||
} | ||
let isValid = true; | ||
const instanceValue = Instance.value(instance); | ||
for (const [propertyName, valueMappings] of Object.entries(propertyDependencies)) { | ||
const propertyValue = instanceValue[propertyName]; | ||
if ( | ||
Instance.has(propertyName, instance) | ||
&& propertyValue in valueMappings | ||
&& !Validation.interpret(valueMappings[propertyValue], instance, ast, dynamicAnchors, quiet) | ||
) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -32,0 +44,0 @@ |
@@ -1,4 +0,3 @@ | ||
import { every } from "@hyperjump/pact"; | ||
import { Validation } from "../experimental.js"; | ||
import * as Instance from "../instance.js"; | ||
import { Validation } from "../experimental.js"; | ||
@@ -11,7 +10,16 @@ | ||
const interpret = (propertyNames, instance, ast, dynamicAnchors) => { | ||
return Instance.typeOf(instance) !== "object" || every((key) => { | ||
return Validation.interpret(propertyNames, Instance.cons(key), ast, dynamicAnchors, true); | ||
}, Instance.keys(instance)); | ||
if (Instance.typeOf(instance) !== "object") { | ||
return true; | ||
} | ||
let isValid = true; | ||
for (const key of Instance.keys(instance)) { | ||
if (!Validation.interpret(propertyNames, key, ast, dynamicAnchors, true)) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/readOnly", ...metaData }; | ||
const id = "https://json-schema.org/keyword/readOnly"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (readOnly, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, readOnly); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/title", ...metaData }; | ||
const id = "https://json-schema.org/keyword/title"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (title, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, title); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,4 @@ | ||
import { pipe, filter, every, zip, range } from "@hyperjump/pact"; | ||
import { zip, range } from "@hyperjump/pact"; | ||
import * as Instance from "../instance.js"; | ||
import { canonicalUri } from "../schema.js"; | ||
import * as Instance from "../instance.js"; | ||
import { Validation } from "../experimental.js"; | ||
@@ -19,7 +19,14 @@ | ||
const itemIndexes = Validation.collectEvaluatedItems(schemaUrl, instance, ast, dynamicAnchors, true); | ||
return itemIndexes === false || pipe( | ||
zip(Instance.iter(instance), range(0)), | ||
filter(([, index]) => !itemIndexes.has(index)), | ||
every(([item]) => Validation.interpret(unevaluatedItems, item, ast, dynamicAnchors, quiet)) | ||
); | ||
if (itemIndexes === false) { | ||
return true; | ||
} | ||
let isValid = true; | ||
for (const [item, index] of zip(Instance.iter(instance), range(0))) { | ||
if (!itemIndexes.has(index) && !Validation.interpret(unevaluatedItems, item, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -26,0 +33,0 @@ |
@@ -1,4 +0,3 @@ | ||
import { pipe, filter, every } from "@hyperjump/pact"; | ||
import { Validation, canonicalUri } from "../experimental.js"; | ||
import * as Instance from "../instance.js"; | ||
import { Validation, canonicalUri } from "../experimental.js"; | ||
@@ -18,8 +17,15 @@ | ||
const evaluatedPropertyNames = Validation.collectEvaluatedProperties(schemaUrl, instance, ast, dynamicAnchors, true); | ||
if (evaluatedPropertyNames === false) { | ||
return true; | ||
} | ||
return !evaluatedPropertyNames || pipe( | ||
Instance.entries(instance), | ||
filter(([propertyName]) => !evaluatedPropertyNames.has(propertyName)), | ||
every(([, property]) => Validation.interpret(unevaluatedProperties, property, ast, dynamicAnchors, quiet)) | ||
); | ||
let isValid = true; | ||
for (const [propertyNameNode, property] of Instance.entries(instance)) { | ||
const propertyName = Instance.value(propertyNameNode); | ||
if (!evaluatedPropertyNames.has(propertyName) && !Validation.interpret(unevaluatedProperties, property, ast, dynamicAnchors, quiet)) { | ||
isValid = false; | ||
} | ||
} | ||
return isValid; | ||
}; | ||
@@ -38,3 +44,4 @@ | ||
for (const [propertyName, property] of Instance.entries(instance)) { | ||
for (const [propertyNameNode, property] of Instance.entries(instance)) { | ||
const propertyName = Instance.value(propertyNameNode); | ||
if (!evaluatedPropertyNames.has(propertyName)) { | ||
@@ -41,0 +48,0 @@ if (!Validation.interpret(unevaluatedProperties, property, ast, dynamicAnchors, true)) { |
@@ -1,2 +0,2 @@ | ||
import jsonStringify from "fastest-stable-stringify"; | ||
import jsonStringify from "json-stringify-deterministic"; | ||
import * as Browser from "@hyperjump/browser"; | ||
@@ -3,0 +3,0 @@ import * as Instance from "../instance.js"; |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/unknown", ...metaData }; | ||
const id = "https://json-schema.org/keyword/unknown"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (value, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, value); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
import { value, entries } from "@hyperjump/browser"; | ||
import { pipe, asyncMap, asyncCollectArray } from "@hyperjump/pact"; | ||
import { append as pointerAppend } from "@hyperjump/json-pointer"; | ||
import { publishAsync, publish } from "../pubsub.js"; | ||
import * as Instance from "../instance.js"; | ||
import { publishAsync } from "../pubsub.js"; | ||
import { toAbsoluteUri } from "../common.js"; | ||
@@ -50,27 +49,25 @@ import { canonicalUri, getKeyword, getKeywordByName } from "../experimental.js"; | ||
!quiet && publish("result.start"); | ||
const isValid = typeof ast[url] === "boolean" ? ast[url] : ast[url].every(([keywordId, schemaUrl, keywordValue]) => { | ||
!quiet && publish("result.start"); | ||
const isValid = getKeyword(keywordId).interpret(keywordValue, instance, ast, dynamicAnchors, quiet); | ||
let isSchemaValid = true; | ||
if (typeof ast[url] === "boolean") { | ||
isSchemaValid = ast[url]; | ||
} else { | ||
for (const [keywordId, schemaUrl, keywordValue] of ast[url]) { | ||
instance.valid = getKeyword(keywordId).interpret(keywordValue, instance, ast, dynamicAnchors, quiet, url); | ||
if (!instance.valid) { | ||
if (!quiet) { | ||
instance.errors[schemaUrl] = keywordId; | ||
} | ||
isSchemaValid = false; | ||
} | ||
} | ||
} | ||
!quiet && publish("result", { | ||
keyword: keywordId, | ||
absoluteKeywordLocation: schemaUrl, | ||
instanceLocation: Instance.uri(instance), | ||
valid: isValid, | ||
ast: keywordValue | ||
}); | ||
!quiet && publish("result.end"); | ||
return isValid; | ||
}); | ||
if (!isSchemaValid) { | ||
if (!quiet) { | ||
instance.errors[url] = id; | ||
} | ||
} | ||
!quiet && publish("result", { | ||
keyword: id, | ||
absoluteKeywordLocation: url, | ||
instanceLocation: Instance.uri(instance), | ||
valid: isValid, | ||
ast: url | ||
}); | ||
!quiet && publish("result.end"); | ||
return isValid; | ||
instance.valid = isSchemaValid; | ||
return isSchemaValid; | ||
}; | ||
@@ -77,0 +74,0 @@ |
@@ -1,4 +0,14 @@ | ||
import metaData from "./meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../../annotations/annotated-instance.js"; | ||
export default { id: "https://json-schema.org/keyword/writeOnly", ...metaData }; | ||
const id = "https://json-schema.org/keyword/writeOnly"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (writeOnly, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, writeOnly); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "../lib/keywords/meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../annotations/annotated-instance.js"; | ||
export default { id: "https://spec.openapis.org/oas/3.0/keyword/discriminator", ...metaData }; | ||
const id = "https://spec.openapis.org/oas/3.0/keyword/discriminator"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (discriminator, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, discriminator); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "../lib/keywords/meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../annotations/annotated-instance.js"; | ||
export default { id: "https://spec.openapis.org/oas/3.0/keyword/example", ...metaData }; | ||
const id = "https://spec.openapis.org/oas/3.0/keyword/example"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (example, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, example); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "../lib/keywords/meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../annotations/annotated-instance.js"; | ||
export default { id: "https://spec.openapis.org/oas/3.0/keyword/externalDocs", ...metaData }; | ||
const id = "https://spec.openapis.org/oas/3.0/keyword/externalDocs"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (externalDocs, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, externalDocs); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -16,2 +16,4 @@ import { addKeyword, defineVocabulary, loadDialect } from "../lib/keywords.js"; | ||
export * from "../draft-04/index.js"; | ||
addKeyword(discriminator); | ||
@@ -77,3 +79,1 @@ addKeyword(example); | ||
registerSchema(schema, "https://spec.openapis.org/oas/3.0/schema/latest"); | ||
export * from "../draft-04/index.js"; |
@@ -1,4 +0,10 @@ | ||
import metaData from "../lib/keywords/meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
export default { id: "https://spec.openapis.org/oas/3.0/keyword/nullable", ...metaData }; | ||
const id = "https://spec.openapis.org/oas/3.0/keyword/nullable"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = () => true; | ||
export default { id, compile, interpret }; |
@@ -1,4 +0,14 @@ | ||
import metaData from "../lib/keywords/meta-data.js"; | ||
import * as Browser from "@hyperjump/browser"; | ||
import * as Instance from "../annotations/annotated-instance.js"; | ||
export default { id: "https://spec.openapis.org/oas/3.0/keyword/xml", ...metaData }; | ||
const id = "https://spec.openapis.org/oas/3.0/keyword/xml"; | ||
const compile = (schema) => Browser.value(schema); | ||
const interpret = (xml, instance, _ast, _dynamicAnchors, _quiet, schemaLocation) => { | ||
Instance.setAnnotation(instance, id, schemaLocation, xml); | ||
return true; | ||
}; | ||
export default { id, compile, interpret }; |
@@ -21,2 +21,4 @@ import { addKeyword, defineVocabulary } from "../lib/keywords.js"; | ||
export * from "../draft-2020-12/index.js"; | ||
addKeyword(discriminator); | ||
@@ -49,3 +51,1 @@ addKeyword(example); | ||
registerSchema(schemaDraft04, "https://spec.openapis.org/oas/3.1/schema-draft-04"); | ||
export * from "../draft-2020-12/index.js"; |
{ | ||
"name": "@hyperjump/json-schema", | ||
"version": "1.8.0", | ||
"version": "1.9.0", | ||
"description": "A JSON Schema validator with support for custom keywords, vocabularies, and dialects", | ||
@@ -71,7 +71,7 @@ "type": "module", | ||
"dependencies": { | ||
"@hyperjump/json-pointer": "^1.0.0", | ||
"@hyperjump/json-pointer": "^1.1.0", | ||
"@hyperjump/pact": "^1.2.0", | ||
"@hyperjump/uri": "^1.2.0", | ||
"content-type": "^1.0.4", | ||
"fastest-stable-stringify": "^2.0.2", | ||
"json-stringify-deterministic": "^1.0.12", | ||
"just-curry-it": "^5.3.0", | ||
@@ -78,0 +78,0 @@ "uuid": "^9.0.0" |
113
README.md
@@ -521,3 +521,3 @@ # Hyperjump - JSON Schema | ||
might effect this one. | ||
* interpret: (compiledKeywordValue: any, instance: InstanceDocument, ast: AST, dynamicAnchors: object, quiet: boolean) => boolean | ||
* interpret: (compiledKeywordValue: any, instance: JsonNode, ast: AST, dynamicAnchors: object, quiet: boolean, schemaLocation: string) => boolean | ||
@@ -528,7 +528,7 @@ This function takes the value returned by the `compile` function and | ||
validating sub-schemas. | ||
* collectEvaluatedProperties?: (compiledKeywordValue: any, instance: InstanceDocument, ast: AST, dynamicAnchors: object) => Set\<string> | false | ||
* collectEvaluatedProperties?: (compiledKeywordValue: any, instance: JsonNode, ast: AST, dynamicAnchors: object) => Set\<string> | false | ||
If the keyword is an applicator, it will need to implement this | ||
function for `unevaluatedProperties` to work as expected. | ||
* collectEvaluatedItems?: (compiledKeywordValue: A, instance: InstanceDocument, ast: AST, dynamicAnchors: object) => Set\<number> | false | ||
* collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonNode, ast: AST, dynamicAnchors: object) => Set\<number> | false | ||
@@ -540,7 +540,2 @@ If the keyword is an applicator, it will need to implement this | ||
function to work properly with the [bundle](#bundling) feature. | ||
* annotation?: (compiledKeywordValue: any) => any | ||
If the keyword is an annotation, it will need to implement this | ||
function to work with the [annotation](#annotations-experimental) | ||
functions. | ||
* **defineVocabulary**: (id: string, keywords: { [keyword: string]: string }) => void | ||
@@ -613,3 +608,3 @@ | ||
something other than validation. | ||
* **interpret**: (schema: CompiledSchema, instance: Instance, outputFormat: OutputFormat = BASIC) => OutputUnit | ||
* **interpret**: (schema: CompiledSchema, instance: JsonNode, outputFormat: OutputFormat = BASIC) => OutputUnit | ||
@@ -619,10 +614,9 @@ A curried function for validating an instance against a compiled schema. | ||
* **OutputFormat**: **FLAG** | **BASIC** | **DETAILED** | **VERBOSE** | ||
* **OutputFormat**: **FLAG** | **BASIC** | ||
In addition to the `FLAG` output format in the Stable API, the Experimental | ||
API includes support for the `BASIC`, `DETAILED`, and `VERBOSE` formats as | ||
specified in the 2019-09 specification (with some minor customizations). | ||
This implementation doesn't include annotations or human readable error | ||
messages. The output can be processed to create human readable error | ||
messages as needed. | ||
API includes support for the `BASIC` format as specified in the 2019-09 | ||
specification (with some minor customizations). This implementation doesn't | ||
include annotations or human readable error messages. The output can be | ||
processed to create human readable error messages as needed. | ||
@@ -634,5 +628,4 @@ ## Instance API (experimental) | ||
This library uses InstanceDocument objects to represent a value in an instance. | ||
You'll work with these objects if you create a custom keyword. This module is a | ||
set of functions for working with InstanceDocuments. | ||
This library uses JsonNode objects to represent instances. You'll work with | ||
these objects if you create a custom keyword. | ||
@@ -644,36 +637,39 @@ This API uses generators to iterate over arrays and objects. If you like using | ||
* **cons**: (instance: any, uri?: string) => InstanceDocument | ||
* **fromJs**: (value: any, uri?: string) => JsonNode | ||
Construct an InstanceDocument from a value. | ||
* **get**: (url: string, contextDoc: InstanceDocument) => InstanceDocument | ||
Construct a JsonNode from a JavaScript value. | ||
* **get**: (url: string, instance: JsonNode) => JsonNode | ||
Apply a same-resource reference to a InstanceDocument. | ||
* **uri**: (doc: InstanceDocument) => string | ||
Apply a same-resource reference to a JsonNode. | ||
* **uri**: (instance: JsonNode) => string | ||
Returns a URI for the value the InstanceDocument represents. | ||
* **value**: (doc: InstanceDocument) => any | ||
Returns a URI for the value the JsonNode represents. | ||
* **value**: (instance: JsonNode) => any | ||
Returns the value the InstanceDocument represents. | ||
* **has**: (key: string, doc: InstanceDocument) => any | ||
Returns the value the JsonNode represents. | ||
* **has**: (key: string, instance: JsonNode) => boolean | ||
Similar to `key in instance`. | ||
* **typeOf**: (doc: InstanceDocument) => string | ||
Returns whether or not "key" is a property name in a JsonNode that | ||
represents an object. | ||
* **typeOf**: (instance: JsonNode) => string | ||
Determines if the JSON type of the given doc matches the given type. | ||
* **step**: (key: string, doc: InstanceDocument) => InstanceDocument | ||
The JSON type of the JsonNode. In addition to the standard JSON types, | ||
there's also the `property` type that indicates a property name/value pair | ||
in an object. | ||
* **step**: (key: string, instance: JsonNode) => JsonType | ||
Similar to `schema[key]`, but returns a InstanceDocument. | ||
* **iter**: (doc: InstanceDocument) => Generator\<InstanceDocument> | ||
Similar to indexing into a object or array using the `[]` operator. | ||
* **iter**: (instance: JsonNode) => Generator\<JsonNode> | ||
Iterate over the items in the array that the SchemaDocument represents. | ||
* **entries**: (doc: InstanceDocument) => Generator\<[string, InstanceDocument]> | ||
Iterate over the items in the array that the JsonNode represents. | ||
* **entries**: (instance: JsonNode) => Generator\<[JsonNode, JsonNode]> | ||
Similar to `Object.entries`, but yields InstanceDocuments for values. | ||
* **values**: (doc: InstanceDocument) => Generator\<InstanceDocument> | ||
Similar to `Object.entries`, but yields JsonNodes for keys and values. | ||
* **values**: (instance: JsonNode) => Generator\<JsonNode> | ||
Similar to `Object.values`, but yields InstanceDocuments for values. | ||
* **keys**: (doc: InstanceDocument) => Generator\<string> | ||
Similar to `Object.values`, but yields JsonNodes for values. | ||
* **keys**: (instance: JsonNode) => Generator\<JsonNode> | ||
Similar to `Object.keys`. | ||
* **length**: (doc: InstanceDocument) => number | ||
Similar to `Object.keys`, but yields JsonNodes for keys. | ||
* **length**: (instance: JsonNode) => number | ||
@@ -688,8 +684,9 @@ Similar to `Array.prototype.length`. | ||
### Usage | ||
An annotated JSON document is represented as an AnnotatedInstance object. This | ||
object is a wrapper around your JSON document with functions that allow you to | ||
traverse the data structure and get annotations for the values within. | ||
An annotated JSON document is represented as a | ||
(JsonNode)[#/instance-api-experimental] AST. You can use this AST to traverse | ||
the data structure and get annotations for the values it represents. | ||
```javascript | ||
import { annotate, annotatedWith, registerSchema } from "@hyperjump/json-schema/annotations/experimental"; | ||
import { registerSchema } from "@hyperjump/json-schema/draft/2020-12"; | ||
import { annotate } from "@hyperjump/json-schema/annotations/experimental"; | ||
import * as AnnotatedInstance from "@hyperjump/json-schema/annotated-instance/experimental"; | ||
@@ -747,4 +744,5 @@ | ||
// Get the title of each of the properties in the object | ||
for (const [propertyName, propertyInstance] of AnnotatedInstance.entries(instance)) { | ||
console.log(propertyName, Instance.annotation(propertyInstance, "title", dialectId)); | ||
for (const [propertyNameNode, propertyInstance] of AnnotatedInstance.entries(instance)) { | ||
const propertyName = AnnotatedInstance.value(propertyName); | ||
console.log(propertyName, AnnotatedInstance.annotation(propertyInstance, "title", dialectId)); | ||
} | ||
@@ -754,3 +752,3 @@ | ||
for (const deprecated of AnnotatedInstance.annotatedWith(instance, "deprecated", dialectId)) { | ||
if (AnnotatedInstance.annotation(instance, "deprecated", dialectId)[0]) { | ||
if (AnnotatedInstance.annotation(deprecated, "deprecated", dialectId)[0]) { | ||
logger.warn(`The value at '${deprecated.pointer}' has been deprecated.`); // => (Example) "WARN: The value at '/name' has been deprecated." | ||
@@ -765,3 +763,3 @@ } | ||
* **annotate**: (schemaUri: string, instance: any, outputFormat: OutputFormat = FLAG) => Promise\<AnnotatedInstance> | ||
* **annotate**: (schemaUri: string, instance: any, outputFormat: OutputFormat = BASIC) => Promise\<JsonNode> | ||
@@ -773,2 +771,7 @@ Annotate an instance using the given schema. The function is curried to | ||
schema. | ||
* **interpret**: (compiledSchema: CompiledSchema, instance: JsonNode, outputFormat: OutputFormat = BASIC) => JsonNode | ||
Annotate a JsonNode object rather than a plain JavaScript value. This might | ||
be useful when building tools on top of the annotation functionality, but | ||
you probably don't need it. | ||
* **ValidationError**: Error & { output: OutputUnit } | ||
@@ -784,11 +787,9 @@ The `output` field contains an `OutputUnit` with information about the | ||
* **annotation**: (instance: AnnotatedInstance, keyword: string, dialectId?: string) => any[] | ||
* **annotation**: (instance: JsonNode, keyword: string, dialect?: string): any[]; | ||
Get the annotations for a given keyword at the location represented by the | ||
instance object. | ||
* **annotatedWith**: (instance: AnnotatedInstance, keyword: string, dialectId?: string) => AnnotatedInstance[] | ||
Get the annotations for a keyword for the value represented by the JsonNode. | ||
* **annotatedWith**: (instance: JsonNode, keyword: string, dialect?: string): JsonNode[]; | ||
Get an array of instances for all the locations that are annotated with the | ||
given keyword. | ||
* **annotate**: (instance: AnnotatedInstance, keywordId: string, value: any) => AnnotatedInstance | ||
Get all JsonNodes that are annotated with the given keyword. | ||
* **setAnnotation**: (instance: JsonNode, keywordId: string, value: any) => JsonNode | ||
@@ -795,0 +796,0 @@ Add an annotation to an instance. This is used internally, you probably |
8957
801
326911
163
+ Addedjson-stringify-deterministic@1.0.12(transitive)
- Removedfastest-stable-stringify@^2.0.2
- Removedfastest-stable-stringify@2.0.2(transitive)