From e758ba16757cc15d0b0d48c6c712466b279167bf Mon Sep 17 00:00:00 2001 From: Jason Desrosiers Date: Tue, 23 Apr 2024 10:56:37 -0700 Subject: [PATCH] Refactor to use JsonNode and JsonSchemaDocument --- .../src/annotation-tests/applicators.json | 2 +- language-server/src/annotations.test.js | 29 +- language-server/src/json-instance.js | 67 ++++ language-server/src/json-schema-document.js | 204 ++++++++++++ language-server/src/json-schema-test.js | 29 +- language-server/src/json-schema.js | 72 ---- language-server/src/jsonc-instance.js | 255 -------------- language-server/src/semantic-tokens.js | 39 +-- language-server/src/server.js | 166 ++++------ language-server/src/validation.js | 141 ++++---- package-lock.json | 310 +++++++++--------- package.json | 3 +- vscode/package-lock.json | 101 +++++- 13 files changed, 693 insertions(+), 725 deletions(-) create mode 100644 language-server/src/json-instance.js create mode 100644 language-server/src/json-schema-document.js delete mode 100644 language-server/src/json-schema.js delete mode 100644 language-server/src/jsonc-instance.js diff --git a/language-server/src/annotation-tests/applicators.json b/language-server/src/annotation-tests/applicators.json index 6a2b2c9..bdc51e2 100644 --- a/language-server/src/annotation-tests/applicators.json +++ b/language-server/src/annotation-tests/applicators.json @@ -366,7 +366,7 @@ { "location": "#", "keyword": "title", - "expected": ["Else", "If"] + "expected": ["Else"] } ] } diff --git a/language-server/src/annotations.test.js b/language-server/src/annotations.test.js index 93e3a93..b1f892c 100644 --- a/language-server/src/annotations.test.js +++ b/language-server/src/annotations.test.js @@ -1,13 +1,14 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import { describe, it, expect, beforeEach, beforeAll, afterAll } from "vitest"; +import { describe, it, expect, beforeAll, afterAll } from "vitest"; import { registerSchema, unregisterSchema } from "@hyperjump/json-schema/draft-2020-12"; -import { annotate } from "./json-schema.js"; +import { getSchema, compile, interpret } from "@hyperjump/json-schema/experimental"; +import * as Instance from "./json-instance.js"; import { toAbsoluteUri } from "./util.js"; import { TextDocument } from "vscode-languageserver-textdocument"; -import { JsoncInstance } from "./jsonc-instance.js"; +import { parseTree } from "jsonc-parser"; const __filename = fileURLToPath(import.meta.url); @@ -28,10 +29,13 @@ describe("Annotations", () => { suites.forEach((suite) => { describe(suite.title + "\n" + JSON.stringify(suite.schema, null, " "), () => { let id; + let compiled; beforeAll(async () => { - id = `${host}/${encodeURIComponent(suite.title)}`; + id = `${host}/${encodeURI(suite.title)}`; registerSchema(suite.schema, id, dialectId); + const schema = await getSchema(id); + compiled = await compile(schema); }); afterAll(() => { @@ -42,17 +46,24 @@ describe("Annotations", () => { describe("Instance: " + JSON.stringify(subject.instance), () => { let instance; - beforeEach(async () => { + beforeAll(async () => { const instanceJson = JSON.stringify(subject.instance, null, " "); const textDocument = TextDocument.create(id, "json", 1, instanceJson); - instance = await annotate(id, JsoncInstance.fromTextDocument(textDocument)); + const json = textDocument.getText(); + const root = parseTree(json, [], { + disallowComments: false, + allowTrailingComma: true, + allowEmptyContent: true + }); + instance = Instance.fromJsonc(root); + interpret(compiled, instance); }); subject.assertions.forEach((assertion) => { it(`${assertion.keyword} annotations at '${assertion.location}' should be ${JSON.stringify(assertion.expected)}`, () => { - const dialect = suite.schema.$schema ? toAbsoluteUri(suite.schema.$schema) : undefined; - const annotations = instance.get(assertion.location) - .annotation(assertion.keyword, dialect); + const dialect = suite.schema.$schema ? toAbsoluteUri(suite.schema.$schema) : dialectId; + const subject = Instance.get(assertion.location, instance); + const annotations = subject ? Instance.annotation(subject, assertion.keyword, dialect) : []; expect(annotations).to.eql(assertion.expected); }); }); diff --git a/language-server/src/json-instance.js b/language-server/src/json-instance.js new file mode 100644 index 0000000..3509a1f --- /dev/null +++ b/language-server/src/json-instance.js @@ -0,0 +1,67 @@ +import * as JsonPointer from "@hyperjump/json-pointer"; +import * as Instance from "@hyperjump/json-schema/annotated-instance/experimental"; +import { getNodeValue } from "jsonc-parser"; + + +export const fromJsonc = (node, uri = "", pointer = "", parent = undefined) => { + const jsonNode = cons(uri, pointer, getNodeValue(node), node.type, [], parent, node.offset, node.length); + + switch (node.type) { + case "array": + jsonNode.children = node.children.map((child, index) => { + const itemPointer = JsonPointer.append(index, pointer); + return fromJsonc(child, uri, itemPointer, jsonNode); + }); + break; + + case "object": + jsonNode.children = node.children.map((child) => { + const propertyPointer = JsonPointer.append(getNodeValue(child.children[0]), pointer); + return fromJsonc(child, uri, propertyPointer, jsonNode); + }); + break; + + case "property": + jsonNode.children = node.children.map((child) => { + return fromJsonc(child, uri, pointer, jsonNode); + }); + break; + } + + return jsonNode; +}; + +// eslint-disable-next-line import/export +export const cons = (uri, pointer, value, type, children, parent, offset, textLength) => { + const node = Instance.cons(uri, pointer, value, type, children, parent); + node.offset = offset; + node.textLength = textLength; + + return node; +}; + +// eslint-disable-next-line import/export +export const annotation = (node, keyword, dialect = "https://json-schema.org/draft/2020-12/schema") => { + return Instance.annotation(node, keyword, dialect); +}; + +export const findNodeAtOffset = (node, offset, includeRightBound = false) => { + if (contains(node, offset, includeRightBound)) { + for (let i = 0; i < node.children.length && node.children[i].offset <= offset; i++) { + const item = findNodeAtOffset(node.children[i], offset, includeRightBound); + if (item) { + return item; + } + } + + return node; + } +}; + +const contains = (node, offset, includeRightBound = false) => { + return (offset >= node.offset && offset < (node.offset + node.textLength)) + || includeRightBound && (offset === (node.offset + node.textLength)); +}; + +// eslint-disable-next-line import/export +export * from "@hyperjump/json-schema/annotated-instance/experimental"; diff --git a/language-server/src/json-schema-document.js b/language-server/src/json-schema-document.js new file mode 100644 index 0000000..7660b7a --- /dev/null +++ b/language-server/src/json-schema-document.js @@ -0,0 +1,204 @@ +import { getSchema, compile, interpret, getKeywordName, hasDialect, BASIC } from "@hyperjump/json-schema/experimental"; +import * as JsonPointer from "@hyperjump/json-pointer"; +import { resolveIri, toAbsoluteIri } from "@hyperjump/uri"; +import { getNodeValue, parseTree } from "jsonc-parser"; +import * as Instance from "./json-instance.js"; + + +export class JsonSchemaDocument { + constructor(textDocument) { + this.textDocument = textDocument; + this.schemaResources = []; + this.errors = []; + } + + static async fromTextDocument(textDocument, contextDialectUri) { + const document = new JsonSchemaDocument(textDocument); + + const json = textDocument.getText(); + if (json) { + const root = parseTree(json, [], { + disallowComments: false, + allowTrailingComma: true, + allowEmptyContent: true + }); + + document.#buildSchemaResources(root, textDocument.uri, contextDialectUri); + + for (const { dialectUri, schemaResource } of document.schemaResources) { + if (!hasDialect(dialectUri)) { + const $schema = Instance.get("#/$schema", schemaResource); + if ($schema && Instance.typeOf($schema) === "string") { + document.errors.push({ + keyword: "https://json-schema.org/keyword/schema", + instanceNode: $schema, + message: "Unknown dialect" + }); + } else if (dialectUri) { + document.errors.push({ + keyword: "https://json-schema.org/keyword/schema", + instanceNode: schemaResource, + message: "Unknown dialect" + }); + } else { + document.errors.push({ + keyword: "https://json-schema.org/keyword/schema", + instanceLocation: schemaResource, + message: "No dialect" + }); + } + + continue; + } + + const schema = await getSchema(dialectUri); + const compiled = await compile(schema); + const output = interpret(compiled, schemaResource, BASIC); + if (!output.valid) { + for (const error of output.errors) { + document.errors.push({ + keyword: error.keyword, + keywordNode: await getSchema(error.absoluteKeywordLocation), + instanceNode: Instance.get(error.instanceLocation, schemaResource) + }); + } + } + } + } + + return document; + } + + #buildSchemaResources(node, uri = "", dialectUri = "", pointer = "", parent = undefined) { + const jsonNode = Instance.cons(uri, pointer, getNodeValue(node), node.type, [], parent, node.offset, node.length); + + switch (node.type) { + case "array": + jsonNode.children = node.children.map((child, index) => { + const itemPointer = JsonPointer.append(index, pointer); + return this.#buildSchemaResources(child, uri, dialectUri, itemPointer, jsonNode); + }); + break; + + case "object": + if (pointer === "") { + // Resource root + const $schema = nodeStep(node, "$schema"); + if ($schema?.type === "string") { + try { + dialectUri = toAbsoluteIri(getNodeValue($schema)); + } catch (error) { + // Ignore + } + } + + const idToken = keywordNameFor("https://json-schema.org/keyword/id", dialectUri); + const $idNode = idToken && nodeStep(node, idToken); + if ($idNode) { + uri = toAbsoluteIri(resolveIri(getNodeValue($idNode), uri)); + jsonNode.baseUri = uri; + } + + const legacyIdToken = keywordNameFor("https://json-schema.org/keyword/draft-04/id", dialectUri); + const legacy$idNode = legacyIdToken && nodeStep(node, legacyIdToken); + if (legacy$idNode?.type === "string") { + const legacy$id = getNodeValue(legacy$idNode); + if (legacy$id[0] !== "#") { + uri = toAbsoluteIri(resolveIri(legacy$id, uri)); + jsonNode.baseUri = uri; + } + } + } else { + // Check for embedded schema + const embeddedDialectUri = getEmbeddedDialectUri(node, dialectUri); + if (embeddedDialectUri) { + this.#buildSchemaResources(node, uri, embeddedDialectUri); + + return Instance.cons(uri, pointer, true, "boolean", [], parent, node.offset, node.length); + } + } + + for (const child of node.children) { + const propertyPointer = JsonPointer.append(getNodeValue(child.children[0]), pointer); + const propertyNode = this.#buildSchemaResources(child, uri, dialectUri, propertyPointer, jsonNode); + + if (propertyNode) { + jsonNode.children.push(propertyNode); + } + } + break; + + case "property": + if (node.children.length !== 2) { + return; + } + + jsonNode.children = node.children.map((child) => { + return this.#buildSchemaResources(child, uri, dialectUri, pointer, jsonNode); + }); + break; + } + + if (jsonNode.pointer === "") { + this.schemaResources.push({ dialectUri, schemaResource: jsonNode }); + } + + return jsonNode; + } + + * annotatedWith(keyword, dialectId = "https://json-schema.org/draft/2020-12/schema") { + for (const { schemaResource } of this.schemaResources) { + for (const node of Instance.allNodes(schemaResource)) { + if (Instance.annotation(node, keyword, dialectId).length > 0) { + yield node; + } + } + } + } + + findNodeAtOffset(offset) { + for (const { schemaResource } of this.schemaResources) { + const node = Instance.findNodeAtOffset(schemaResource, offset); + if (node) { + return node; + } + } + } +} + +const getEmbeddedDialectUri = (node, dialectUri) => { + const $schema = nodeStep(node, "$schema"); + if ($schema?.type === "string") { + const embeddedDialectUri = toAbsoluteIri(getNodeValue($schema)); + if (!hasDialect(embeddedDialectUri)) { + return embeddedDialectUri; + } else { + dialectUri = embeddedDialectUri; + } + } + + const idToken = keywordNameFor("https://json-schema.org/keyword/id", dialectUri); + const $idNode = idToken && nodeStep(node, idToken); + if ($idNode?.type === "string") { + return dialectUri; + } + + const legacyIdToken = keywordNameFor("https://json-schema.org/keyword/draft-04/id", dialectUri); + const legacy$idNode = legacyIdToken && nodeStep(node, legacyIdToken); + if (legacy$idNode?.type === "string" && getNodeValue(legacy$idNode)[0] !== "#") { + return dialectUri; + } +}; + +const nodeStep = (node, key) => { + const property = node.children.find((property) => getNodeValue(property.children[0]) === key); + return property?.children[1]; +}; + +const keywordNameFor = (keywordUri, dialectUri) => { + try { + return getKeywordName(dialectUri, keywordUri); + } catch (error) { + return undefined; + } +}; diff --git a/language-server/src/json-schema-test.js b/language-server/src/json-schema-test.js index 824a446..073d409 100644 --- a/language-server/src/json-schema-test.js +++ b/language-server/src/json-schema-test.js @@ -2,9 +2,10 @@ import fs from "node:fs"; import { describe, it, expect, beforeAll, afterAll } from "vitest"; import { toAbsoluteIri } from "@hyperjump/uri"; import { registerSchema, unregisterSchema } from "@hyperjump/json-schema"; -import { annotate } from "./json-schema.js"; -import { JsoncInstance } from "./jsonc-instance.js"; +import { getSchema, compile, interpret } from "@hyperjump/json-schema/experimental"; import { TextDocument } from "vscode-languageserver-textdocument"; +import { parseTree } from "jsonc-parser"; +import * as Instance from "./json-instance.js"; const shouldSkip = (skip, path) => { @@ -53,13 +54,16 @@ export const runTestSuite = (draft, dialectId, skip) => { suites.forEach((suite) => { describe(suite.description, () => { let url; + let compiled; - beforeAll(() => { + beforeAll(async () => { if (shouldSkip(skip, [draft, entry.name, suite.description])) { return; } - url = `http://${draft}-test-suite.json-schema.org/${encodeURIComponent(suite.description)}`; + url = `http://${draft}-test-suite.json-schema.org/${encodeURI(suite.description)}`; registerSchema(suite.schema, url, dialectId); + const schema = await getSchema(url); + compiled = await compile(schema); }); afterAll(() => { @@ -73,14 +77,15 @@ export const runTestSuite = (draft, dialectId, skip) => { it(test.description, async () => { const instanceJson = JSON.stringify(test.data, null, " "); const textDocument = TextDocument.create(url, "json", 1, instanceJson); - const instance = JsoncInstance.fromTextDocument(textDocument); - let isValid = true; - try { - await annotate(url, instance); - } catch (error) { - isValid = false; - } - expect(isValid).to.equal(test.valid); + const json = textDocument.getText(); + const root = parseTree(json, [], { + disallowComments: false, + allowTrailingComma: true, + allowEmptyContent: true + }); + const instance = Instance.fromJsonc(root); + const output = interpret(compiled, instance); + expect(output.valid).to.equal(test.valid); }); } }); diff --git a/language-server/src/json-schema.js b/language-server/src/json-schema.js deleted file mode 100644 index 37a5d1a..0000000 --- a/language-server/src/json-schema.js +++ /dev/null @@ -1,72 +0,0 @@ -import { DETAILED, compile, getSchema, getKeywordName } from "@hyperjump/json-schema/experimental"; -import { interpret } from "@hyperjump/json-schema/annotations/experimental"; -import { toAbsoluteUri } from "./util.js"; - - -export const annotate = async (uri, instance) => { - const schema = await getSchema(uri); - const compiled = await compile(schema); - return interpret(compiled, instance, DETAILED); -}; - -export const decomposeSchemaDocument = function* (schemaInstance, contextDialectUri) { - if (typeof contextDialectUri === "string") { - contextDialectUri = toAbsoluteUri(contextDialectUri); - } - yield* _decomposeSchemaDocument(schemaInstance, contextDialectUri); - yield { dialectUri: contextDialectUri, schemaInstance }; -}; - -const _decomposeSchemaDocument = function* (schemaInstance, contextDialectUri) { - if (schemaInstance.typeOf() === "object") { - const embeddedDialectUri = getEmbeddedDialectUri(schemaInstance, contextDialectUri); - - if (embeddedDialectUri) { - const embeddedSchemaInstance = schemaInstance.asEmbedded(); - yield* decomposeSchemaDocument(embeddedSchemaInstance, embeddedDialectUri); - } else { - for (const value of schemaInstance.values()) { - yield* _decomposeSchemaDocument(value, contextDialectUri); - } - } - } else if (schemaInstance.typeOf() === "array") { - for (const item of schemaInstance.iter()) { - yield* _decomposeSchemaDocument(item, contextDialectUri); - } - } -}; - -const getEmbeddedDialectUri = (schemaInstance, contextDialectUri) => { - if (schemaInstance.pointer === "") { - return; - } - - const $schema = schemaInstance.step("$schema"); - if ($schema.typeOf() === "string") { - return $schema.value(); - } - - const idToken = keywordNameFor("https://json-schema.org/keyword/id", contextDialectUri); - if (idToken) { - const id = schemaInstance.step(idToken); - if (id.typeOf() === "string") { - return contextDialectUri; - } - } - - const legacyIdToken = keywordNameFor("https://json-schema.org/keyword/draft-04/id", contextDialectUri); - if (legacyIdToken) { - const legacyId = schemaInstance.step(legacyIdToken); - if (legacyId.typeOf() === "string" && legacyId.value()[0] !== "#") { - return contextDialectUri; - } - } -}; - -const keywordNameFor = (keywordUri, dialectUri) => { - try { - return getKeywordName(dialectUri, keywordUri); - } catch (error) { - return undefined; - } -}; diff --git a/language-server/src/jsonc-instance.js b/language-server/src/jsonc-instance.js deleted file mode 100644 index 6112be7..0000000 --- a/language-server/src/jsonc-instance.js +++ /dev/null @@ -1,255 +0,0 @@ -import { findNodeAtOffset, parseTree, getNodePath, getNodeValue } from "jsonc-parser"; -import * as JsonPointer from "@hyperjump/json-pointer"; -import { getKeywordId } from "@hyperjump/json-schema/experimental"; -import { find, some } from "@hyperjump/pact"; -import { toAbsoluteUri, uriFragment } from "./util.js"; - - -export class JsoncInstance { - constructor(textDocument, root, node, pointer, annotations) { - this.textDocument = textDocument; - this.root = root; - this.node = node; - this.pointer = pointer; - this.annotations = annotations; - } - - static fromTextDocument(textDocument) { - const json = textDocument.getText(); - const root = parseTree(json, [], { - disallowComments: false, - allowTrailingComma: true, - allowEmptyContent: true - }); - - return new JsoncInstance(textDocument, root, root, "", {}); - } - - _fromNode(node, pointer) { - return new JsoncInstance(this.textDocument, this.root, node, pointer, this.annotations); - } - - asEmbedded() { - const instance = new JsoncInstance(this.textDocument, this.node, this.node, "", {}); - - const parent = this.node.parent; - const index = parent.type === "property" ? 1 : parent.children.findIndex((node) => node === this.node); - parent.children[index] = { - type: "boolean", - offset: this.node.offset, - length: 0 - }; - delete this.node.parent; - - return instance; - } - - uri() { - return `#${this.pointer}`; - } - - value() { - if (this.node === undefined) { - return undefined; - } else { - return getNodeValue(this.node); - } - } - - has(key) { - return some((propertyName) => propertyName.value() === key, this.keys()); - } - - typeOf() { - return this.node?.type ?? "undefined"; - } - - step(propertyName) { - let node; - - if (this.typeOf() === "object") { - const pair = find((pair) => getNodeValue(pair.children[0]) === propertyName, this.node.children); - node = pair?.children[1]; - } else if (this.typeOf() === "array") { - const index = parseInt(propertyName, 10); - node = this.node.children[index]; - } - - const pointer = JsonPointer.append(propertyName, this.pointer); - return this._fromNode(node, pointer); - } - - * entries() { - if (this.typeOf() !== "object") { - return; - } - - for (const propertyNode of this.node.children) { - if (!propertyNode.children[1]) { - continue; - } - const propertyName = propertyNode.children[0].value; - const pointer = JsonPointer.append(propertyName, this.pointer); - yield [ - this._fromNode(propertyNode.children[0], pointer), - this._fromNode(propertyNode.children[1], pointer) - ]; - } - } - - * iter() { - if (this.typeOf() !== "array") { - return; - } - - for (let itemIndex = 0; itemIndex < this.node.children.length; itemIndex++) { - const itemNode = this.node.children[itemIndex]; - const pointer = JsonPointer.append(`${itemIndex}`, this.pointer); - yield this._fromNode(itemNode, pointer); - } - } - - * keys() { - if (this.typeOf() !== "object") { - return; - } - - for (const propertyNode of this.node.children) { - const propertyNameNode = propertyNode.children[0]; - const pointer = JsonPointer.append(propertyNameNode.value, this.pointer); - yield this._fromNode(propertyNameNode, pointer); - } - } - - * values() { - if (this.typeOf() !== "object") { - return; - } - - for (const propertyNode of this.node.children) { - const propertyName = propertyNode.children[0].value; - const pointer = JsonPointer.append(propertyName, this.pointer); - yield this._fromNode(propertyNode.children[1], pointer); - } - } - - length() { - if (this.typeOf() !== "array") { - return; - } - - return this.node.children.length; - } - - get(uri) { - const schemaId = toAbsoluteUri(uri); - if (schemaId !== "") { - throw Error(`Not a local reference: ${uri}`); - } - - const pointer = uriFragment(uri); - const node = findNodeAtPointer(this.root, [...pointerSegments(pointer)]); - return this._fromNode(node, node ? pointer : ""); - } - - annotation(keyword, dialectId = "https://json-schema.org/draft/2020-12/schema") { - const keywordId = getKeywordId(keyword, dialectId); - return this.annotations[this.pointer]?.[keywordId] || []; - } - - annotate(keyword, value) { - const instance = Object.assign(Object.create(Object.getPrototypeOf(this)), this); - instance.annotations = { - ...this.annotations, - [this.pointer]: { - ...this.annotations[this.pointer], - [keyword]: [ - value, - ...this.annotations[this.pointer]?.[keyword] || [] - ] - } - }; - - return instance; - } - - annotatedWith(keyword, dialectId = "https://json-schema.org/draft/2020-12/schema") { - const instances = []; - - const keywordId = getKeywordId(keyword, dialectId); - for (const instancePointer in this.annotations) { - if (keywordId in this.annotations[instancePointer]) { - instances.push(this.get(`#${instancePointer}`)); - } - } - - return instances; - } - - parent() { - return this._fromNode(this.node.parent, this.pointer); - } - - startPosition() { - return this.textDocument.positionAt(this.node.offset); - } - - endPosition() { - return this.textDocument.positionAt(this.node.offset + this.node.length); - } - - textLength() { - return this.node.length; - } - - getInstanceAtPosition(position) { - const offset = this.textDocument.offsetAt(position); - const node = findNodeAtOffset(this.root, offset); - if (node && node.type !== "property") { - const pathToNode = getNodePath(node); - const pointer = pathToNode.reduce((pointer, segment) => JsonPointer.append(segment, pointer), ""); - return this._fromNode(node, pointer); - } else { - return this._fromNode(undefined, ""); - } - } -} - -const pointerSegments = function* (pointer) { - if (pointer.length > 0 && pointer[0] !== "/") { - throw Error(`Invalid JSON Pointer: '${pointer}'`); - } - - let segmentStart = 1; - let segmentEnd = 0; - - while (segmentEnd < pointer.length) { - const position = pointer.indexOf("/", segmentStart); - segmentEnd = position === -1 ? pointer.length : position; - const segment = pointer.slice(segmentStart, segmentEnd); - segmentStart = segmentEnd + 1; - - yield segment.toString().replace(/~1/g, "/").replace(/~0/g, "~"); - } -}; - -const findNodeAtPointer = (root, path) => { - let node = root; - for (const segment of path) { - if (!node) { - return; - } - - if (node.type === "object") { - const propertyNode = node.children.find((propertyNode) => propertyNode.children[0].value === segment); - node = propertyNode?.children[1]; - } else if (node.type === "array") { - const index = parseInt(segment, 10); - node = node.children[index]; - } else { - return; - } - } - - return node; -}; diff --git a/language-server/src/semantic-tokens.js b/language-server/src/semantic-tokens.js index 193b2b5..120707c 100644 --- a/language-server/src/semantic-tokens.js +++ b/language-server/src/semantic-tokens.js @@ -1,37 +1,22 @@ import { getKeywordId } from "@hyperjump/json-schema/experimental"; +import * as Instance from "./json-instance.js"; import { toAbsoluteUri } from "./util.js"; -export const getSemanticTokens = (schemaResources) => { - const semanticTokens = allSemanticTokens(schemaResources); - return sortSemanticTokens(semanticTokens); -}; - -const allSemanticTokens = function* (schemaResources) { - for (const { dialectUri, schemaInstance } of schemaResources) { - yield* schemaHandler(schemaInstance, dialectUri); +export const getSemanticTokens = function* (schemaDocument) { + for (const { schemaResource, dialectUri } of schemaDocument.schemaResources) { + yield* schemaHandler(schemaResource, dialectUri); } }; -const sortSemanticTokens = (semanticTokens) => { - return [...semanticTokens].sort((a, b) => { - const aStartPosition = a.keywordInstance.startPosition(); - const bStartPosition = b.keywordInstance.startPosition(); - - return aStartPosition.line === bStartPosition.line - ? aStartPosition.character - bStartPosition.character - : aStartPosition.line - bStartPosition.line; - }); -}; - -const schemaHandler = function* (schemaInstance, dialectUri) { - for (const [keyNode, valueNode] of schemaInstance.entries()) { - const keywordName = keyNode.value(); +const schemaHandler = function* (schemaResource, dialectUri) { + for (const [keyNode, valueNode] of Instance.entries(schemaResource)) { + const keywordName = Instance.value(keyNode); const keywordId = keywordIdFor(keywordName, dialectUri); if (keywordId) { if (keywordId === "https://json-schema.org/keyword/comment") { - yield { keywordInstance: keyNode.parent(), tokenType: "comment" }; + yield { keywordInstance: keyNode.parent, tokenType: "comment" }; } else if (toAbsoluteUri(keywordId) !== "https://json-schema.org/keyword/unknown") { yield { keywordInstance: keyNode, tokenType: "keyword" }; yield* getKeywordHandler(keywordId)(valueNode, dialectUri); @@ -50,14 +35,14 @@ const keywordIdFor = (keywordName, dialectUri) => { } }; -const schemaMapHandler = function* (schemaInstance, dialectUri) { - for (const schemaNode of schemaInstance.values()) { +const schemaMapHandler = function* (schemaResource, dialectUri) { + for (const schemaNode of Instance.values(schemaResource)) { yield* schemaHandler(schemaNode, dialectUri); } }; -const schemaArrayHandler = function* (schemaInstance, dialectUri) { - for (const schemaNode of schemaInstance.iter()) { +const schemaArrayHandler = function* (schemaResource, dialectUri) { + for (const schemaNode of Instance.iter(schemaResource)) { yield* schemaHandler(schemaNode, dialectUri); } }; diff --git a/language-server/src/server.js b/language-server/src/server.js index 5036634..856b0c7 100644 --- a/language-server/src/server.js +++ b/language-server/src/server.js @@ -18,23 +18,21 @@ import { readFile } from "node:fs/promises"; import { fileURLToPath } from "node:url"; // Hyperjump -import { setMetaSchemaOutputFormat, setShouldValidateSchema } from "@hyperjump/json-schema/draft-2020-12"; +import { setShouldValidateSchema } from "@hyperjump/json-schema/draft-2020-12"; import "@hyperjump/json-schema/draft-2019-09"; import "@hyperjump/json-schema/draft-07"; import "@hyperjump/json-schema/draft-06"; import "@hyperjump/json-schema/draft-04"; -import { hasDialect, DETAILED, getDialectIds } from "@hyperjump/json-schema/experimental"; -import { ValidationError } from "@hyperjump/json-schema/annotations/experimental"; +import { getDialectIds } from "@hyperjump/json-schema/experimental"; // Other -import { annotate, decomposeSchemaDocument } from "./json-schema.js"; -import { JsoncInstance } from "./jsonc-instance.js"; import { invalidNodes } from "./validation.js"; import { addWorkspaceFolders, workspaceSchemas, removeWorkspaceFolders, watchWorkspace } from "./workspace.js"; import { getSemanticTokens } from "./semantic-tokens.js"; +import { JsonSchemaDocument } from "./json-schema-document.js"; +import * as Instance from "./json-instance.js"; -setMetaSchemaOutputFormat(DETAILED); setShouldValidateSchema(false); const isSchema = RegExp.prototype.test.bind(/(?:\.|\/|^)schema\.json$/); @@ -162,27 +160,25 @@ connection.onDidChangeWatchedFiles(validateWorkspace); // MANAGED INSTANCES -const schemaResourceCache = new Map(); +const schemaDocuments = new Map(); -const getSchemaResources = async (textDocument) => { - let { version, schemaResources } = schemaResourceCache.get(textDocument.uri) ?? {}; +const getSchemaDocument = async (textDocument) => { + let { version, schemaDocument } = schemaDocuments.get(textDocument.uri) ?? {}; if (version !== textDocument.version) { - const instance = JsoncInstance.fromTextDocument(textDocument); - const settings = await getDocumentSettings(instance.textDocument.uri); - const contextDialectUri = instance.get("#/$schema").value() ?? settings.defaultDialect; - schemaResources = [...decomposeSchemaDocument(instance, contextDialectUri)]; + const settings = await getDocumentSettings(textDocument.uri); + schemaDocument = await JsonSchemaDocument.fromTextDocument(textDocument, settings.defaultDialect); if (textDocument.version !== -1) { - schemaResourceCache.set(textDocument.uri, { version: textDocument.version, schemaResources }); + schemaDocuments.set(textDocument.uri, { version: textDocument.version, schemaDocument }); } } - return schemaResources; + return schemaDocument; }; documents.onDidClose(({ document }) => { - schemaResourceCache.delete(document.uri); + schemaDocuments.delete(document.uri); }); // CONFIGURATION @@ -235,54 +231,28 @@ const validateSchema = async (textDocument) => { const diagnostics = []; - for (const { dialectUri, schemaInstance } of await getSchemaResources(textDocument)) { - if (schemaInstance.typeOf() === "undefined") { - continue; - } - - if (!hasDialect(dialectUri)) { - const $schema = schemaInstance.get("#/$schema"); - if ($schema.typeOf() === "string") { - diagnostics.push(buildDiagnostic($schema, "Unknown dialect")); - } else if (dialectUri) { - diagnostics.push(buildDiagnostic(schemaInstance, "Unknown dialect")); - } else { - diagnostics.push(buildDiagnostic(schemaInstance, "No dialect")); - } - - continue; - } - - try { - const annotatedInstance = await annotate(dialectUri, schemaInstance); + const schemaDocument = await getSchemaDocument(textDocument); + for await (const [instance, message] of invalidNodes(schemaDocument.errors)) { + diagnostics.push(buildDiagnostic(textDocument, instance, message)); + } - for (const deprecated of annotatedInstance.annotatedWith("deprecated")) { - if (deprecated.annotation("deprecated").some((deprecated) => deprecated)) { - const message = deprecated.annotation("x-deprecationMessage").join("\n") || "deprecated"; - diagnostics.push(buildDiagnostic(deprecated.parent(), message, DiagnosticSeverity.Warning, [DiagnosticTag.Deprecated])); - } - } - } catch (error) { - if (error instanceof ValidationError) { - for await (const [instance, message] of invalidNodes(schemaInstance, error.output)) { - diagnostics.push(buildDiagnostic(instance, message)); - } - } else { - throw error; - } + for (const deprecated of schemaDocument.annotatedWith("deprecated")) { + if (Instance.annotation(deprecated, "deprecated").some((deprecated) => deprecated)) { + const message = Instance.annotation(deprecated, "x-deprecationMessage").join("\n") || "deprecated"; + diagnostics.push(buildDiagnostic(textDocument, deprecated.parent, message, DiagnosticSeverity.Warning, [DiagnosticTag.Deprecated])); } } connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); }; -const buildDiagnostic = (instance, message, severity = DiagnosticSeverity.Error, tags = []) => { +const buildDiagnostic = (textDocument, node, message, severity = DiagnosticSeverity.Error, tags = []) => { return { severity: severity, tags: tags, range: { - start: instance.startPosition(), - end: instance.endPosition() + start: textDocument.positionAt(node.offset), + end: textDocument.positionAt(node.offset + node.textLength) }, message: message, source: "json-schema" @@ -345,19 +315,32 @@ const getTokenBuilder = (uri) => { const buildTokens = async (builder, uri) => { const textDocument = documents.get(uri); - const schemaResources = await getSchemaResources(textDocument); - for (const { keywordInstance, tokenType, tokenModifier } of getSemanticTokens(schemaResources)) { - const startPosition = keywordInstance.startPosition(); + const schemaDocument = await getSchemaDocument(textDocument); + const semanticTokens = getSemanticTokens(schemaDocument); + for (const { keywordInstance, tokenType, tokenModifier } of sortSemanticTokens(semanticTokens, textDocument)) { + const startPosition = textDocument.positionAt(keywordInstance.offset); builder.push( startPosition.line, startPosition.character, - keywordInstance.textLength(), + keywordInstance.textLength, semanticTokensLegend.tokenTypes[tokenType] ?? 0, semanticTokensLegend.tokenModifiers[tokenModifier] ?? 0 ); } }; +// VSCode requires this list to be in order. Neovim doesn't care. +const sortSemanticTokens = (semanticTokens, textDocument) => { + return [...semanticTokens].sort((a, b) => { + const aStartPosition = textDocument.positionAt(a.keywordInstance.offset); + const bStartPosition = textDocument.positionAt(b.keywordInstance.offset); + + return aStartPosition.line === bStartPosition.line + ? aStartPosition.character - bStartPosition.character + : aStartPosition.line - bStartPosition.line; + }); +}; + connection.languages.semanticTokens.on(async ({ textDocument }) => { connection.console.log(`semanticTokens.on: ${textDocument.uri}`); @@ -385,16 +368,16 @@ connection.languages.semanticTokens.onDelta(async ({ textDocument, previousResul connection.onCompletion(async ({ textDocument, position }) => { const document = documents.get(textDocument.uri); - for (const { schemaInstance } of await getSchemaResources(document)) { - const currentProperty = schemaInstance.getInstanceAtPosition(position); - if (currentProperty.pointer.endsWith("/$schema")) { - return getDialectIds().map((uri) => { - return { - label: shouldHaveTrailingHash(uri) ? `${uri}#` : uri, - kind: CompletionItemKind.Value - }; - }); - } + const schemaDocument = await getSchemaDocument(document); + const offset = document.offsetAt(position); + const currentProperty = schemaDocument.findNodeAtOffset(offset); + if (currentProperty.pointer.endsWith("/$schema")) { + return getDialectIds().map((uri) => { + return { + label: shouldHaveTrailingHash(uri) ? `${uri}#` : uri, + kind: CompletionItemKind.Value + }; + }); } }); @@ -407,41 +390,26 @@ const shouldHaveTrailingHash = (uri) => trailingHashDialects.has(uri); // KEYWORD HOVER -connection.onHover(async (textDocumentPositionParams) => { - const { textDocument: { uri: textDocumentURI }, position } = textDocumentPositionParams; - const document = documents.get(textDocumentURI); - - const schemaResources = await getSchemaResources(document); - for (const { dialectUri, schemaInstance } of schemaResources) { - if (!hasDialect(dialectUri)) { - continue; - } - - try { - const annotations = await annotate(dialectUri, schemaInstance); - const keyword = annotations.getInstanceAtPosition(position); - if (keyword.typeOf() !== "undefined") { - // Found - const description = keyword.annotation("description", dialectUri).join("\n"); - return buildHover(MarkupKind.Markdown, description, keyword.startPosition(), keyword.endPosition()); - } - } catch (error) { - if (error instanceof ValidationError) { - return null; +connection.onHover(async ({ textDocument, position }) => { + const document = documents.get(textDocument.uri); + const schemaDocument = await getSchemaDocument(document); + const offset = document.offsetAt(position); + const keyword = schemaDocument.findNodeAtOffset(offset); + if (keyword?.parent && Instance.typeOf(keyword.parent) === "property" && keyword.parent.children[0] === keyword) { + // This is a little wierd because the we want to hover for the keyword, but + // the annotation is actually on the value not the keyword itself. + return { + contents: { + kind: MarkupKind.Markdown, + value: Instance.annotation(keyword.parent.children[1], "description").join("\n") + }, + range: { + start: document.positionAt(keyword.offset), + end: document.positionAt(keyword.offset + keyword.textLength) } - } + }; } }); -const buildHover = (kind, value, startPosition, endPosition) => { - return { - contents: { kind, value }, - range: { - start: startPosition, - end: endPosition - } - }; -}; - connection.listen(); documents.listen(connection); diff --git a/language-server/src/validation.js b/language-server/src/validation.js index b2ed0e3..c77537d 100644 --- a/language-server/src/validation.js +++ b/language-server/src/validation.js @@ -1,36 +1,32 @@ import * as Browser from "@hyperjump/browser"; -import { getSchema } from "@hyperjump/json-schema/experimental"; -export const invalidNodes = async function* (instance, outputUnit) { - for await (const message of toErrorMessage(instance, outputUnit)) { - yield [instance, message]; - } - - for (const error of outputUnit.errors) { - const errorInstance = instance.get(outputUnit.instanceLocation); - yield* invalidNodes(errorInstance, error); +export const invalidNodes = async function* (errors) { + for (const error of errors) { + for await (const message of toErrorMessage(error)) { + yield [error.instanceNode, message]; + } } }; -const toErrorMessage = async function* (instance, outputUnit) { - if (outputUnit.keyword === "https://json-schema.org/keyword/additionalProperties") { +const toErrorMessage = async function* (error) { + if (error.keyword === "https://json-schema.org/keyword/schema") { + yield error.message; + } else if (error.keyword === "https://json-schema.org/keyword/additionalProperties") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/allOf") { + } else if (error.keyword === "https://json-schema.org/keyword/allOf") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/anyOf") { + } else if (error.keyword === "https://json-schema.org/keyword/anyOf") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/const") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const constValue = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/const") { + const constValue = Browser.value(error.keywordNode); yield `Expected : ${JSON.stringify(constValue)}`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/contains") { + } else if (error.keyword === "https://json-schema.org/keyword/contains") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/dependentRequired") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const dependentRequired = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/dependentRequired") { + const dependentRequired = Browser.value(error.keywordNode); - const object = instance.value(); + const object = error.instanceNode.value(); for (const propertyName in dependentRequired) { if (propertyName in object) { for (const required of dependentRequired[propertyName]) { @@ -40,98 +36,83 @@ const toErrorMessage = async function* (instance, outputUnit) { } } } - } else if (outputUnit.keyword === "https://json-schema.org/keyword/dynamicRef") { + } else if (error.keyword === "https://json-schema.org/keyword/dynamicRef") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/draft-2020-12/dynamicRef") { + } else if (error.keyword === "https://json-schema.org/keyword/draft-2020-12/dynamicRef") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/enum") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const enumValue = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/enum") { + const enumValue = Browser.value(error.keywordNode); yield `Expected one of: ${enumValue.map((value) => JSON.stringify(value, null, " ")).join(", ")}`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/exclusiveMaximum") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const exclusiveMaximum = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/exclusiveMaximum") { + const exclusiveMaximum = Browser.value(error.keywordNode); yield `Must be less than ${exclusiveMaximum}`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/exclusiveMinimum") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const exclusiveMinimum = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/exclusiveMinimum") { + const exclusiveMinimum = Browser.value(error.keywordNode); yield `Must be greater than ${exclusiveMinimum}`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/items") { + } else if (error.keyword === "https://json-schema.org/keyword/items") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/maxItems") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const maxItems = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/maxItems") { + const maxItems = Browser.value(error.keywordNode); yield `Too many items. A maximum of ${maxItems} are allowed.`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/maxLength") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const maxLength = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/maxLength") { + const maxLength = Browser.value(error.keywordNode); yield `A maximum of ${maxLength} characters are allowed.`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/maxProperties") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const maxProperties = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/maxProperties") { + const maxProperties = Browser.value(error.keywordNode); yield `A maximum of ${maxProperties} properties are allowed.`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/maximum") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const maximum = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/maximum") { + const maximum = Browser.value(error.keywordNode); yield `Must be less than or equal to ${maximum}`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/minItems") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const minItems = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/minItems") { + const minItems = Browser.value(error.keywordNode); yield `A minimum of ${minItems} are required.`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/minLength") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const minLength = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/minLength") { + const minLength = Browser.value(error.keywordNode); yield `A minimum of ${minLength} characters are required.`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/minProperties") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const minProperties = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/minProperties") { + const minProperties = Browser.value(error.keywordNode); yield `A minimum of ${minProperties} properties are required.`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/minimum") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const minimum = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/minimum") { + const minimum = Browser.value(error.keywordNode); yield `Must be greater than or equal to ${minimum}`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/multipleOf") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const multipleOf = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/multipleOf") { + const multipleOf = Browser.value(error.keywordNode); yield `Must be a multiple of ${multipleOf}`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/pattern") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const pattern = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/pattern") { + const pattern = Browser.value(error.keywordNode); yield `Must match the pattern /${pattern.replace("/", "\\/")}/`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/prefixItems") { + } else if (error.keyword === "https://json-schema.org/keyword/prefixItems") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/properties") { + } else if (error.keyword === "https://json-schema.org/keyword/properties") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/propertyNames") { + } else if (error.keyword === "https://json-schema.org/keyword/propertyNames") { yield `Object contains invalid property names`; - } else if (outputUnit.keyword === "https://json-schema.org/keyword/ref") { + } else if (error.keyword === "https://json-schema.org/keyword/ref") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/keyword/required") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const required = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/required") { + const required = Browser.value(error.keywordNode); - const object = instance.value(); + const object = error.instanceNode.value(); for (const propertyName of required) { if (!(propertyName in object)) { yield `Property ${propertyName} is required.`; } } - } else if (outputUnit.keyword === "https://json-schema.org/keyword/type") { - const schema = await getSchema(outputUnit.absoluteKeywordLocation); - const type = Browser.value(schema); + } else if (error.keyword === "https://json-schema.org/keyword/type") { + const type = Browser.value(error.keywordNode); yield `Expected a(n) ${type}`; - } else if (outputUnit.keyword === "https://json-schema.org/evaluation/unevaluatedItems") { + } else if (error.keyword === "https://json-schema.org/evaluation/unevaluatedItems") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/evaluation/unevaluatedProperties") { + } else if (error.keyword === "https://json-schema.org/evaluation/unevaluatedProperties") { // Skip - } else if (outputUnit.keyword === "https://json-schema.org/evaluation/uniqueItems") { + } else if (error.keyword === "https://json-schema.org/evaluation/uniqueItems") { yield `All items must be unique`; - } else if (outputUnit.keyword === "https://json-schema.org/evaluation/validate") { - if (outputUnit.errors.length === 0) { + } else if (error.keyword === "https://json-schema.org/evaluation/validate") { + if (Browser.value(error.keywordNode) === false) { yield `No value allowed`; } } else { - const keyword = outputUnit.absoluteKeywordLocation.split("/").pop(); + const keyword = error.keywordNode.cursor.split("/").pop(); yield `Fails JSON Schema constraint '${keyword}'`; } }; diff --git a/package-lock.json b/package-lock.json index 9cc2837..9e2935d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "@hyperjump/browser": "^1.1.0", "@hyperjump/json-pointer": "^1.0.1", "@hyperjump/json-schema": "github:hyperjump-io/json-schema#lsp", - "@hyperjump/pact": "^1.3.0" + "@hyperjump/pact": "^1.3.0", + "@hyperjump/uri": "^1.2.2" }, "devDependencies": { "@vitest/coverage-v8": "*", @@ -25,15 +26,6 @@ "vitest": "*" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -57,18 +49,18 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -78,13 +70,13 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -555,11 +547,11 @@ "dev": true }, "node_modules/@hyperjump/browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@hyperjump/browser/-/browser-1.1.3.tgz", - "integrity": "sha512-H7DtqWbP3YvdbZFTLln3BGPg5gt9B9aUxblIHyFRLMYGoNyq0yJN6LYRb3ZwYcRsxytAOwL6efnEKNFzF91iQQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@hyperjump/browser/-/browser-1.1.4.tgz", + "integrity": "sha512-85rfa3B79MssMOxNChvXJhfgvIXqA2FEzwrxKe9iMpCKZVQIxQe54w210VeFM0D33pVOeNskg7TyptSjenY2+w==", "dependencies": { - "@hyperjump/json-pointer": "^1.0.1", + "@hyperjump/json-pointer": "^1.1.0", "@hyperjump/uri": "^1.2.0", "content-type": "^1.0.5", "just-curry-it": "^5.3.0" @@ -582,11 +574,10 @@ } }, "node_modules/@hyperjump/json-schema": { - "version": "1.8.0", - "resolved": "git+ssh://git@github.com/hyperjump-io/json-schema.git#6a2702e146c61b4f3c0ba3efe4bc42d4ea7ad1c6", - "license": "MIT", + "version": "1.9.2", + "resolved": "git+ssh://git@github.com/hyperjump-io/json-schema.git#088cf7657b9f8e8eb8ec030c6d68d1e5f1d42d73", "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", @@ -728,9 +719,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.4.tgz", - "integrity": "sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", + "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", "cpu": [ "arm" ], @@ -741,9 +732,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.16.4.tgz", - "integrity": "sha512-Bvm6D+NPbGMQOcxvS1zUl8H7DWlywSXsphAeOnVeiZLQ+0J6Is8T7SrjGTH29KtYkiY9vld8ZnpV3G2EPbom+w==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", + "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", "cpu": [ "arm64" ], @@ -754,9 +745,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.16.4.tgz", - "integrity": "sha512-i5d64MlnYBO9EkCOGe5vPR/EeDwjnKOGGdd7zKFhU5y8haKhQZTN2DgVtpODDMxUr4t2K90wTUJg7ilgND6bXw==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", + "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", "cpu": [ "arm64" ], @@ -767,9 +758,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.16.4.tgz", - "integrity": "sha512-WZupV1+CdUYehaZqjaFTClJI72fjJEgTXdf4NbW69I9XyvdmztUExBtcI2yIIU6hJtYvtwS6pkTkHJz+k08mAQ==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", + "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", "cpu": [ "x64" ], @@ -780,9 +771,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.16.4.tgz", - "integrity": "sha512-ADm/xt86JUnmAfA9mBqFcRp//RVRt1ohGOYF6yL+IFCYqOBNwy5lbEK05xTsEoJq+/tJzg8ICUtS82WinJRuIw==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", + "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", "cpu": [ "arm" ], @@ -793,9 +784,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.16.4.tgz", - "integrity": "sha512-tJfJaXPiFAG+Jn3cutp7mCs1ePltuAgRqdDZrzb1aeE3TktWWJ+g7xK9SNlaSUFw6IU4QgOxAY4rA+wZUT5Wfg==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", + "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", "cpu": [ "arm" ], @@ -806,9 +797,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.16.4.tgz", - "integrity": "sha512-7dy1BzQkgYlUTapDTvK997cgi0Orh5Iu7JlZVBy1MBURk7/HSbHkzRnXZa19ozy+wwD8/SlpJnOOckuNZtJR9w==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", + "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", "cpu": [ "arm64" ], @@ -819,9 +810,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.16.4.tgz", - "integrity": "sha512-zsFwdUw5XLD1gQe0aoU2HVceI6NEW7q7m05wA46eUAyrkeNYExObfRFQcvA6zw8lfRc5BHtan3tBpo+kqEOxmg==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", + "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", "cpu": [ "arm64" ], @@ -832,9 +823,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.16.4.tgz", - "integrity": "sha512-p8C3NnxXooRdNrdv6dBmRTddEapfESEUflpICDNKXpHvTjRRq1J82CbU5G3XfebIZyI3B0s074JHMWD36qOW6w==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", + "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", "cpu": [ "ppc64" ], @@ -845,9 +836,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.16.4.tgz", - "integrity": "sha512-Lh/8ckoar4s4Id2foY7jNgitTOUQczwMWNYi+Mjt0eQ9LKhr6sK477REqQkmy8YHY3Ca3A2JJVdXnfb3Rrwkng==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", + "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", "cpu": [ "riscv64" ], @@ -858,9 +849,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.16.4.tgz", - "integrity": "sha512-1xwwn9ZCQYuqGmulGsTZoKrrn0z2fAur2ujE60QgyDpHmBbXbxLaQiEvzJWDrscRq43c8DnuHx3QorhMTZgisQ==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", + "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", "cpu": [ "s390x" ], @@ -871,9 +862,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.16.4.tgz", - "integrity": "sha512-LuOGGKAJ7dfRtxVnO1i3qWc6N9sh0Em/8aZ3CezixSTM+E9Oq3OvTsvC4sm6wWjzpsIlOCnZjdluINKESflJLA==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", + "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", "cpu": [ "x64" ], @@ -884,9 +875,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.16.4.tgz", - "integrity": "sha512-ch86i7KkJKkLybDP2AtySFTRi5fM3KXp0PnHocHuJMdZwu7BuyIKi35BE9guMlmTpwwBTB3ljHj9IQXnTCD0vA==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", + "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", "cpu": [ "x64" ], @@ -897,9 +888,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.16.4.tgz", - "integrity": "sha512-Ma4PwyLfOWZWayfEsNQzTDBVW8PZ6TUUN1uFTBQbF2Chv/+sjenE86lpiEwj2FiviSmSZ4Ap4MaAfl1ciF4aSA==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", + "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", "cpu": [ "arm64" ], @@ -910,9 +901,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.16.4.tgz", - "integrity": "sha512-9m/ZDrQsdo/c06uOlP3W9G2ENRVzgzbSXmXHT4hwVaDQhYcRpi9bgBT0FTG9OhESxwK0WjQxYOSfv40cU+T69w==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", + "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", "cpu": [ "ia32" ], @@ -923,9 +914,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.16.4.tgz", - "integrity": "sha512-YunpoOAyGLDseanENHmbFvQSfVL5BxW3k7hhy0eN4rb3gS/ct75dVD0EXOWIqFT/nE8XYW6LP6vz6ctKRi0k9A==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", + "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", "cpu": [ "x64" ], @@ -960,9 +951,9 @@ "dev": true }, "node_modules/@vitest/coverage-v8": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.1.tgz", - "integrity": "sha512-Zx+dYEDcZg+44ksjIWvWosIGlPLJB1PPpN3O8+Xrh/1qa7WSFA6Y8H7lsZJTYrxu4G2unk9tvP5TgjIGDliF1w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", + "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -983,17 +974,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.5.1" + "vitest": "1.6.0" } }, "node_modules/@vitest/expect": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.1.tgz", - "integrity": "sha512-w3Bn+VUMqku+oWmxvPhTE86uMTbfmBl35aGaIPlwVW7Q89ZREC/icfo2HBsEZ3AAW6YR9lObfZKPEzstw9tJOQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", "dev": true, "dependencies": { - "@vitest/spy": "1.5.1", - "@vitest/utils": "1.5.1", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "chai": "^4.3.10" }, "funding": { @@ -1001,12 +992,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.1.tgz", - "integrity": "sha512-mt372zsz0vFR7L1xF/ert4t+teD66oSuXoTyaZbl0eJgilvyzCKP1tJ21gVa8cDklkBOM3DLnkE1ljj/BskyEw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", "dev": true, "dependencies": { - "@vitest/utils": "1.5.1", + "@vitest/utils": "1.6.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -1042,9 +1033,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.1.tgz", - "integrity": "sha512-h/1SGaZYXmjn6hULRBOlqam2z4oTlEe6WwARRzLErAPBqljAs6eX7tfdyN0K+MpipIwSZ5sZsubDWkCPAiVXZQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -1056,9 +1047,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.1.tgz", - "integrity": "sha512-vsqczk6uPJjmPLy6AEtqfbFqgLYcGBe9BTY+XL8L6y8vrGOhyE23CJN9P/hPimKXnScbqiZ/r/UtUSOQ2jIDGg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -1068,9 +1059,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.1.tgz", - "integrity": "sha512-92pE17bBXUxA0Y7goPcvnATMCuq4NQLOmqsG0e2BtzRi7KLwZB5jpiELi/8ybY8IQNWemKjSD5rMoO7xTdv8ug==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -2301,12 +2292,13 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -2836,9 +2828,8 @@ }, "node_modules/json-schema-test-suite": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/json-schema-org/JSON-Schema-Test-Suite.git#54f3784a8c6926d8e91ed43267950a07efc34086", - "dev": true, - "license": "MIT" + "resolved": "git+ssh://git@github.com/json-schema-org/JSON-Schema-Test-Suite.git#dd9599a5238abe76d13fab37857265a3be3a01ea", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -3047,15 +3038,15 @@ } }, "node_modules/mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.0.tgz", + "integrity": "sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==", "dev": true, "dependencies": { "acorn": "^8.11.3", "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" + "pkg-types": "^1.1.0", + "ufo": "^1.5.3" } }, "node_modules/ms": { @@ -3225,17 +3216,17 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -3450,9 +3441,9 @@ ] }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/regexp.prototype.flags": { @@ -3534,9 +3525,9 @@ } }, "node_modules/rollup": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.16.4.tgz", - "integrity": "sha512-kuaTJSUbz+Wsb2ATGvEknkI12XV40vIiHmLuFlejoo7HtDok/O5eDDD0UpCVY5bBX5U5RYo8wWP83H7ZsqVEnA==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", + "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -3549,22 +3540,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.16.4", - "@rollup/rollup-android-arm64": "4.16.4", - "@rollup/rollup-darwin-arm64": "4.16.4", - "@rollup/rollup-darwin-x64": "4.16.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.16.4", - "@rollup/rollup-linux-arm-musleabihf": "4.16.4", - "@rollup/rollup-linux-arm64-gnu": "4.16.4", - "@rollup/rollup-linux-arm64-musl": "4.16.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.16.4", - "@rollup/rollup-linux-riscv64-gnu": "4.16.4", - "@rollup/rollup-linux-s390x-gnu": "4.16.4", - "@rollup/rollup-linux-x64-gnu": "4.16.4", - "@rollup/rollup-linux-x64-musl": "4.16.4", - "@rollup/rollup-win32-arm64-msvc": "4.16.4", - "@rollup/rollup-win32-ia32-msvc": "4.16.4", - "@rollup/rollup-win32-x64-msvc": "4.16.4", + "@rollup/rollup-android-arm-eabi": "4.17.2", + "@rollup/rollup-android-arm64": "4.17.2", + "@rollup/rollup-darwin-arm64": "4.17.2", + "@rollup/rollup-darwin-x64": "4.17.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", + "@rollup/rollup-linux-arm-musleabihf": "4.17.2", + "@rollup/rollup-linux-arm64-gnu": "4.17.2", + "@rollup/rollup-linux-arm64-musl": "4.17.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", + "@rollup/rollup-linux-riscv64-gnu": "4.17.2", + "@rollup/rollup-linux-s390x-gnu": "4.17.2", + "@rollup/rollup-linux-x64-gnu": "4.17.2", + "@rollup/rollup-linux-x64-musl": "4.17.2", + "@rollup/rollup-win32-arm64-msvc": "4.17.2", + "@rollup/rollup-win32-ia32-msvc": "4.17.2", + "@rollup/rollup-win32-x64-msvc": "4.17.2", "fsevents": "~2.3.2" } }, @@ -4089,9 +4080,9 @@ } }, "node_modules/vite": { - "version": "5.2.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz", - "integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==", + "version": "5.2.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", + "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", "dev": true, "dependencies": { "esbuild": "^0.20.1", @@ -4144,9 +4135,9 @@ } }, "node_modules/vite-node": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.1.tgz", - "integrity": "sha512-HNpfV7BrAsjkYVNWIcPleJwvJmydJqqJRrRbpoQ/U7QDwJKyEzNa4g5aYg8MjXJyKsk29IUCcMLFRcsEvqUIsA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -4166,16 +4157,16 @@ } }, "node_modules/vitest": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.1.tgz", - "integrity": "sha512-3GvBMpoRnUNbZRX1L3mJCv3Ou3NAobb4dM48y8k9ZGwDofePpclTOyO+lqJFKSQpubH1V8tEcAEw/Y3mJKGJQQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", "dev": true, "dependencies": { - "@vitest/expect": "1.5.1", - "@vitest/runner": "1.5.1", - "@vitest/snapshot": "1.5.1", - "@vitest/spy": "1.5.1", - "@vitest/utils": "1.5.1", + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -4189,7 +4180,7 @@ "tinybench": "^2.5.1", "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.5.1", + "vite-node": "1.6.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -4204,8 +4195,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.5.1", - "@vitest/ui": "1.5.1", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", "happy-dom": "*", "jsdom": "*" }, @@ -4296,6 +4287,15 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index f21ee53..c4d2533 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@hyperjump/browser": "^1.1.0", "@hyperjump/json-pointer": "^1.0.1", "@hyperjump/json-schema": "github:hyperjump-io/json-schema#lsp", - "@hyperjump/pact": "^1.3.0" + "@hyperjump/pact": "^1.3.0", + "@hyperjump/uri": "^1.2.2" } } diff --git a/vscode/package-lock.json b/vscode/package-lock.json index 7bd8048..9fbec3b 100644 --- a/vscode/package-lock.json +++ b/vscode/package-lock.json @@ -536,15 +536,17 @@ "dev": true }, "node_modules/@vscode/vsce": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.24.0.tgz", - "integrity": "sha512-p6CIXpH5HXDqmUkgFXvIKTjZpZxy/uDx4d/UsfhS9vQUun43KDNUbYeZocyAHgqcJlPEurgArHz9te1PPiqPyA==", + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.25.0.tgz", + "integrity": "sha512-VXMCGUaP6wKBadA7vFQdsksxkBAMoh4ecZgXBwauZMASAgnwYesHyLnqIyWYeRwjy2uEpitHvz/1w5ENnR30pg==", "dev": true, "dependencies": { - "azure-devops-node-api": "^11.0.1", + "azure-devops-node-api": "^12.5.0", "chalk": "^2.4.2", "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", "commander": "^6.2.1", + "form-data": "^4.0.0", "glob": "^7.0.6", "hosted-git-info": "^4.0.2", "jsonc-parser": "^3.2.0", @@ -566,7 +568,7 @@ "vsce": "vsce" }, "engines": { - "node": ">= 14" + "node": ">= 16" }, "optionalDependencies": { "keytar": "^7.7.0" @@ -750,6 +752,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -766,9 +774,9 @@ } }, "node_modules/azure-devops-node-api": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz", - "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", "dev": true, "dependencies": { "tunnel": "0.0.6", @@ -950,6 +958,15 @@ "dev": true, "optional": true }, + "node_modules/cockatiel": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.1.2.tgz", + "integrity": "sha512-5yARKww0dWyWg2/3xZeXgoxjHLwpVqFptj9Zy7qioJ6+/L0ARM184sgMUrQDjxw7ePJWlGhV998mKhzrxT0/Kg==", + "dev": true, + "engines": { + "node": ">=16" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -965,6 +982,18 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -1156,6 +1185,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -1869,6 +1907,20 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -2675,6 +2727,27 @@ "node": ">=4" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -2742,9 +2815,9 @@ "dev": true }, "node_modules/node-abi": { - "version": "3.57.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz", - "integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==", + "version": "3.59.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.59.0.tgz", + "integrity": "sha512-HyyfzvTLCE8b1SX2nWimlra8cibEsypcSu/Az4SXMhWhtuctkwAX7qsEYNjUOIoYtPV884oN3wtYTN+iZKBtvw==", "dev": true, "optional": true, "dependencies": { @@ -3074,9 +3147,9 @@ } }, "node_modules/qs": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", - "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", "dev": true, "dependencies": { "side-channel": "^1.0.6"