From da3d4a7af1f6f4d2f554a8795be2376fcf1d9b4f Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Tue, 9 Jun 2026 09:04:55 +0200 Subject: [PATCH 01/48] feat!: migrate from CodeMirror 5 to Monaco editor (VSCode) with a language server - `Yasqe` now extends `EventEmitter` instead of `CodeMirror` - Use a wasm language server for diagnostics, syntax highlight, autocompletion, code actions, etc - In the `dev/index.html` implement a demo using the Qlue-LS language server - Delete code in yasqe related to built-in autocomplete and grammar (since this now handled by the language server) --- dev/index.html | 35 +- dev/qluels.ts | 304 ++ dev/qluels.worker.ts | 36 + dev/style.css | 87 + dev/yasqe.html | 119 +- dev/yasr.html | 40 +- package-lock.json | 1268 ++++- package.json | 7 +- packages/yasgui/src/Tab.ts | 146 +- packages/yasgui/src/darkmode.css | 293 + packages/yasgui/src/index.ts | 104 + packages/yasqe/grammar/README.md | 12 - packages/yasqe/grammar/_tokenizer-table.d.ts | 3 - packages/yasqe/grammar/_tokenizer-table.js | 4776 ----------------- packages/yasqe/grammar/build.sh | 2 - packages/yasqe/grammar/sparql11-grammar.pl | 834 --- .../yasqe/grammar/sparqljs-browser-min.js | 4535 ---------------- packages/yasqe/grammar/tokenizer.ts | 729 --- packages/yasqe/grammar/util/gen_ll1.pl | 37 - packages/yasqe/grammar/util/gen_sparql11.pl | 11 - packages/yasqe/grammar/util/ll1.pl | 175 - .../grammar/util/output_to_javascript.pl | 75 - packages/yasqe/grammar/util/prune.pl | 49 - packages/yasqe/grammar/util/rewrite.pl | 104 - packages/yasqe/package.json | 8 +- packages/yasqe/src/CodeMirror.ts | 54 - packages/yasqe/src/autocompleters/classes.ts | 32 - packages/yasqe/src/autocompleters/index.ts | 346 -- packages/yasqe/src/autocompleters/prefixes.ts | 137 - .../yasqe/src/autocompleters/properties.ts | 28 - .../yasqe/src/autocompleters/show-hint.scss | 38 - .../yasqe/src/autocompleters/variables.ts | 52 - packages/yasqe/src/defaults.ts | 144 +- packages/yasqe/src/editor/editorConfig.ts | 221 + packages/yasqe/src/editor/sparqlGrammar.ts | 393 ++ packages/yasqe/src/editor/sparqlTheme.ts | 211 + packages/yasqe/src/env.d.ts | 13 + packages/yasqe/src/index.ts | 981 ++-- packages/yasqe/src/prefixFold.ts | 93 - packages/yasqe/src/prefixUtils.ts | 65 - packages/yasqe/src/scss/codemirrorMods.scss | 36 - packages/yasqe/src/scss/yasqe.scss | 89 - packages/yasqe/src/sparql.ts | 2 +- .../{scss/buttons.scss => style/buttons.css} | 69 +- packages/yasqe/src/style/yasqe.css | 153 + packages/yasqe/src/tokenUtils.ts | 121 - packages/yasqe/src/tooltip.ts | 31 - packages/yasqe/src/trie.ts | 238 - packages/yasr/src/darkmode.css | 185 + packages/yasr/src/index.ts | 1 + packages/yasr/src/plugins/response/index.ts | 1 + packages/yasr/src/plugins/table/index.scss | 6 + packages/yasr/src/plugins/table/index.ts | 1 + tsconfig.json | 2 +- vite.config.ts | 59 +- 55 files changed, 3884 insertions(+), 13707 deletions(-) create mode 100644 dev/qluels.ts create mode 100644 dev/qluels.worker.ts create mode 100644 dev/style.css create mode 100644 packages/yasgui/src/darkmode.css delete mode 100644 packages/yasqe/grammar/README.md delete mode 100644 packages/yasqe/grammar/_tokenizer-table.d.ts delete mode 100644 packages/yasqe/grammar/_tokenizer-table.js delete mode 100755 packages/yasqe/grammar/build.sh delete mode 100644 packages/yasqe/grammar/sparql11-grammar.pl delete mode 100644 packages/yasqe/grammar/sparqljs-browser-min.js delete mode 100644 packages/yasqe/grammar/tokenizer.ts delete mode 100644 packages/yasqe/grammar/util/gen_ll1.pl delete mode 100644 packages/yasqe/grammar/util/gen_sparql11.pl delete mode 100644 packages/yasqe/grammar/util/ll1.pl delete mode 100644 packages/yasqe/grammar/util/output_to_javascript.pl delete mode 100644 packages/yasqe/grammar/util/prune.pl delete mode 100644 packages/yasqe/grammar/util/rewrite.pl delete mode 100644 packages/yasqe/src/CodeMirror.ts delete mode 100644 packages/yasqe/src/autocompleters/classes.ts delete mode 100644 packages/yasqe/src/autocompleters/index.ts delete mode 100644 packages/yasqe/src/autocompleters/prefixes.ts delete mode 100644 packages/yasqe/src/autocompleters/properties.ts delete mode 100644 packages/yasqe/src/autocompleters/show-hint.scss delete mode 100644 packages/yasqe/src/autocompleters/variables.ts create mode 100644 packages/yasqe/src/editor/editorConfig.ts create mode 100644 packages/yasqe/src/editor/sparqlGrammar.ts create mode 100644 packages/yasqe/src/editor/sparqlTheme.ts create mode 100644 packages/yasqe/src/env.d.ts delete mode 100644 packages/yasqe/src/prefixFold.ts delete mode 100644 packages/yasqe/src/prefixUtils.ts delete mode 100644 packages/yasqe/src/scss/codemirrorMods.scss delete mode 100644 packages/yasqe/src/scss/yasqe.scss rename packages/yasqe/src/{scss/buttons.scss => style/buttons.css} (75%) create mode 100644 packages/yasqe/src/style/yasqe.css delete mode 100644 packages/yasqe/src/tokenUtils.ts delete mode 100644 packages/yasqe/src/tooltip.ts delete mode 100644 packages/yasqe/src/trie.ts create mode 100644 packages/yasr/src/darkmode.css diff --git a/dev/index.html b/dev/index.html index 072add26..c3fda609 100644 --- a/dev/index.html +++ b/dev/index.html @@ -2,37 +2,20 @@ - + YASGUI -
+ @@ -40,12 +23,16 @@ diff --git a/dev/qluels.ts b/dev/qluels.ts new file mode 100644 index 00000000..d7055a45 --- /dev/null +++ b/dev/qluels.ts @@ -0,0 +1,304 @@ +/// +/** + * Consumer-side qlue-ls configuration for the demo pages. + * + * yasqe/yasgui are language-server agnostic: they only receive a ready LSP `Worker` and expose the + * resulting language client. Everything qlue-ls specific (the WASM worker, the backend/completion + * query config, the `qlueLs/addBackend` extension) lives here, so swapping to another SPARQL + * language server (e.g. SemanticWebLanguageServer/swls) only touches this file. + */ +import QlueLsWorker from "./qluels.worker?worker"; + +export type SparqlEngine = "QLever" | "GraphDB" | "Virtuoso" | "MillenniumDB" | "Blazegraph" | "Jena"; + +export interface PrefixMap { + [key: string]: string; +} + +/** Valid qlue-ls completion query template keys (the `CompletionTemplate` enum, camelCase). */ +export type CompletionTemplate = + | "hover" + | "subjectCompletion" + | "predicateCompletionContextSensitive" + | "predicateCompletionContextInsensitive" + | "objectCompletionContextSensitive" + | "objectCompletionContextInsensitive" + | "valuesCompletionContextSensitive" + | "valuesCompletionContextInsensitive"; + +export type CompletionQueries = Partial>; + +/** Qlue-ls endpoint backend configuration */ +export interface BackendConfiguration { + name: string; + url: string; + healthCheckUrl?: string; + engine?: SparqlEngine; + requestMethod?: "GET" | "POST"; + prefixMap: PrefixMap; + default: boolean; + queries: CompletionQueries; + additionalData?: unknown; +} + +/** + * Create a qlue-ls language-server worker and resolve once its WASM is initialized. + * Pass the result to Yasqe/Yasgui via the `languageServerWorker` config option. + */ +export function createQlueLsWorker(): Promise { + return new Promise((resolve) => { + const worker = new QlueLsWorker({ name: "qlue-ls" }); + worker.onmessage = (event) => { + if (event.data?.type === "ready") resolve(worker); + }; + }); +} + +/** Minimal fallback prefixes used when an endpoint exposes none. */ +export const fallbackPrefixMap: PrefixMap = { + rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + rdfs: "http://www.w3.org/2000/01/rdf-schema#", + owl: "http://www.w3.org/2002/07/owl#", + xsd: "http://www.w3.org/2001/XMLSchema#", + skos: "http://www.w3.org/2004/02/skos/core#", + foaf: "http://xmlns.com/foaf/0.1/", + dc: "http://purl.org/dc/elements/1.1/", + dcterms: "http://purl.org/dc/terms/", +}; + +/** + * Completion query templates (qlue-ls Jinja-like templating) used to resolve term completions + * against the endpoint. Generic enough to work on most label-bearing datasets. + */ +const completionQueries: CompletionQueries = { + // TODO: improve default completionQueries + subjectCompletion: `{% include "prefix_declarations" %} +PREFIX rdfs: +PREFIX rdf: +SELECT ?qlue_ls_entity (SAMPLE(?label) as ?qlue_ls_label) WHERE { + ?qlue_ls_entity rdf:type ?type ; rdfs:label ?label . + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(?label, "^{{ search_term }}") + {% endif %} +} +GROUP BY ?qlue_ls_entity +ORDER BY DESC(COUNT(?qlue_ls_entity)) +LIMIT {{ limit }} OFFSET {{ offset }}`, + predicateCompletionContextInsensitive: `{% include "prefix_declarations" %} +SELECT ?qlue_ls_entity ?qlue_ls_score WHERE { + { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_score) WHERE + { + {{local_context}} + } + GROUP BY ?qlue_ls_entity } + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?qlue_ls_entity), "{{ search_term }}", "i") + {% endif %} +} ORDER BY DESC(?qlue_ls_score) +LIMIT {{ limit }} OFFSET {{ offset }}`, + objectCompletionContextInsensitive: `{% include "prefix_declarations" %} +PREFIX rdfs: +SELECT ?qlue_ls_entity (MIN(?name) AS ?qlue_ls_label) (MIN(?alias) AS ?qlue_ls_alias) (MAX(?count) AS ?qlue_ls_count) WHERE { + { + { SELECT ?qlue_ls_entity ?name ?alias ?count WHERE { + { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?count) WHERE { + {{local_context}} + } GROUP BY ?qlue_ls_entity } + ?qlue_ls_entity rdfs:label ?name BIND(?name AS ?alias) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?alias), "^{{ search_term }}") + {% endif %} + } } + } UNION { + { SELECT ?qlue_ls_entity ?name ?alias ?count WHERE { + { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?count) WHERE { + {{local_context}} + } GROUP BY ?qlue_ls_entity } + BIND(?qlue_ls_entity AS ?name) BIND(?qlue_ls_entity AS ?alias) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?alias), "^{{ search_term }}") + {% endif %} + } } + } +} GROUP BY ?qlue_ls_entity ORDER BY DESC(?qlue_ls_count) +LIMIT {{ limit }} OFFSET {{ offset }}`, + predicateCompletionContextSensitive: `{% include "prefix_declarations" %} +PREFIX rdfs: +{% if subject is not variable %} + +SELECT ?qlue_ls_entity (SAMPLE(?qlue_ls_label_or_null) AS ?qlue_ls_label) ?qlue_ls_count WHERE { + { + SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { + {{ local_context }} + } + GROUP BY ?qlue_ls_entity + } + OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } + BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?label) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?label), "{{ search_term }}", "i") + {% endif %} +} +GROUP BY ?qlue_ls_entity ?qlue_ls_count +ORDER BY DESC(?qlue_ls_count) + +{% else %} + +SELECT ?qlue_ls_entity (SAMPLE(?qlue_ls_label_or_null) AS ?qlue_ls_label) ?qlue_ls_count WHERE { + {% if not context %} + { + SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { + {{ local_context }} + } + GROUP BY ?qlue_ls_entity + } + {% else %} + { + SELECT ?qlue_ls_entity (COUNT(DISTINCT {{ subject }}) AS ?qlue_ls_count) WHERE { + {{ context }} {{ local_context }} + } + GROUP BY ?qlue_ls_entity + } + {% endif %} + OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } + BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?label) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?label), "{{ search_term }}", "i") + {% endif %} +} +GROUP BY ?qlue_ls_entity ?qlue_ls_count +ORDER BY DESC(?qlue_ls_count) + +{% endif %} +LIMIT {{ limit }} OFFSET {{ offset }}`, + objectCompletionContextSensitive: `{% include "prefix_declarations" %} +PREFIX rdfs: +SELECT ?qlue_ls_entity ?qlue_ls_label ?qlue_ls_count WHERE { + { + SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { + {{ context }} {{ local_context }} . + } + GROUP BY ?qlue_ls_entity + } + OPTIONAL { + ?qlue_ls_entity rdf:type [ rdfs:label ?qlue_ls_label_or_null ] . + } + OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } + BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?qlue_ls_label) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?qlue_ls_label), "^{{ search_term }}") + {% endif %} +} +ORDER BY DESC(?qlue_ls_count) +LIMIT {{ limit }} OFFSET {{ offset }}`, +}; + +async function fetchPrefixMap(endpoint: string): Promise { + const prefixes: PrefixMap = {}; + try { + const url = new URL(endpoint); + url.searchParams.set( + "query", + `PREFIX sh: + SELECT DISTINCT ?prefix ?namespace + WHERE { [] sh:namespace ?namespace ; sh:prefix ?prefix} + ORDER BY ?prefix`, + ); + const response = await fetch(url.toString(), { + method: "GET", + signal: AbortSignal.timeout(5000), + headers: { Accept: "application/sparql-results+json" }, + }); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const bindings = (await response.json()).results.bindings; + const used = new Set(); + bindings.forEach((b: any) => { + const prefix = b.prefix.value; + const namespace = b.namespace.value; + if (!used.has(namespace) && !prefixes[prefix]) { + prefixes[prefix] = namespace; + used.add(namespace); + } + }); + } catch (error: any) { + console.warn(`Error retrieving prefixes from ${endpoint}:`, error?.message || error); + } + return Object.keys(prefixes).length === 0 ? fallbackPrefixMap : prefixes; +} + +/** Build a flat qlue-ls BackendConfiguration for an endpoint, fetching prefixes when needed. */ +export async function createBackendConf(endpoint: string): Promise { + const prefixMap = await fetchPrefixMap(endpoint); + return { + name: endpoint, + url: endpoint, + prefixMap, + queries: completionQueries, + default: false, + }; +} + +// Avoid re-registering the same backend repeatedly +let lastBackendEndpoint: string | undefined; + +/** + * Register a SPARQL endpoint with the qlue-ls language client and make it the default backend, + */ +export async function configureQlueLsBackend(languageClient: any, endpoint: string): Promise { + if (!languageClient || !endpoint || endpoint === lastBackendEndpoint) return; + lastBackendEndpoint = endpoint; + try { + const backend = await createBackendConf(endpoint); + languageClient.sendNotification("qlueLs/addBackend", backend); + languageClient.sendNotification("qlueLs/updateDefaultBackend", { backendName: backend.name }); + } catch (error) { + lastBackendEndpoint = undefined; // allow retry + console.error("Failed to configure qlue-ls backend for", endpoint, error); + } +} + +// Shared code for the demo pages + +/** Default SPARQL endpoint used across the demo pages. */ +export const DEMO_ENDPOINT = "https://dbpedia.org/sparql"; + +export type DevTheme = "light" | "dark"; + +/** + * Wire the demo page's light/dark switcher: start from the OS preference, and on toggle set the + * `[data-theme]` attribute (which drives the CSS) and call `onThemeChange` (e.g. editor.setTheme). + */ +export function setupThemeToggle(onThemeChange?: (theme: DevTheme) => void): DevTheme { + let currentTheme: DevTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + document.documentElement.dataset.theme = currentTheme; + const button = document.getElementById("darkModeToggle"); + const render = () => { + if (!button) return; + button.textContent = currentTheme === "dark" ? "☀️" : "🌙"; + const title = currentTheme === "dark" ? "Switch to light theme" : "Switch to dark theme"; + button.setAttribute("title", title); + button.setAttribute("aria-label", title); + }; + render(); + button?.addEventListener("click", () => { + currentTheme = currentTheme === "dark" ? "light" : "dark"; + document.documentElement.dataset.theme = currentTheme; + render(); + onThemeChange?.(currentTheme); + }); + return currentTheme; +} diff --git a/dev/qluels.worker.ts b/dev/qluels.worker.ts new file mode 100644 index 00000000..b17143c0 --- /dev/null +++ b/dev/qluels.worker.ts @@ -0,0 +1,36 @@ +/** + * qlue-ls SPARQL language server running as a Web Worker (WASM). + * + * This is consumer-side config: yasqe is language-server agnostic and just receives this worker. + */ +// @ts-ignore qlue-ls is loaded as a wasm module via vite-plugin-wasm +import init, { init_language_server, listen } from "qlue-ls?init"; + +init().then(() => { + // Connection Worker <-> Language Server (WASM) + const wasmInputStream = new TransformStream(); + const wasmOutputStream = new TransformStream(); + const wasmReader = wasmOutputStream.readable.getReader(); + const wasmWriter = wasmInputStream.writable.getWriter(); + + // Initialize and start language server + const server = init_language_server(wasmOutputStream.writable.getWriter()); + listen(server, wasmInputStream.readable.getReader()); + + // Language Client -> Language Server + self.onmessage = function (message) { + wasmWriter.write(JSON.stringify(message.data)); + }; + // Language Server -> Language Client + (async () => { + while (true) { + const { value, done } = await wasmReader.read(); + if (done) break; + self.postMessage(JSON.parse(value)); + } + })(); + + // Signal to the host that the WASM server is initialized and ready to connect + self.postMessage({ type: "ready" }); +}); +export {}; diff --git a/dev/style.css b/dev/style.css new file mode 100644 index 00000000..980cfeff --- /dev/null +++ b/dev/style.css @@ -0,0 +1,87 @@ +body { + font-family: "Roboto", sans-serif; + margin: 0px; +} +.navItem a { + color: #555; + text-decoration: none; + padding: 5px; + padding-right: 15px; +} +.navItem a:hover { + color: #222; +} +.navItem.active a { + color: black; + font-size: 110%; +} +/* Light/dark theme toggle */ +.themeToggle { + margin-left: auto; + align-self: center; + margin-right: 12px; + padding: 5px 10px; + font-size: 18px; + line-height: 1; + cursor: pointer; + border: 1px solid transparent; + border-radius: 8px; + background: rgba(0, 0, 0, 0.06); + user-select: none; + transition: background 0.15s ease; +} +.themeToggle:hover { + background: rgba(0, 0, 0, 0.13); +} +.navBar { + background: #eee; +} +/* Light/dark theme */ +html[data-theme="dark"] body { + background: #1e1e1e; + color: #d4d4d4; +} +html[data-theme="dark"] .navBar { + background: #2d2d30; +} +html[data-theme="dark"] .navItem a, +html[data-theme="dark"] .themeToggle { + color: #ccc; +} +html[data-theme="dark"] .navItem.active a { + color: #fff; +} +html[data-theme="dark"] .logo { + filter: invert(1); +} +html[data-theme="dark"] .themeToggle { + background: rgba(255, 255, 255, 0.1); +} +html[data-theme="dark"] .themeToggle:hover { + background: rgba(255, 255, 255, 0.18); +} +@media (prefers-color-scheme: dark) { + html:not([data-theme="light"]) .themeToggle { + background: rgba(255, 255, 255, 0.1); + } + html:not([data-theme="light"]) .themeToggle:hover { + background: rgba(255, 255, 255, 0.18); + } + html:not([data-theme="light"]) .logo { + filter: invert(1); + } + html:not([data-theme="light"]) body { + background: #1e1e1e; + color: #d4d4d4; + } + html:not([data-theme="light"]) .navBar { + background: #2d2d30; + } + html:not([data-theme="light"]) .navItem a, + html:not([data-theme="light"]) .themeToggle { + color: #ccc; + } + html:not([data-theme="light"]) .navItem.active a { + color: #fff; + } +} diff --git a/dev/yasqe.html b/dev/yasqe.html index 70f8ea85..36ef3d02 100644 --- a/dev/yasqe.html +++ b/dev/yasqe.html @@ -2,129 +2,34 @@ - + YASQE - -
+ - -
+ + + + + + diff --git a/docs/.vitepress/theme/demo.css b/docs/.vitepress/theme/demo.css new file mode 100644 index 00000000..82ce0d01 --- /dev/null +++ b/docs/.vitepress/theme/demo.css @@ -0,0 +1,113 @@ +/* Full-bleed live demo on the home page (rendered with `layout: page`). */ +.yasgui-home .VPPage { + padding: 0; +} + +/* VitePress global CSS resets can inflate the endpoint input (larger font-size / + line-height than Yasgui expects). Pin it back to match the 35px control bar. */ +.yasgui .autocomplete { + font-size: 13px; + line-height: normal; + height: auto; +} + +/* Make the demo a fixed-height viewport so the result area can flex. The flex + column runs from .yasgui down to .yasr_results: the editor keeps its natural + height and the graph plugin grows to fill whatever space is left under it. + (tabPanelsEl and yasrWrapperEl have no class, so we target them as last-child.) */ +.yasgui-demo, +.yasgui-demo__root { + height: calc(100vh - var(--vp-nav-height, 64px)); +} +.yasgui-demo .yasgui { + display: flex; + flex-direction: column; + height: 100%; +} +.yasgui-demo .yasgui > *:last-child, /* tabPanelsEl */ +.yasgui-demo .yasgui .tabPanel.active, +.yasgui-demo .yasgui .tabPanel.active > *:last-child, /* yasrWrapperEl */ +.yasgui-demo .yasgui .yasr { + flex: 1 1 auto; + min-height: 0; + display: flex; + flex-direction: column; +} +.yasgui-demo .yasgui .yasr_results { + flex: 1 1 auto; + min-height: 0; +} +/* The graph plugin hardcodes 500px; fill the leftover space, with a floor so it + stays usable when the editor is large. */ +.yasgui-demo .yasgui-graph-plugin-container { + height: 100%; + min-height: 30vh; +} + +/* VitePress dark mode uses class="dark" on . Override the clear-endpoint + button which has hardcoded light colors in the Yasgui source. */ +.dark .yasgui .clearEndpointBtn { + background-color: #2d2d30 !important; + border-color: #555555 !important; + color: #d4d4d4 !important; +} + +/* .yasgui-home-hero { + max-width: 960px; + margin: 0 auto; + padding: 32px 24px 8px; + text-align: center; +} +.yasgui-home-hero h1 { + font-size: 2.1rem; + font-weight: 700; + line-height: 1.2; + margin: 0 0 8px; + background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-3)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} +.yasgui-home-hero p { + color: var(--vp-c-text-2); + font-size: 1.05rem; + margin: 0 auto; + max-width: 640px; +} +.yasgui-home-hero .yasgui-home-links { + margin-top: 16px; + display: flex; + gap: 12px; + justify-content: center; + flex-wrap: wrap; +} +.yasgui-home-links a { + display: inline-block; + padding: 6px 16px; + border-radius: 20px; + font-size: 0.9rem; + font-weight: 500; + text-decoration: none; + border: 1px solid var(--vp-c-divider); + color: var(--vp-c-text-1); + transition: border-color 0.2s, color 0.2s; +} +.yasgui-home-links a.primary { + background: var(--vp-c-brand-3); + border-color: var(--vp-c-brand-3); + color: #fff; +} +.yasgui-home-links a:hover { + border-color: var(--vp-c-brand-1); + color: var(--vp-c-brand-1); +} +.yasgui-home-links a.primary:hover { + color: #fff; + opacity: 0.9; +} + +.yasgui-home-demo { + padding: 16px 16px 32px; + max-width: 1400px; + margin: 0 auto; +} */ diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 00000000..6237256b --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,13 @@ +// https://vitepress.dev/guide/custom-theme +import type { Theme } from "vitepress"; +import DefaultTheme from "vitepress/theme"; +import YasguiDemo from "./components/YasguiDemo.vue"; +import "./style.css"; +import "./demo.css"; + +export default { + extends: DefaultTheme, + enhanceApp({ app }) { + app.component("YasguiDemo", YasguiDemo); + }, +} satisfies Theme; diff --git a/docs/.vitepress/theme/qluels.ts b/docs/.vitepress/theme/qluels.ts new file mode 100644 index 00000000..d7055a45 --- /dev/null +++ b/docs/.vitepress/theme/qluels.ts @@ -0,0 +1,304 @@ +/// +/** + * Consumer-side qlue-ls configuration for the demo pages. + * + * yasqe/yasgui are language-server agnostic: they only receive a ready LSP `Worker` and expose the + * resulting language client. Everything qlue-ls specific (the WASM worker, the backend/completion + * query config, the `qlueLs/addBackend` extension) lives here, so swapping to another SPARQL + * language server (e.g. SemanticWebLanguageServer/swls) only touches this file. + */ +import QlueLsWorker from "./qluels.worker?worker"; + +export type SparqlEngine = "QLever" | "GraphDB" | "Virtuoso" | "MillenniumDB" | "Blazegraph" | "Jena"; + +export interface PrefixMap { + [key: string]: string; +} + +/** Valid qlue-ls completion query template keys (the `CompletionTemplate` enum, camelCase). */ +export type CompletionTemplate = + | "hover" + | "subjectCompletion" + | "predicateCompletionContextSensitive" + | "predicateCompletionContextInsensitive" + | "objectCompletionContextSensitive" + | "objectCompletionContextInsensitive" + | "valuesCompletionContextSensitive" + | "valuesCompletionContextInsensitive"; + +export type CompletionQueries = Partial>; + +/** Qlue-ls endpoint backend configuration */ +export interface BackendConfiguration { + name: string; + url: string; + healthCheckUrl?: string; + engine?: SparqlEngine; + requestMethod?: "GET" | "POST"; + prefixMap: PrefixMap; + default: boolean; + queries: CompletionQueries; + additionalData?: unknown; +} + +/** + * Create a qlue-ls language-server worker and resolve once its WASM is initialized. + * Pass the result to Yasqe/Yasgui via the `languageServerWorker` config option. + */ +export function createQlueLsWorker(): Promise { + return new Promise((resolve) => { + const worker = new QlueLsWorker({ name: "qlue-ls" }); + worker.onmessage = (event) => { + if (event.data?.type === "ready") resolve(worker); + }; + }); +} + +/** Minimal fallback prefixes used when an endpoint exposes none. */ +export const fallbackPrefixMap: PrefixMap = { + rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + rdfs: "http://www.w3.org/2000/01/rdf-schema#", + owl: "http://www.w3.org/2002/07/owl#", + xsd: "http://www.w3.org/2001/XMLSchema#", + skos: "http://www.w3.org/2004/02/skos/core#", + foaf: "http://xmlns.com/foaf/0.1/", + dc: "http://purl.org/dc/elements/1.1/", + dcterms: "http://purl.org/dc/terms/", +}; + +/** + * Completion query templates (qlue-ls Jinja-like templating) used to resolve term completions + * against the endpoint. Generic enough to work on most label-bearing datasets. + */ +const completionQueries: CompletionQueries = { + // TODO: improve default completionQueries + subjectCompletion: `{% include "prefix_declarations" %} +PREFIX rdfs: +PREFIX rdf: +SELECT ?qlue_ls_entity (SAMPLE(?label) as ?qlue_ls_label) WHERE { + ?qlue_ls_entity rdf:type ?type ; rdfs:label ?label . + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(?label, "^{{ search_term }}") + {% endif %} +} +GROUP BY ?qlue_ls_entity +ORDER BY DESC(COUNT(?qlue_ls_entity)) +LIMIT {{ limit }} OFFSET {{ offset }}`, + predicateCompletionContextInsensitive: `{% include "prefix_declarations" %} +SELECT ?qlue_ls_entity ?qlue_ls_score WHERE { + { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_score) WHERE + { + {{local_context}} + } + GROUP BY ?qlue_ls_entity } + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?qlue_ls_entity), "{{ search_term }}", "i") + {% endif %} +} ORDER BY DESC(?qlue_ls_score) +LIMIT {{ limit }} OFFSET {{ offset }}`, + objectCompletionContextInsensitive: `{% include "prefix_declarations" %} +PREFIX rdfs: +SELECT ?qlue_ls_entity (MIN(?name) AS ?qlue_ls_label) (MIN(?alias) AS ?qlue_ls_alias) (MAX(?count) AS ?qlue_ls_count) WHERE { + { + { SELECT ?qlue_ls_entity ?name ?alias ?count WHERE { + { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?count) WHERE { + {{local_context}} + } GROUP BY ?qlue_ls_entity } + ?qlue_ls_entity rdfs:label ?name BIND(?name AS ?alias) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?alias), "^{{ search_term }}") + {% endif %} + } } + } UNION { + { SELECT ?qlue_ls_entity ?name ?alias ?count WHERE { + { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?count) WHERE { + {{local_context}} + } GROUP BY ?qlue_ls_entity } + BIND(?qlue_ls_entity AS ?name) BIND(?qlue_ls_entity AS ?alias) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?alias), "^{{ search_term }}") + {% endif %} + } } + } +} GROUP BY ?qlue_ls_entity ORDER BY DESC(?qlue_ls_count) +LIMIT {{ limit }} OFFSET {{ offset }}`, + predicateCompletionContextSensitive: `{% include "prefix_declarations" %} +PREFIX rdfs: +{% if subject is not variable %} + +SELECT ?qlue_ls_entity (SAMPLE(?qlue_ls_label_or_null) AS ?qlue_ls_label) ?qlue_ls_count WHERE { + { + SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { + {{ local_context }} + } + GROUP BY ?qlue_ls_entity + } + OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } + BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?label) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?label), "{{ search_term }}", "i") + {% endif %} +} +GROUP BY ?qlue_ls_entity ?qlue_ls_count +ORDER BY DESC(?qlue_ls_count) + +{% else %} + +SELECT ?qlue_ls_entity (SAMPLE(?qlue_ls_label_or_null) AS ?qlue_ls_label) ?qlue_ls_count WHERE { + {% if not context %} + { + SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { + {{ local_context }} + } + GROUP BY ?qlue_ls_entity + } + {% else %} + { + SELECT ?qlue_ls_entity (COUNT(DISTINCT {{ subject }}) AS ?qlue_ls_count) WHERE { + {{ context }} {{ local_context }} + } + GROUP BY ?qlue_ls_entity + } + {% endif %} + OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } + BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?label) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?label), "{{ search_term }}", "i") + {% endif %} +} +GROUP BY ?qlue_ls_entity ?qlue_ls_count +ORDER BY DESC(?qlue_ls_count) + +{% endif %} +LIMIT {{ limit }} OFFSET {{ offset }}`, + objectCompletionContextSensitive: `{% include "prefix_declarations" %} +PREFIX rdfs: +SELECT ?qlue_ls_entity ?qlue_ls_label ?qlue_ls_count WHERE { + { + SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { + {{ context }} {{ local_context }} . + } + GROUP BY ?qlue_ls_entity + } + OPTIONAL { + ?qlue_ls_entity rdf:type [ rdfs:label ?qlue_ls_label_or_null ] . + } + OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } + BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?qlue_ls_label) + {% if search_term_uncompressed %} + FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) + {% elif search_term %} + FILTER REGEX(STR(?qlue_ls_label), "^{{ search_term }}") + {% endif %} +} +ORDER BY DESC(?qlue_ls_count) +LIMIT {{ limit }} OFFSET {{ offset }}`, +}; + +async function fetchPrefixMap(endpoint: string): Promise { + const prefixes: PrefixMap = {}; + try { + const url = new URL(endpoint); + url.searchParams.set( + "query", + `PREFIX sh: + SELECT DISTINCT ?prefix ?namespace + WHERE { [] sh:namespace ?namespace ; sh:prefix ?prefix} + ORDER BY ?prefix`, + ); + const response = await fetch(url.toString(), { + method: "GET", + signal: AbortSignal.timeout(5000), + headers: { Accept: "application/sparql-results+json" }, + }); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const bindings = (await response.json()).results.bindings; + const used = new Set(); + bindings.forEach((b: any) => { + const prefix = b.prefix.value; + const namespace = b.namespace.value; + if (!used.has(namespace) && !prefixes[prefix]) { + prefixes[prefix] = namespace; + used.add(namespace); + } + }); + } catch (error: any) { + console.warn(`Error retrieving prefixes from ${endpoint}:`, error?.message || error); + } + return Object.keys(prefixes).length === 0 ? fallbackPrefixMap : prefixes; +} + +/** Build a flat qlue-ls BackendConfiguration for an endpoint, fetching prefixes when needed. */ +export async function createBackendConf(endpoint: string): Promise { + const prefixMap = await fetchPrefixMap(endpoint); + return { + name: endpoint, + url: endpoint, + prefixMap, + queries: completionQueries, + default: false, + }; +} + +// Avoid re-registering the same backend repeatedly +let lastBackendEndpoint: string | undefined; + +/** + * Register a SPARQL endpoint with the qlue-ls language client and make it the default backend, + */ +export async function configureQlueLsBackend(languageClient: any, endpoint: string): Promise { + if (!languageClient || !endpoint || endpoint === lastBackendEndpoint) return; + lastBackendEndpoint = endpoint; + try { + const backend = await createBackendConf(endpoint); + languageClient.sendNotification("qlueLs/addBackend", backend); + languageClient.sendNotification("qlueLs/updateDefaultBackend", { backendName: backend.name }); + } catch (error) { + lastBackendEndpoint = undefined; // allow retry + console.error("Failed to configure qlue-ls backend for", endpoint, error); + } +} + +// Shared code for the demo pages + +/** Default SPARQL endpoint used across the demo pages. */ +export const DEMO_ENDPOINT = "https://dbpedia.org/sparql"; + +export type DevTheme = "light" | "dark"; + +/** + * Wire the demo page's light/dark switcher: start from the OS preference, and on toggle set the + * `[data-theme]` attribute (which drives the CSS) and call `onThemeChange` (e.g. editor.setTheme). + */ +export function setupThemeToggle(onThemeChange?: (theme: DevTheme) => void): DevTheme { + let currentTheme: DevTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + document.documentElement.dataset.theme = currentTheme; + const button = document.getElementById("darkModeToggle"); + const render = () => { + if (!button) return; + button.textContent = currentTheme === "dark" ? "☀️" : "🌙"; + const title = currentTheme === "dark" ? "Switch to light theme" : "Switch to dark theme"; + button.setAttribute("title", title); + button.setAttribute("aria-label", title); + }; + render(); + button?.addEventListener("click", () => { + currentTheme = currentTheme === "dark" ? "light" : "dark"; + document.documentElement.dataset.theme = currentTheme; + render(); + onThemeChange?.(currentTheme); + }); + return currentTheme; +} diff --git a/docs/.vitepress/theme/qluels.worker.ts b/docs/.vitepress/theme/qluels.worker.ts new file mode 100644 index 00000000..b17143c0 --- /dev/null +++ b/docs/.vitepress/theme/qluels.worker.ts @@ -0,0 +1,36 @@ +/** + * qlue-ls SPARQL language server running as a Web Worker (WASM). + * + * This is consumer-side config: yasqe is language-server agnostic and just receives this worker. + */ +// @ts-ignore qlue-ls is loaded as a wasm module via vite-plugin-wasm +import init, { init_language_server, listen } from "qlue-ls?init"; + +init().then(() => { + // Connection Worker <-> Language Server (WASM) + const wasmInputStream = new TransformStream(); + const wasmOutputStream = new TransformStream(); + const wasmReader = wasmOutputStream.readable.getReader(); + const wasmWriter = wasmInputStream.writable.getWriter(); + + // Initialize and start language server + const server = init_language_server(wasmOutputStream.writable.getWriter()); + listen(server, wasmInputStream.readable.getReader()); + + // Language Client -> Language Server + self.onmessage = function (message) { + wasmWriter.write(JSON.stringify(message.data)); + }; + // Language Server -> Language Client + (async () => { + while (true) { + const { value, done } = await wasmReader.read(); + if (done) break; + self.postMessage(JSON.parse(value)); + } + })(); + + // Signal to the host that the WASM server is initialized and ready to connect + self.postMessage({ type: "ready" }); +}); +export {}; diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 00000000..68fb09b5 --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,125 @@ +/** + * Customize default theme styling by overriding CSS variables: + * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css + */ + +/** + * Colors + * + * Each colors have exact same color scale system with 3 levels of solid + * colors with different brightness, and 1 soft color. + * - `XXX-1`: The most solid color used mainly for colored text. It must + * satisfy the contrast ratio against when used on top of `XXX-soft`. + * - `XXX-2`: The color used mainly for hover state of the button. + * - `XXX-3`: The color for solid background, such as bg color of the button. + * It must satisfy the contrast ratio with pure white (#ffffff) text on + * top of it. + * - `XXX-soft`: The color used for subtle background such as custom container + * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors + * on top of it. + * The soft color must be semi transparent alpha channel. This is crucial + * because it allows adding multiple "soft" colors on top of each other + * to create a accent, such as when having inline code block inside + * custom containers. + * - `default`: The color used purely for subtle indication without any + * special meanings attached to it such as bg color for menu hover state. + * - `brand`: Used for primary brand colors, such as link text, button with + * brand theme, etc. + * - `tip`: Used to indicate useful information. The default theme uses the + * brand color for this by default. + * - `warning`: Used to indicate warning to the users. Used in custom + * container, badges, etc. + * - `danger`: Used to show error, or dangerous message to the users. Used + * in custom container, badges, etc. + */ + +:root { + --vp-c-default-1: var(--vp-c-gray-1); + --vp-c-default-2: var(--vp-c-gray-2); + --vp-c-default-3: var(--vp-c-gray-3); + --vp-c-default-soft: var(--vp-c-gray-soft); + + --vp-c-brand-1: var(--vp-c-indigo-1); + --vp-c-brand-2: var(--vp-c-indigo-2); + --vp-c-brand-3: var(--vp-c-indigo-3); + --vp-c-brand-soft: var(--vp-c-indigo-soft); + + --vp-c-tip-1: var(--vp-c-brand-1); + --vp-c-tip-2: var(--vp-c-brand-2); + --vp-c-tip-3: var(--vp-c-brand-3); + --vp-c-tip-soft: var(--vp-c-brand-soft); + + --vp-c-warning-1: var(--vp-c-yellow-1); + --vp-c-warning-2: var(--vp-c-yellow-2); + --vp-c-warning-3: var(--vp-c-yellow-3); + --vp-c-warning-soft: var(--vp-c-yellow-soft); + + --vp-c-danger-1: var(--vp-c-red-1); + --vp-c-danger-2: var(--vp-c-red-2); + --vp-c-danger-3: var(--vp-c-red-3); + --vp-c-danger-soft: var(--vp-c-red-soft); +} + +/** + * Component: Button + */ + +:root { + --vp-button-brand-border: transparent; + --vp-button-brand-text: var(--vp-c-white); + --vp-button-brand-bg: var(--vp-c-brand-3); + --vp-button-brand-hover-border: transparent; + --vp-button-brand-hover-text: var(--vp-c-white); + --vp-button-brand-hover-bg: var(--vp-c-brand-2); + --vp-button-brand-active-border: transparent; + --vp-button-brand-active-text: var(--vp-c-white); + --vp-button-brand-active-bg: var(--vp-c-brand-1); +} + +/** + * Component: Home + */ + +:root { + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient( + 120deg, + #bd34fe 30%, + #41d1ff + ); + + --vp-home-hero-image-background-image: linear-gradient( + -45deg, + #bd34fe 50%, + #47caff 50% + ); + --vp-home-hero-image-filter: blur(44px); +} + +@media (min-width: 640px) { + :root { + --vp-home-hero-image-filter: blur(56px); + } +} + +@media (min-width: 960px) { + :root { + --vp-home-hero-image-filter: blur(68px); + } +} + +/** + * Component: Custom Block + */ + +:root { + --vp-custom-block-tip-border: transparent; + --vp-custom-block-tip-text: var(--vp-c-text-1); + --vp-custom-block-tip-bg: var(--vp-c-brand-soft); + --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); +} + +.DocSearch { + --docsearch-primary-color: var(--vp-c-brand-1) !important; +} + diff --git a/docs/docs/api.md b/docs/docs/api.md new file mode 100644 index 00000000..65334991 --- /dev/null +++ b/docs/docs/api.md @@ -0,0 +1,47 @@ +# Yasqe API + +`yasgui.yasqe` is the shared [Yasqe](./yasqe) instance, so this reference applies to both the full +app and the standalone editor. + +**Constructor:** `new Yasqe(parentElement, config?)` + +## Methods + +| method | description | +| --- | --- | +| `getValue()` / `setValue(q)` | get / set the query string | +| `query(config?)` | run the query (returns a Promise) | +| `abortQuery()` | abort the running query | +| `getPrefixesFromQuery()` | `{ prefix: iri }` from the query's `PREFIX` lines | +| `getLanguageClient()` | the `monaco-languageclient` (or `undefined`), send custom LSP requests | +| `setTheme("light" \| "dark")` | switch the editor + chrome theme | +| `setSize(height?, width?)` | resize the editor (e.g. `"300px"`) | +| `refresh()` / `focus()` | re-layout / focus | +| `getQueryType()` | the query type (`SELECT`, `CONSTRUCT`, `ASK`, …) | +| `getQueryMode()` | `"query"` vs `"update"` | +| `destroy()` | tear down the instance | +| `on(event, handler)` / `off(...)` | subscribe / unsubscribe (handlers are instance-first) | + +## Events + +`query`, `queryBefore`, `queryResponse`, `queryResults`, `queryAbort`, `error`, `blur`, `change`, +`resize`, `autocompletionShown`, `autocompletionClose`. + +::: warning Instance-first handlers +Handlers receive the Yasqe instance as their first argument: `(yasqeInstance, ...payload)`. For +example `queryResponse` is `(yasqe, response, duration)`. +::: + +```ts +yasqe.on("query", (yasqe, req) => console.log("running", req)); +yasqe.on("queryResponse", (yasqe, response, duration) => console.log(response, duration)); +yasqe.on("error", (yasqe, error) => console.error(error)); +``` + +## Common config + +`value`, `theme`, `editorOptions`, `requestConfig`, `editorHeight`, `resizeable`, `showQueryButton`, +`persistenceId`, `languageServerWorker`, `onLanguageClientReady`. + +See [Yasqe](./yasqe) for descriptions, [Request configuration](./request-config) for `requestConfig` +and [Monaco editor options](./editor-options) for `editorOptions`. diff --git a/docs/docs/build.md b/docs/docs/build.md new file mode 100644 index 00000000..1d576251 --- /dev/null +++ b/docs/docs/build.md @@ -0,0 +1,40 @@ +# Build from source + +The repository is an npm workspaces monorepo with four packages under `packages/`: `utils`, `yasqe`, +`yasr` and `yasgui`. + +```sh +npm i +npm run dev # dev server with the demo pages (dev/index.html, yasqe.html, yasr.html) +npm run build # build the demo + all four library packages +npm run build:lib # only the libraries (packages/*/build) +``` + +## What the library build emits + +For each package, `build:lib` emits into `packages//build`: + +- ESM (`*.esm.js`), the recommended entry point. +- CSS (`*.min.css`). +- TypeScript declarations. +- The editor / language-server worker assets. + +Asset URLs use a relative base (`base: "./"`) so they resolve in any consuming bundler. + +## The `dev/` reference + +The `dev/` folder is a working reference for integrating the packages: + +- `dev/qluels.ts` + `dev/qluels.worker.ts` show the qlue-ls integration. +- `dev/index.html` / `yasqe.html` / `yasr.html` show Yasgui / Yasqe / Yasr with the language server + and a light/dark switcher. + +## The documentation site + +This site is built with [VitePress](https://vitepress.dev) from the `docs/` folder: + +```sh +npm run docs:dev # local preview with hot reload +npm run docs:build # build the static site into docs/.vitepress/dist +npm run docs:preview # preview the built site +``` diff --git a/docs/docs/editor-options.md b/docs/docs/editor-options.md new file mode 100644 index 00000000..eed1fda8 --- /dev/null +++ b/docs/docs/editor-options.md @@ -0,0 +1,37 @@ +# Monaco editor options + +Pass any [Monaco editor options](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneEditorConstructionOptions.html) +via `editorOptions`. They are **deep-merged over** Yasqe's defaults. + +```ts +new Yasqe(el, { + editorOptions: { + lineNumbers: "off", + wordWrap: "off", + fontSize: 16, + minimap: { enabled: true }, + renderWhitespace: "all", + }, +}); +``` + +Via Yasgui, forward them through the `yasqe` option: + +```ts +new Yasgui(el, { + yasqe: { editorOptions: { fontSize: 16 } }, +}); +``` + +## Defaults + +Yasqe's defaults already enable: + +- line numbers +- word wrap +- bracket matching +- code folding +- the VS Code right-click context menu (including **Format Document**) +- semantic highlighting + +You only need `editorOptions` to override these or to enable extra Monaco features. diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md new file mode 100644 index 00000000..1733351c --- /dev/null +++ b/docs/docs/getting-started.md @@ -0,0 +1,154 @@ +# Getting started + +This guide embeds the full Yasgui app with the recommended **qlue-ls** language server. If you only +need the editor or the result viewer, see [Yasqe](./yasqe) and [Yasr](./yasr). + +## 1. Install + +```bash +npm install @zazuko/yasgui # or @zazuko/yasqe / @zazuko/yasr individually +``` + +To use the qlue-ls language server (recommended), also add it and the Vite WASM plugin to **your +app**: + +```bash +npm install qlue-ls +npm install -D vite-plugin-wasm +``` + +The `@zazuko/*` packages are **self-contained ESM bundles** (Monaco and the language client are +bundled in), you do **not** need to install `monaco-editor`. They also ship a UMD build, but the +**ESM build is the one that works** (Monaco loads its workers via `import.meta.url`, which UMD +can't do). Use a modern bundler (Vite recommended). + +Each package ships its own CSS that you must import once: + +```js +import "@zazuko/yasgui/build/yasgui.min.css"; +// or for standalone use: +// import "@zazuko/yasqe/build/yasqe.min.css"; +// import "@zazuko/yasr/build/yasr.min.css"; +``` + +## 2. Bundler setup (Vite) + +Because the qlue-ls worker loads WebAssembly, your app's Vite config needs `vite-plugin-wasm` and +ES-module workers: + +```ts +// vite.config.ts +import { defineConfig } from "vite"; +import wasm from "vite-plugin-wasm"; + +export default defineConfig({ + plugins: [wasm()], + worker: { + format: "es", + plugins: () => [wasm()], + }, +}); +``` + +::: info +If you don't use a language server at all, none of this is needed, the editor still does syntax +highlighting. +::: + +## 3. Set up the qlue-ls language server + +The language server runs in a **Web Worker**. Create two small files in your app. They are the only +files you change to switch to a different SPARQL language server later. See the +[Language server](./language-server) page for the full versions; here is the minimal setup: + +:::code-group + +```ts [qlue-ls.ts] +// qlue-ls.ts +import QlueLsWorker from "./qlue-ls.worker?worker"; + +/** Create a qlue-ls worker and resolve once its WASM is ready. */ +export function createQlueLsWorker(): Promise { + return new Promise((resolve) => { + const worker = new QlueLsWorker({ name: "qlue-ls" }); + worker.onmessage = (e) => { + if (e.data?.type === "ready") resolve(worker); + }; + }); +} + +/** Register a SPARQL endpoint as the default qlue-ls backend so completions resolve against it. */ +let last: string | undefined; +export async function configureQlueLsBackend(languageClient: any, endpoint: string) { + if (!languageClient || !endpoint || endpoint === last) return; + last = endpoint; + const backend = { + name: endpoint, // backend label/id (the endpoint URL is fine) + url: endpoint, + default: false, + prefixMap: { + rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + rdfs: "http://www.w3.org/2000/01/rdf-schema#", + }, + queries: {}, // optional completion-query templates, see the Language server page + }; + // qlueLs/addBackend and qlueLs/updateDefaultBackend are LSP notifications (no response) + languageClient.sendNotification("qlueLs/addBackend", backend); + languageClient.sendNotification("qlueLs/updateDefaultBackend", { backendName: backend.name }); +} +``` + +```ts [qlue-ls.worker.ts] +// @ts-ignore qlue-ls is loaded as a WASM module via vite-plugin-wasm +import init, { init_language_server, listen } from "qlue-ls?init"; + +init().then(() => { + const input = new TransformStream(); + const output = new TransformStream(); + const reader = output.readable.getReader(); + const writer = input.writable.getWriter(); + + const server = init_language_server(output.writable.getWriter()); + listen(server, input.readable.getReader()); + + self.onmessage = (msg) => writer.write(JSON.stringify(msg.data)); + (async () => { + while (true) { + const { value, done } = await reader.read(); + if (done) break; + self.postMessage(JSON.parse(value)); + } + })(); + + self.postMessage({ type: "ready" }); // tell the host the WASM is initialized +}); +export {}; +``` + +::: + +## 4. Mount Yasgui + +```ts +import Yasgui from "@zazuko/yasgui"; +import "@zazuko/yasgui/build/yasgui.min.css"; +import { createQlueLsWorker, configureQlueLsBackend } from "./qlue-ls"; + +const yasgui = new Yasgui(document.getElementById("yasgui")!, { + requestConfig: { endpoint: "https://dbpedia.org/sparql" }, + + // Provide the language server worker (forwarded to the shared editor) + languageServerWorker: createQlueLsWorker, + + // Fires on load, tab switch, and endpoint edits, defined once, applies to all tabs. + // Use it to point the language server at the active endpoint. + onEndpointChange: (yasgui, endpoint) => + configureQlueLsBackend(yasgui.yasqe?.getLanguageClient(), endpoint), +}); +``` + +That is the same setup that powers the [live demo](/). From here: + +- [Yasgui (full app)](./yasgui) · the complete config reference. +- [Language server](./language-server) · richer completion queries and using a different LSP. +- [Theming](./theming) · light/dark wiring. diff --git a/docs/docs/introduction.md b/docs/docs/introduction.md new file mode 100644 index 00000000..ad6fa8a6 --- /dev/null +++ b/docs/docs/introduction.md @@ -0,0 +1,47 @@ +# What is Yasgui? + +**Yasgui (Yet Another SPARQL GUI)** is a web-based interface for writing, running and exploring +SPARQL queries against any endpoint. It is built from three packages you can use together or +independently: + +| Package | npm | What it is | +| --- | --- | --- | +| **Yasqe** | `@zazuko/yasqe` | The SPARQL query **editor** (Monaco-based, with optional LSP) | +| **Yasr** | `@zazuko/yasr` | The SPARQL **result** viewer (table, response, geo, …) | +| **Yasgui** | `@zazuko/yasgui` | The full **app**: tabs, endpoint selector, Yasqe + Yasr wired together | + +Try it now on the [live demo](/). Then head to [Getting started](./getting-started) to embed it in +your own app. + +## Monaco editor with a language server + +The editor is **Monaco** (the editor that powers VS Code). Syntax highlighting works out of the box +through a TextMate grammar. The smart features, autocompletion, diagnostics, hover, formatting and +semantic highlighting, come from a **SPARQL language server (LSP)** that *you* provide. + +The recommended language server is [**qlue-ls**](https://github.com/IoannisNezis/Qlue-ls), a fast +WASM SPARQL language server, but Yasqe and Yasgui are language-server **agnostic**, so you can plug +in any LSP `Worker`. + +::: tip Key idea +Yasqe and Yasgui never import a specific language server. You pass them a ready LSP `Worker`, and +they wire a `monaco-languageclient` to it. All server-specific configuration lives in your app. This +keeps the libraries small and lets you swap servers later. +::: + +## Key features + +- **Advanced query editor** · SPARQL syntax highlighting, smart autocomplete, query formatting, + prefix management, bracket matching and folding. +- **Powerful result viewers** · interactive tables, graph visualizations, geographic maps and a raw + response viewer. +- **Multiple tabs** · work with several queries at once, each with its own endpoint. +- **Light and dark themes** · follows the OS preference, or set it explicitly. +- **Persistent storage** · queries, tabs and results survive a page reload via `localStorage`. +- **Developer friendly** · small ESM packages, an event system and a documented API. + +## Next steps + +- [Getting started](./getting-started) · install and embed Yasgui in your app. +- [Yasgui (full app)](./yasgui) · configure the complete interface. +- [Language server](./language-server) · set up qlue-ls or plug in your own. diff --git a/docs/docs/language-server.md b/docs/docs/language-server.md new file mode 100644 index 00000000..b3c0c71b --- /dev/null +++ b/docs/docs/language-server.md @@ -0,0 +1,143 @@ +# Language server + +Smart features, autocompletion, diagnostics, hover, formatting and semantic highlighting, come from +a **SPARQL language server (LSP)** running in a Web Worker. Yasqe and Yasgui are language-server +**agnostic**: you pass them a ready LSP `Worker` and they wire a `monaco-languageclient` to it. + +The recommended server is [**qlue-ls**](https://github.com/IoannisNezis/Qlue-ls), a fast WASM SPARQL +language server. + +## The worker + +qlue-ls resolves completions against a registered **backend**. Register one and make it the default +whenever the active endpoint changes + +:::code-group + +```ts [qlue-ls.ts] +import QlueLsWorker from "./qlue-ls.worker?worker"; + +export function createQlueLsWorker(): Promise { + return new Promise((resolve) => { + const worker = new QlueLsWorker({ name: "qlue-ls" }); + worker.onmessage = (e) => { + if (e.data?.type === "ready") resolve(worker); + }; + }); +} + +let last: string | undefined; +export async function configureQlueLsBackend(languageClient: any, endpoint: string) { + if (!languageClient || !endpoint || endpoint === last) return; + last = endpoint; + const backend = await createBackendConf(endpoint); + // qlueLs/addBackend and qlueLs/updateDefaultBackend are LSP notifications (no response) + languageClient.sendNotification("qlueLs/addBackend", backend); + languageClient.sendNotification("qlueLs/updateDefaultBackend", { backendName: backend.name }); +} +``` + +```ts [qlue-ls.worker.ts] +// @ts-ignore qlue-ls is loaded as a WASM module via vite-plugin-wasm +import init, { init_language_server, listen } from "qlue-ls?init"; + +init().then(() => { + // Connection Worker <-> Language Server (WASM) + const wasmInputStream = new TransformStream(); + const wasmOutputStream = new TransformStream(); + const wasmReader = wasmOutputStream.readable.getReader(); + const wasmWriter = wasmInputStream.writable.getWriter(); + + // Initialize and start the language server + const server = init_language_server(wasmOutputStream.writable.getWriter()); + listen(server, wasmInputStream.readable.getReader()); + + // Language Client -> Language Server + self.onmessage = (message) => wasmWriter.write(JSON.stringify(message.data)); + // Language Server -> Language Client + (async () => { + while (true) { + const { value, done } = await wasmReader.read(); + if (done) break; + self.postMessage(JSON.parse(value)); + } + })(); + + // Signal to the host that the WASM server is initialized and ready + self.postMessage({ type: "ready" }); +}); +export {}; +``` + +::: + +### The backend object + +The qlue-ls `BackendConfiguration` is flat and camelCase: + +| field | required | meaning | +| --- | --- | --- | +| `name` | yes | backend identifier / label | +| `url` | yes | SPARQL endpoint URL | +| `default` | — | whether it is the default backend | +| `prefixMap` | — | `{ prefix: namespace }` used for prefix completion | +| `queries` | — | completion-query templates, keyed by qlue-ls `CompletionTemplate` (`subjectCompletion`, `predicateCompletionContextSensitive`, `objectCompletionContextSensitive`, …). Needed for **term** completion. An empty object still gives prefix/keyword completion. | +| `engine`, `requestMethod`, `healthCheckUrl` | — | optional | + +```ts +function createBackendConf(endpoint: string) { + return { + name: endpoint, + url: endpoint, + default: false, + prefixMap: { + rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + rdfs: "http://www.w3.org/2000/01/rdf-schema#", + }, + queries: {}, // add CompletionTemplate entries for subject/predicate/object completion + }; +} +``` + +::: tip Auto-discovering prefixes +Many endpoints expose their prefixes via `sh:namespace` / `sh:prefix`. You can query that at +backend-creation time and fall back to a minimal prefix map when none are returned. The live demo's +`qluels.ts` does exactly this. +::: + +## Hooking it up + +- With **Yasgui**, register backends from `onEndpointChange` (fires on load, tab switch and endpoint + edits, once for the whole app): + + ```ts [main.ts] + new Yasgui(el, { + languageServerWorker: createQlueLsWorker, + onEndpointChange: (yasgui, endpoint) => + configureQlueLsBackend(yasgui.yasqe?.getLanguageClient(), endpoint), + }); + ``` + +- With **Yasqe**, use `onLanguageClientReady`: + + ```ts [main.ts] + new Yasqe(el, { + languageServerWorker: createQlueLsWorker, + onLanguageClientReady: (lc) => configureQlueLsBackend(lc, endpoint), + }); + ``` + +`yasqe.getLanguageClient()` returns the underlying `monaco-languageclient` so you can send any LSP +request or custom notification. + +## Using a different language server + +Yasqe and Yasgui only need a ready LSP `Worker`. To use, for example, +[swls](https://github.com/SemanticWebLanguageServer/swls) instead of qlue-ls: + +1. Replace `qlue-ls.worker.ts` / `qlue-ls.ts` with that server's worker and connection. +2. Pass its worker factory as `languageServerWorker`. +3. In `onEndpointChange` / `onLanguageClientReady`, send whatever that server needs to target an + endpoint (its own custom requests). + +No changes to the `@zazuko/*` packages are required. diff --git a/docs/docs/request-config.md b/docs/docs/request-config.md new file mode 100644 index 00000000..952a77f1 --- /dev/null +++ b/docs/docs/request-config.md @@ -0,0 +1,35 @@ +# Request configuration + +`requestConfig` controls how queries are sent to the endpoint. It is accepted by both +[Yasqe](./yasqe) and [Yasgui](./yasgui) (where it sets the default for every tab). + +Every field may be a value **or** a `(yasqe) => value` function, so you can compute it per request. + +```ts +new Yasgui(el, { + requestConfig: { + endpoint: "https://dbpedia.org/sparql", + method: "POST", + headers: () => ({ Authorization: `Bearer ${getToken()}` }), + withCredentials: false, + }, +}); +``` + +| field | default | description | +| --- | --- | --- | +| `endpoint` | — | SPARQL endpoint URL | +| `method` | `"POST"` | `"GET"` or `"POST"` | +| `acceptHeaderSelect` | `application/sparql-results+json,*/*;q=0.9` | accept header for `SELECT` / `ASK` | +| `acceptHeaderGraph` | `application/n-triples,*/*;q=0.9` | accept header for `CONSTRUCT` / `DESCRIBE` | +| `acceptHeaderUpdate` | `text/plain,*/*;q=0.9` | accept header for updates | +| `namedGraphs` / `defaultGraphs` | `[]` | graph URIs | +| `args` | `[]` | extra `{ name, value }` request args | +| `headers` | `{}` | extra HTTP headers | +| `withCredentials` | `false` | send credentials (cookies) with the request | +| `adjustQueryBeforeRequest` | `false` | `(yasqe) => string` to rewrite the query before sending | + +::: tip CORS +If the endpoint does not return CORS headers, set a `corsProxy` on Yasgui rather than fighting the +request config. See [Yasgui · CORS](./yasgui#cors). +::: diff --git a/docs/docs/theming.md b/docs/docs/theming.md new file mode 100644 index 00000000..cd1ddf50 --- /dev/null +++ b/docs/docs/theming.md @@ -0,0 +1,34 @@ +# Theming + +Yasgui supports light and dark themes through two layers, both following standard mechanisms: + +1. **The Monaco editor**, set via `theme: "light" | "dark"` in config, or at runtime with + `yasqe.setTheme("dark")`. The default follows the OS `prefers-color-scheme`. +2. **The surrounding chrome CSS** (buttons, tabs, result table), driven by a `data-theme` attribute + on `` **and** the OS preference. `setTheme()` sets `document.documentElement.dataset.theme`. + +The built-in CSS auto-adapts to dark mode: + +- `@media (prefers-color-scheme: dark)`, follows the OS automatically. +- `html[data-theme="dark"]`, explicit opt-in; `html[data-theme="light"]` forces light. + +## A minimal app-level toggle + +```ts +function setTheme(theme: "light" | "dark") { + document.documentElement.dataset.theme = theme; // drives the chrome CSS + yasgui.yasqe?.setTheme(theme); // drives the Monaco editor +} +``` + +Set the initial theme from the OS preference: + +```ts +const initial = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; +setTheme(initial); +``` + +::: tip Integrating with a framework's dark mode +If your app already has a dark-mode switch (Tailwind, VitePress, etc.), just call both lines from its +change handler. The [live demo](/) wires Yasgui's theme to VitePress' own dark-mode toggle this way. +::: diff --git a/docs/docs/yasgui.md b/docs/docs/yasgui.md new file mode 100644 index 00000000..565ea9de --- /dev/null +++ b/docs/docs/yasgui.md @@ -0,0 +1,71 @@ +# Yasgui (full app) + +`@zazuko/yasgui` is the complete app: query tabs, an endpoint selector, and Yasqe + Yasr wired +together. + +```ts +import Yasgui from "@zazuko/yasgui"; +import "@zazuko/yasgui/build/yasgui.min.css"; +import { createQlueLsWorker, configureQlueLsBackend } from "./qlue-ls"; + +const yasgui = new Yasgui(document.getElementById("yasgui")!, { + requestConfig: { endpoint: "https://dbpedia.org/sparql" }, + languageServerWorker: createQlueLsWorker, + onEndpointChange: (yasgui, endpoint) => + configureQlueLsBackend(yasgui.yasqe?.getLanguageClient(), endpoint), +}); +``` + +## One shared editor + +Yasgui uses **one shared Monaco editor** across all tabs (Monaco can only be initialized once per +page) and swaps its content and endpoint on tab switch. + +- `yasgui.yasqe` is that shared [Yasqe](./yasqe) instance. +- `yasgui.yasqe.getLanguageClient()` returns the underlying `monaco-languageclient` so you can send + any LSP request. + +## Configuration + +| option | type | description | +| --- | --- | --- | +| `requestConfig` | `RequestConfig` | default endpoint & request settings (see [Request configuration](./request-config)) | +| `languageServerWorker` | `Worker \| () => Worker \| Promise` | the LSP worker; omit for highlighting-only | +| `onEndpointChange` | `(yasgui, endpoint) => void` | called when the active endpoint changes | +| `yasqe` | `Partial` | forwarded to the shared editor (e.g. `{ theme, editorOptions }`) | +| `yasr` | `Partial` | result-viewer config | +| `corsProxy` | `string` | optional CORS proxy URL | +| `persistenceId` | `string \| fn \| null` | localStorage namespace; `null` disables persistence | + +## The endpoint selector + +Yasgui ships with a catalogue of popular public SPARQL endpoints in the endpoint dropdown, +including DBpedia, Wikidata, UniProt, the QLever services and many life-science datasets. Users can +type any other endpoint URL directly. + +When the active endpoint changes (on load, tab switch, or when the user edits the endpoint), +`onEndpointChange` fires once for the whole app. Use it to point the language server at the active +endpoint, as shown above. + +## CORS + +Public endpoints usually send the right CORS headers. For endpoints that don't, set a `corsProxy`: + +```ts +new Yasgui(el, { corsProxy: "https://corsproxy.example/?" }); +``` + +The proxy URL is prepended to the request URL. + +## Persistence + +By default Yasgui persists tabs, queries and the last results to `localStorage` under a namespace +derived from the container element id. Pass `persistenceId: null` to disable persistence, or a +string / function to control the namespace. + +## See also + +- [Request configuration](./request-config) · how queries are sent. +- [Theming](./theming) · light/dark wiring. +- [Monaco editor options](./editor-options) · customize the editor. +- [Yasqe API](./api) · methods and events on the shared editor. diff --git a/docs/docs/yasqe.md b/docs/docs/yasqe.md new file mode 100644 index 00000000..1daf8701 --- /dev/null +++ b/docs/docs/yasqe.md @@ -0,0 +1,46 @@ +# Yasqe (editor) + +`@zazuko/yasqe` is the SPARQL query editor on its own, the Monaco editor plus an optional language +client. Use it when you want only the editor, without tabs or the result viewer. + +```ts +import Yasqe from "@zazuko/yasqe"; +import "@zazuko/yasqe/build/yasqe.min.css"; +import { createQlueLsWorker, configureQlueLsBackend } from "./qlue-ls"; + +const yasqe = new Yasqe(document.getElementById("yasqe")!, { + value: "SELECT * WHERE { ?s ?p ?o } LIMIT 10", + requestConfig: { endpoint: "https://dbpedia.org/sparql" }, + languageServerWorker: createQlueLsWorker, + onLanguageClientReady: (languageClient) => + configureQlueLsBackend(languageClient, "https://dbpedia.org/sparql"), +}); + +yasqe.on("query", (yasqe, req) => console.log("running", req)); +yasqe.on("queryResponse", (yasqe, response, duration) => console.log(response, duration)); +``` + +Without `languageServerWorker`, Yasqe still works as a syntax-highlighted editor, you just don't get +completion, diagnostics or formatting. + +::: warning Events are instance-first +Yasqe events are emitted **instance-first**, handlers receive `(yasqeInstance, ...payload)`. For +example `queryResponse` is `(yasqe, response, duration)`. +::: + +## Common config + +| option | description | +| --- | --- | +| `value` | initial query string | +| `theme` | `"light"` / `"dark"` (defaults to the OS preference) | +| `editorOptions` | [Monaco options](./editor-options), deep-merged over the defaults | +| `requestConfig` | how queries are sent, see [Request configuration](./request-config) | +| `editorHeight` | initial editor height (e.g. `"300px"`) | +| `resizeable` | whether the editor can be resized | +| `showQueryButton` | show the run button | +| `persistenceId` | localStorage namespace | +| `languageServerWorker` | the LSP worker; omit for highlighting-only | +| `onLanguageClientReady` | `(languageClient) => void`, fired once the LSP client is connected | + +See the [Yasqe API](./api) for the full list of methods and events. diff --git a/docs/docs/yasr.md b/docs/docs/yasr.md new file mode 100644 index 00000000..bd99b3c6 --- /dev/null +++ b/docs/docs/yasr.md @@ -0,0 +1,58 @@ +# Yasr (results) + +`@zazuko/yasr` renders a SPARQL response, as a table, raw response, graph or map. Use it standalone +when you have results from anywhere and want Yasgui's viewer without the editor. + +Wire it to a [Yasqe](./yasqe) instance (or feed it a response from any source): + +```ts +import Yasqe from "@zazuko/yasqe"; +import Yasr from "@zazuko/yasr"; +import "@zazuko/yasqe/build/yasqe.min.css"; +import "@zazuko/yasr/build/yasr.min.css"; + +const yasqe = new Yasqe(document.getElementById("yasqe")!, { + requestConfig: { endpoint: "https://dbpedia.org/sparql" }, +}); +const yasr = new Yasr(document.getElementById("yasr")!, { + // resolve prefixed names in results using the query's PREFIX declarations + prefixes: () => yasqe.getPrefixesFromQuery(), +}); + +// queryResponse is emitted instance-first: (yasqe, response, duration) +yasqe.on("queryResponse", (yasqe, response, duration) => { + yasr.setResponse(response, duration); +}); +``` + +## Feeding a response directly + +You don't need Yasqe at all, `setResponse` accepts any SPARQL JSON / response object: + +```ts +const yasr = new Yasr(document.getElementById("yasr")!); +yasr.setResponse(sparqlResultsJson); +``` + +## Result-view plugins + +Yasr picks a sensible plugin based on the response (a table for `SELECT`, a boolean for `ASK`, +etc.). The live demo registers two extra plugins before creating the app: + +```ts +import GraphPlugin from "@matdata/yasgui-graph-plugin"; +import GeoPlugin from "yasgui-geo-tg"; + +Yasgui.Yasr.registerPlugin("Graph", GraphPlugin); +Yasgui.Yasr.registerPlugin("Geo", GeoPlugin); +``` + +Built-in and community plugins include: + +- **Table** · sortable, paginated table for `SELECT` results. +- **Response** · the raw response body (JSON, XML, Turtle, …). +- **Boolean** · the result of an `ASK` query. +- **Graph** · node-link visualization of `CONSTRUCT` / `DESCRIBE` results. +- **Geo** · map view for results with geographic literals. + +Users switch between the available plugins with the tabs above the result area. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..c04caf2b --- /dev/null +++ b/docs/index.md @@ -0,0 +1,26 @@ +--- +layout: page +title: Yasgui · SPARQL query editor +pageClass: yasgui-home +sidebar: false +aside: false +navbar: false +--- + + + +
+ +
diff --git a/docs/public/yasgui.png b/docs/public/yasgui.png new file mode 100644 index 0000000000000000000000000000000000000000..8fed2aee007f3ae29c2985c5c06c1dd7ac662120 GIT binary patch literal 32177 zcmXtf2RzmP_y2XR>`iudMn-04xU#~BY_eT@Z?eiv6f&+E*}JZNlN&|pl98F4s}hAv zkt_56x_$q@9*MM^2Ckncen@mKmqvpxIm7jA!*2z_!=qe7Jt0w1 zQPMtveqrve!Jg7VpTl+Km>w~lf8EcKWlE3*gk%IQ>EocVOPuy*(I~ZSMby{xWrS(xri|-^cFKOiT-L zgb9p%*bd<|cH4Lid(D|+LE{)Bzusuq8!R9wC}?pT)56(7-tmh$h#NBQF7-!o_l!Ac zr~lYXZtm?1E=5OhO~D6@HKdy&k|Y+QMskJp1{aOGO?2F6>?kEBPvzegY-1QD2>m7B z=lvv-sV1F{{EUjuAE4)KGFMgz> z?~osoZedT8C$+Et+X%eajsa)tGCOG}@mAw{P}I4S$Ay0yNM<|mJta}$Z}k;fWx*BU z&u%n8{)qv-ikwaQlsTdH_#1{iVI4o8a=9(d7zPt&l>b!FusD6?_5L!$NhQk67}-#G zMGppp$|A#(>;FR*bl*#!ANLVMyBr#@pCPQIe+mFw9=a^?nK7q-5{5Z6o1O>0ALO*wb@#g)HbyV1(% zXk6BtGt}Y3!BE0c=U!HZLBv&LL8f(g0uX_9n=I;Ff0<+4W7rce2}O}oLgOIKBqnaX z`%s)QjtVD$o6<%?8{^h>p^m9++ z5~YB#twkh4BTfkkA9tH;vAbERS^h!u_<@<$>Eob5y4Y~nzVFbu{^P8W+p^w3&P`@T zTS~|c^=8}buA~jV<9ldb{Fx703RsvsTm%JUq8P^mwR*zzRj%*{>br-=~^1U zwmwmlQl)@>#d_27v4v;uoEotTLi#!1#+6sTl?J`cl#BRMN1}#0b_j8lG62shB~64T zRK))JqCr;n75`L+5}5yqEOIaTtSU^Q<~AyF>-l?W|A9Yq{(WnOoq<@@UnTA%4_v>! z&3$lD?r!4w@yf?}uLOrbMQD1wX7@5p?NTse(H^abp8EEc>YXa?1`Zm<&#(Fb*l;Og zTf5V$Ry4e@)@QDT6Wbr*-ac^PcD&tF->tM<^yOW?*xt8%v)>Eu59K1a7F@2~-=*ky ziu!P6U9eHX5kAL$sIklaMDGjvS7n+*`QSd(`cYkHMuzY(MwK)OTCU?|X8|^?aiq%2XH?6nt9;sO=^=L# zaNbdsY^oVxiDp^d-?yttu-XyZO9k>D)br_-mu15r+g`ElNJ8QJv$%s^DJP#|;~h6H0o&t~N6{Y+KH~KC^;P+jA+&)-MM($o zv-|CDOqC^`{k5~*E-?%Ddsg$z{Fyp~FC3ekc&mJz9p>dR0Eerk(1XzbLl#*H9Zkh_ zj7c=h2IH(YlR^zw;}Dr}Q|c+HQ-`l3X6oH`9E_X5;v(?3(sNUCj|}koduy z?7&`33q>#2Wj|@XRIUyDAPuh$43_5#uZJYkSqyQY4{zLQ-PA&@J(!|CRTg@JQ1QMnOSlEqRCR4e>vhVh^?q&(BVq`xl`L9{DC0 z6}%anTiLf_QcYpEIPXt4q`k)^P@PoC?Ubg>wK~l7g?ZU``*W&}eUVl|p6aGEGUYCS zL_lTUYt!yj7Ba%A{qWnnJn7s0yM)2>@;~#=T9zqa$6Fdcjft#SC?H#tRwVNdhnZT9gV}5&xnv}d2tGqL0 zEHm)>W8j}L;{2C9!(5j-efGCM6`5%T ze7rdwirvHy`fuooC^{13GgpT-=d1g3wZ2tRs_{r@6Y;-lsq!^U6#ymR!UUj1}`W;yJG z2BxIo5Hrn@jEy*}&M-(oEdE4$KZz-cKt=6Lyy0+e`IMn|Q65eUbmIvO%%|w(BKG0! z&oo9py@6zfanC{(0+t?EXvBqL+L$nW1vgC=HeoJ~LkpOz~1+j+4^Utbn#H0?Z7)CpN|iCLFop9R5>6I~!j zaV&k^gbuac%{|JWs9vrXWm*k0*E~N7OQYo++WVkY>4C_{o-PT~02p(EX||m>M@mWW0faGb_Ru4Ow9}(M z?yobLd}!PpHvrhK{!X z{N_p$%XCZ3M|1tgMG0N7_IgE}G_P9ZO{71~lbz=YVSU+;5!-b88S*9p0axOW_E(2! zWNKpeED5~qt$JDPv=?QQ+#hOmw}=T9xdzE+{c)EN6wF*=@GiA}Hm>yPfzv>2tyP;_ zm(NVv-h`UW_VaWO(=UIMJ(V2c)-;Y>UiYnA?L&Ul7KMJx)gOsjgnj(@akAM)M!J8a zdT0Fa{da5ck4QlZ!(3Bs)iEhiSjEVH_{fqew+O<^BCwkosXjgzD`#E{BZvN+a&`X~fEUPd@3bEL)~f5uTvA|L=T+I&AF!&}$yz9^IOeoHi6I zhi%Ta&Pc^zrV~OM>14$~=r`;sLCPi1rCmAaFO zup(&Nf&@d5v?!5Sc^MyvHM)5nxHBQBClSxA>eQE+{CXCef+`H2ZD|fo6N^^`YWqLto3Nd3g+-Y<%{Gfe2S< z%2;e>hw0U%wvX#GukE5qe=H)s%8E|zAzE42$@O5ueDFE81*L5J=JuDdZ|<~Ws+X4R zbJXy?3dU!+?l05Uy$O^p%=%s1Ju4L~X6kY|#-1aM^%Y}47S8!KRg%H%O*aq5eBRo=^+ULFg{IMIWp5Oda`%<%jl$JqlOSVL5 zR^HNQA)p7_i|^mOsFiSsYb}zb!gfDjr1Hh#E7A6mjioM0jabVA{|_33<$SfB7x`09 z^+!Yc7Wn=H5&FdY^NVZ8cvR5FMXRUX-G!tK>W{SVQPi3T&hN}ReNlGz={-8wE`6PH zaI>+sbuEQu|4~&&hJsU1Al7@f$!b{U@xs=#Q9hr5sH03om&1Pkxs?L)Pty4Nfg^gg zEyne_-#0&jaK7M{wE!OmwpOqR5R0yUgDME@E~Nq8hEFZ8>>}SJK7UK%>D(7=>ZaJ( zSuYFDk9Yf1kJqPwTL@JFV}oZ?rz-((-f*`)4-yLmer^>d{0aoCHY-reh%e$%J5C{H zzt{DMLd{7PcnO|GRk&VBLPS6!B?wIbhWQ=pu{FdI@Kr(}98LK5U?Z!9EL+*H*mwN> z?zvMfF_H$iCS7R_-qu%;ubu*_AEcn?aovthb2o0mU|jX!RQlDJZH$yzd8YO1C~6;cDsm99!iMmNyLt5Cklpl4CyG{W*5C(6*4eEaH8$BBZaEP%=QVZPfia;B%*X*x%2wV?vCET&2n{R3^jMu+P z0K3=^0VhlW-1erk%vHhJG@1>_;|O2)@xk`j3y}2v9vM2{l*@P-}$>%^zYg)FH+xBt2XY;`YT@Rbv+H#nLZoGaZ zHn?1VbaXT+2g=H?FAu+DO)}NE45d&>Ioi&(HMvOz6Z)PhI6{Wg8Dt2|eb(FO=b-#5 z3;8-i)MaJ-fgP7;oo;xvB3ggVX}iG2$@TM#m^CNSBKUa8o5Zud%6|(}iry?npZwj^ z9c8re>lH;u(ar0wIRyaFWc4Zh#nI2^qft-;=ry*t&j-ADV9>aF#7Iq7gn9_olqTFC zPNS0qkG5<*UjfIc;y#)~$E%$r=RQ(n{+;wNFqH=PDzsh2e?bA7`vQQ- z1>UNk1K=m00Lr6R)bkf`&7tkFAV%#1WLOl3a0D8Xi9TFS2U`Jr1+mJTsWYMBecCi| zcC_hJaCAmpgqkOP_&9F1FKj&&8!=x1Zn~%%ewl_{>H@DWa1QwDQ0fDoYgfY`G!;%u zd{xGuDc(s;rE%+Ry|8=u^`U(1{)UQlfgOLsp#UgU{WmTG_Zl11cJ*U?jFPR zWkug9hC=_|3W_n!Z;dZsR)E+Do^z91Jvx|c#zzlUqcZ>mT_WXqORPHPe#pv59lauO zNk>(Zo=E0Ux4-yp^d|7h;Ga2%nC-549pYYiN>=JJm zB7a20VyB&KXzoB;EWRBp8G*+VmctkT1`o~itZ@N#zxvU}Q=fvkz+aUgrTn@^Fl|uN ztmdC{4ngCfa?u9R@=L#mFZ@Hrcu?Z8!;NgN+1;!xf>@N6*gq_C2eVZPTe&FK1d!Bn z;`wQ(&y45~3#%PqM_#+QS3U*Dji|x?4Kd?S!(DG7m%1DM+Xv57!Q-#yi<#wY<~0_} zxymRo1h2Mn-gA> zDOw`iex}|G;7dG6gH|QLEP9qhjY~v=oPKi@ehTh8_OGVfe^#OvCC%X&OafqpiQlg6 z)X$$kdx0nS?N1x$tA6~=6IEICIu$G#*;8k|G3S7gb=9$7YZ?O;FuKMte_DcY&Dg&< zJ${Mp%8xl%a>Rf14LK`z1%Ro+q4J84k)00#t+G0tUT@VVLCd4^h@6I9;biDg>d)u3 ztD`zn?=VVaPUK4?7k^R$|I`Ll0APKL^E{Ip7P(c!y}1(m#7IB&9~alqWM&w=z_d^r z_vt-;Y+9}wGGn5=78~hlt$ud+6@c(d<9Y3U$B_UES9424hQD&mF31< zD>p!;UQe@??r-<+44!L(*yp~`;p$_8u4LF+#mm%~->h+jN2&(5qr|$GCG*7K*18I0 z5!g;&eCLd*VdUJEL`|oe>h;<$&I!IjKvEPq+=6CG!$GpLMC5ko<0gA**3*e3! zP*yT%9$vC>it$|XtY>Nsyb{(BBj+xV@I+Ssrwi`s4T|RQ9g_5h#XNOc=e#O@{U1NBDX34PM8+%U9%^4 zOft?)(H$LJq88(A0{!uin~3+HogSxuE-JI*Pdt(Hs*G3K`%ZrC?HWJ+Gw$N?jqO!? zF8NK9@Ea~Ow$jBiVaZ>y8ngFGU|jDp-6R#{M!fsCC;>Npjw_SR^#!FlM4H;N6s$M{ z;uhnY{0~sFd~ZQcP)B@3aY0h;OiUgFn#es-)oVtZk01K-Yi zOrNRXm{q(u0W)ql*08x;e*@yxcWhjBFN5_xMin}ol+Dt6(gho}6DGWa3E!h$dHsU2 zlfIzJFkh4>@#Ggz!r|)2k5U28jXgQF64Vn9hxv)wUPqtvCw{c(H+`lOad82(2$~|3 z!+3%cz4Nad0W)%i2V_$GIum|%RhcEAuHCpJ&ER|SA_7K3qkXzxueJ7KZ{lgdr|zn! z!rxOloN&{|GUG!xpBtiY;5BO>M;X{zoBlgoI-u^b+K>WVU6om-<`!z_C0p0hA3)E9fWP(tI7-WT{&otV z;MchG*nYUnz%_B+4*lc$d&{gMIK`A>YjUb#yy=x`nG1xOC~@J;ZbIR1sYXIZkZxZ2(jsjf4a zGf!ky@4NXBgs}qTw>FL`>ip`PCBbL%NP8SRE+a|KNl10Szea;zFvbn8h%Way%sc^P zUxuB1Cb;CEo-VB)J^A%&qch zW8sMs)#JYu(GMEl#SUTb?VwpQvk<4{-h&?AJ5bbS>BUA%cqCh}{RYpQhQ`J!P)YJc z~)pciXjhVUR*i*`U~B>1d%Cti;Z7Jf{8I;tps^wJESjU1*$#Qf_l$syOpb~=Wzwva@4tOIAg9#@nUw_{jz;4eLZv~IP*gBVo*}E?z(MGCYld_770l6HCZGd z%R;TLi(Sko@7P=wQz>oF_S zsi3heY8~cuMFu&=i*Q(vk~MXCE!leC@rq75qfg2pgfg98AhY!P{`C(IhU}mto5uq0 zCfQZ^Z%Ka1_4ao>DY#6$08vg}#;lqyj2Ch-i!wWzyrEHhIgd0|tR{{0wvLzhE0?Gp zZq*cnM$DE$^_~Gz(^qe7PqpJADzV^(TtErOI~Dm&GIiaTC9Mw0`TtvRn_@Ysq}7N3 znIijZS#o8Py2zl8jZZ(3?=WOTN+ddI!?3a+qzVuswMf!(+cokmN@CCm{MmdbO*7E? zWqAjtX!=7R;P|^fwMTU+*2g3>TI;`_o^m1t>)N>Nl{`z+FY&61+ ze~)06STs(X;mx5MWd58@i%{ZTtcplAbki@t+C+cA##;j;d@R*K`G2zjwU=9jwP^iW zf%4r%caNnW)DiV02cM%<5em;?egHj~&eh0MhR>yJNKNyC)+PnERH(rTq?ZSt#O!xYRBOBl>az6J;n{`3BR3uJ83()4L$Ps zOt0%RMpKL{6!g~eDBo*#G`5{!QYE*R(SviInfn?k9#=~62?AOG@FaK#PKfoFb%D2MHP#}>|# z>h~S_*F>U9Y1cOirW}q*49HWspC$F!)^FMn=oFu2<$_82a;j)y1k{>2KyR zI$MjCN5Y;0Q;5PI2*GMh0?8$vn}pfYsQ%<^7kL+TF603!23{5Zjz`zn;ZKDO)c?LoQ!gEn|G}Q4Mo+IV5KirQI>pT8? z)GH)EIVxjs-UMjld=lp$bGsE<0bmBYZs7j0P zU19n_MhJY4zStnLlJzVLRaqEmF5gUU3Z;NZ*51~lI48*oCNStyHp1a^G#9y4^)cTW z>9`ZbTkpcVsi&OY&B4z3icW&$U2d!f#I6R6xSf~xwrId-4gRegsSq5t!Sg;?V=`_k z-^_=IAW+HWGcBb_c4(g}!hn^e{PDG^TeSrWd{umhsVFT*)eMD-2@6&#q2``3JVg$} zr2jVA1L5;=_Upzj8Hm#vj6}fqTXl%VNVJBfHIjUnThZZfJ)3myW5nWpv~aZy#_sp! z;QyB59$~gvT`v8-)`?;bB00AH!eV3R4(DbZU;24b)fjX34Zog3FR(TPg4j`{VBGlJ z9$J8;q*aSqW6dTLv}zwFCvxOG&P^${6T=xwv@iDSfr$Sfh#@aq=v?lwEog0PSHP7R zRb<9XlEW#|5OxnIwIy;9U4p95y#^-j+zwa^l&7nk8dsSOoeSYc111Wsu{Z3=$6H1) z6eRWaR`R6wP=?Y)`l@@D%<>K-y$lOrW}tXLJN$9sjR%&1B^CVfevEovAC1SKnT$o8 zL(7Uo$ECdK)U8!Q^}filE=Y!F6laqpRD4&~&Z7sHi%UGvd^@C>{{z;;G-!nMcKt9H zG~I%b3rMcV_@7Q{|DM1|Q{>Q<3&Hb~2bNFYPihM%fYdeJ6}bT+k6%7LRiG(ZL|Bei zw5ElVo!O~a+Ht+Y96{F8CYX?mFtbiy@4!@Ge0gnMI+T}|dLq0^$j7(HaK9D&;LGgj zN0S1--(u)5sU9@ueIx-BDmFTlx!*vDGPGgmYLlGi@%M%6L+(vC6$$B=*sXx8Yj!7J zm0NyxnR$sn3*KM^%}B6i>Vd>&zG(GUx768;(#iD_Pfl;R7B#OR1IAzIe<{=LK%bY? zig(}eKjGU|49yAiSh0HZx0pyFRf)^{=V66i*L&ExJ>bnGW{`hAPlr!P0IL%<^n)6E zr{?$mt<|{?_pa|L{9JQnkUx&tV6+1hGb9g5Hpz0Bdq$I4K$PGAC-_F1pYJp|byyuA zslCYU=DUtQ@a7cyTt)aCYcK!8RpN2o2!A|x7idz0s7bb?&O;dC5!=C{x}HLi(f#?H zpA}{Yq@b^`4B6rXMyDyjbuKF(&S+Q!Voi9h#%#Rdrf7t#yaQ!)+Aagdx7i??o)5ym z#c*#Y1>6o4r(9+%Dd~-sh{DW2M*o?YLz;Yf*?@=;_3fT}aJ*#Y!FJtU5F)R}xxoS4 z63)!KxEHc)9Q~kgr165@Ctk`tzpq#4IG5VUB0`I-VhbF!d*ow_1zn1CBJEo$r|61 ztxo$@pIMh@RI1Ts37?~lhAapYPY!xAhTQ}k*MP0VsoBGVxBkKBQ&vlg1+1=Y0{bMP2n~Cl&(2Tbqfp56tlm%_fA zyaQ)1^G11i2lIXy;k|CU!CgeT5hp`{$46Qhuc!~xg(UA7LHWsUIl}FAA9K) zww)p{OU%Nqzfi4A$r$4=kJEkZOqJt|PR5YDF*F7;Cb|6Ffa)@le=7J^}`vKncp;@_H@jhh@kYv@1ke|Z50L-(~m6u7T=)jfkV zq9i-*Xy8lVFypY9)qEwzRIc~POTni%mS5SR-kdH<9cwhCU6z1mX0wiALp{X zaIM`$e8q?y=e9+|Gxa^_ybJ=tOYg`|Ou;0Sea8;qh6HfKJ4jR_Bw9E;hbQ{3H!}1~ zTr=SDCdGfK(!I3;({CuKvW49}6AtKLB*9CUGWQPHK_0IdOZlj%vGN1&0)bZ1*B26l z30vqtS#Y=(#hKv&y{C+gR@9(S_=R^`KsB7J_)3z|)QMd4c9P@%xDq;p53GJy-g8uu zvJPYVK}sFl6Fsl}+l+E%Qcba>Al~r>PkYx2|4LBi|vWy+vK#a|cdvVu~^#norOBqdjZc+U-LZ^iCCT*E(ANBDvAB7S<09?aK zLW&EPU%)V>usb#P5QB*QVk7)(5|(;&t7Zg~1IYpND_8H7Oy?J8TM!+y;}$TvW0~M2 z)CW*LN@RgRM;l6bhYm!iS0Tx+<~A%EMSRaiL{O&H*@ayL-KZxdi9+1R%U|}%r&?Mk z_vUM$MD1RVs=Tm8{eY@m#IurA4HefkUWW+KRVZP89VR>f2TCZZTT9lC_n$+bYB2SY8@n_m4dgK1LslcIzBjFj&`@@;#jsM-7UFUue7ogbl6U{rJ%peGMjgslY`bm94WtFOFVqNaOwJ&7mOr`<&iF4lX-!nm)e$5#k7$fdJa*d z|LET(Xy5wI4M05mw360Bm|38;J@Suw@Pm>ohWXb2C;}x4Y03kd^4beQB=OjISjHFl z9M2)gR6=iy>I623mUjqK0%3UcaL}eUntP+-;EF#Jh>G9JP=h=8V^WQ`_^rC0Bxi^N zg@3m-1TXSwe9i+mF)2VzVGd%j7wn$NtiRz;AfAOQ#-9?$T1_Fg&*CYRVZFjfA9Gw6 zOgkO`W8n2z^u#AbZZN_lH7B)z4aAccqaK1p(3B7CjoK3+kwH-~*TVfow08PyhSL5@ z*+J)_ryNoM{R>Q=y727^f%41ag1j1iuck*D8Ad>r3=6#UXKk8s4R!G%7y1nSA(=PX z%I(D+jj2ZyVl+WQ%LAGtn?-LhZDbP{%Aa0Hj3mtHQph6XGj3S4FkaQifT7<0<5LhF zB7mz~c!Jr$g$(c5({6Mu4B5~>xqIy{GU;J*RPs4=hf&cspM}OTjR(thOkul%9i(DI zW4c9RW81A7XsNEf!M1~V4FfaSCuXkSr;T9QHpWlLWl4+4ZblLoNxl}m#+V9mmF7}< z^~QCy02X2@0hLpARalrM?hEY6^TKSsnwxmd%!a-(Mfqbb7ncnly;#Z%-OyXbz2{^F z^ZrPN15W3$n{w+OAg7K`8kiIIy{IYx#K@Z$lAw4Am5Z`&-Z4=qTGR z*d|`+h(aG3UZmOjil5P?WcoLd{!T;w6JK<(cYY$@1BwJD-#clD+hjZ()sc5aNkI#v z2vq7)*$2UfP1HhEXJjwF;UA_<2>tbfJ3wR@cl@6Tn~+3?6Bu+@m62aFt$QTi`Gd|) zA{gMz^_-$Es@ZP8u7z`z=LSKQj@RJ#d(hUZ5Y!rwi;4BeOAM3En+zH#`p=0P7FJ-}=?o{b! zD1d{h8Ny76_80U@(Iiz}PmHQ8HkEkSJajF0IGGNxJkvTj6~HjTehnGnErIN#Ogy|{{ugvE6$G4Rx~4u%j0Q!hJuo9aV0 z(;6zDlrEpngNL#j(-jGifyfmI^)ZJZ0s>W0kVFVHZ>QXyD zoSsWF;~$oNvW`lW%mqN<+4}Yh%fTZVgBd1k9=yK;>l~DP`;y}wJ&_vXSpYeK*%6OF z)nQcv9Qji;pTJMC4Xx`8tx3k88&>mR#fufNu-uY2;qPpck<0P6H|O(t#1$AApvNf_ z7I$CaKPy{?$fGz_R4q?a9#BLE6WRfDBVI6~W3WYR)j)nJIzT>=%9Jb$!MlAP%ojY6 zM~b4izcnxR)2WDC{u*L=RWRotZ=$eZ33$Fd>t11NcsJdyR7dDt&!3qGskd?XN-l0?;DiKx;oo362AlrUKHzvbN^Za_M%Y8C7o&Fy7ohFI&S}tQm|rhBZ#Dp zbv17>Z4?P$qMfRO;vj+$BXGae&J3q1v5D8?1sUpG5?D)tEG1A&T~dvt~bG+W{=EarEu2@dv@)9NA@x zyX>C%^7_gu{wS;59Udtf9J9eA5>2`w^3kXn5g}62zakAgCA+O4Jiu4I{@cIWHuPF`-k#{HuAgc%*Xm~k_K+mfqEDaty zg=x7m^?HY&GUvIJ8R_YAyvN2R3m$njXm9GtmNpkL8ZwwKW2{Aw(!`q2DY!Syao3}O z$gqERn^Uv2R8k~xsTz~AL!%f?j`_G%vZT@xyw^Kd<5B^is!-9e$i-6Ki%=xxBaNgp z*V1lh##Ych=P(r(2T%8a&p{74I*b9SSWM|?9KNvqZPNAIF~(oH+A8~d6_VA5c`D#F z0XWZt_q}u~>R{jTWLp=19<&(b{eJE*!|4i_WaORCuZPoA3$)aeR2DBTrnk+{PV|T6(?5XC%d2Jsqg#v;PZy!K_q^}^b%1v}Z^+KR z#VAo!2!rULUY)%xP}-`S)T}dW_7+pdrrIaf_`Vz5h6$V3Y&o29!|(-{s)3>` zG9nY+)5@f3oXf6A&X7j!0*2A+-+pMFcRO9e22q0(@&P*1hDQnnYj5y~_wgSNpb}YC z4QeGY*~#t*cr|+D#A0?9*k91t#6p)uG|!aUD;Cx4P(8u`YT|nM&jDe}hu_x=U+j zU~PgwJf22Ku7(NIA~Lnrt*+=m3RQL(&m8fzv_~pJRc?+KT5>w1KeFx!A!U@1ZnvO8AyT6cy6|7e}yhj;nBxnLvxJ( zh2T3IJe*40yU4sK;YfvQ-|pVx}F9Glh3#W z?ZAskpx)MJ!D{BGWs^^k;xF{^U(Uj8UBa0@f0nl=-GM}z5s|{2D3^bKEK#qesB=+} z$y+AtV#G^OD>@1yj$GaJ!Q8w32&-dt7l9|k}kE%Jq^33=UW~uIAm4M;|w3t8PrR67gb*I;P#TsJ z)BVlPs+T;YsN>1sEQ%F~R$YaK@UBPKn0LLRv^0P*yqLVAC!U3pfgCYn?H@&-LN9v4 z=S%GBCtas4Rp4IyxMx?T!){QNv>w2kBS7lK(v^V%(xAdS)!5Ayeh!HR+2<7;Vgm)$ zk?+Ac!tNF>68DICrE3u7d@=3`b1`6ik!X5>Fku|Ii&;6Fgavr>l9@){kMhMs`X19I z*b^<;&W?WDUzirEVBTQ=+?~(@6y@!BaSx~P6myZ7bnlblPN37@H+on_*i-j5KnwOM zbqh5u^vn+qnr!(LQSGETq;29~*5*?KyQ$l1K@JE(fA;N$ap_AsGE2(G-K9 z%oOMuc!lZccqAh@rdTqyy%31S7+AfWWnLpp{{RFB#@rWxJ$-f+X_BNowL0e$N2c-# ztDj1r*XeoFH<3)`@_vA|{IO!N?ps=_V&ib0=?x3+ zn-{@c+IxwZ!E6Mt?!L#CQ&r`%0`QND&wgxI^`Qxy_(VDYBbbRei#4Yq2G9u-%A5d! zST;V~Ewu}vIPh;}cmRlvM9D8CziIH% zKY$SwiGh?k(<>aghu%^t!atPJm`Q<@h|HHsXQP-%8s0h!*1&QtBj8XUpo7*(5oVdz z;57r+pGqJQh8Maa7I%^)b=Dv4#87Xn{rJ! zhrazB;R#`0a?Yr`<@HV&M_(=0(KN+}=7vWCyhFt+T7PB=o>K`u2F_rZ#?{6ni{x^- zY>xa>iViW#n;LFagPfCelkqW>i9F`Q8Q@6exouTS4DY+ZIiTl6Znch4WBZx_I7Fk` zG)ew5=t=+X*X?ot<43MWYj~lgP0$S2NpyLR+9BP7zF=1|yF8velWc}*ab87x+EK`F z(m65`8CufgzT;y65-qPPI&yl!IrfIFS{++M+4hC{3yRnznG~P*9`B(K&{gPB0bTNs z?7ggqlbD16_|bAl%1}f8tE#ZqEqp33c1?0Kgbhl+%VRJTP-%y;pmECnZvlU`o{m;* zZR5?Pjto~{rF>1^YkbjXR2yBi(eMN`Ec3`yt=86)3agN>TiQ`J_AKIghDXv$PcbbF z#zEbeRO>s;?uUE{WqH)4Ti7m#WY0?Yg5SfQ+PlP8f6CDoE2iw^+-*P%xWGT9(zE0t zPBXkubV1QoL{#ZR>)xwxF*#&`0!s@oq?}3oP{+&R7H@_zO5`{4T8CTDH}LB++sYV! ztLoXd&mNLtOL%e+N1FQA><{<&Nr*rM6BtQw2XM4e6rAWqc{Rq~+%;tfMl4=c;V?!fy5zwf*Gltp| z*rcz|aV?afpOmfxma&3&hsw!>6Gk&B>iJ0T6nZyp*=Kv-97-JDdNS$y#SKo{@B;3~ z+)-b0B{#Tph^-hAL7v8Bq80Rj9NmvUi@DP{?F#oJeVQbNEjcPh6@7eNVi$(Xk-Y%hUi6b-(K*(bOZ^dwalJHQSWh|9L%U3QBLK^ zr#kBPGiiJIG}~fj3bO?DVrf;i-g|wJp1guOZkW_IGLLpyOOWte8>HW`vo&YWP5Ky2 z4p0Y+qQmA$aGxRC9i%tSLnilrU7Dl_u|vVQApYuV9}g1= z4xgV{(k69n43oe<|7Q7=qtuD&{DEG#bV1vI}$;)ljGQ)mo8OZ?Y5(AOR zD{oj?mC{ydc#4A?RbApiGk}#)jj*Ge%Alnw_mtZq(TXJe4RPx1Sc>5?&qpktn#gg{ zgufp;%o|!kIwT7bDfdxy4*o0xV58r<_Nzv=tNF*-!YlsTo#uT()Dg_9c$#FEOn8>3 zo((Q4#Q7uEtM3@%5|8t}B!67OEK9O*g=XCPRV|ne_`}vw1Wa{nn%ZUv=u124wfDYXWRfMF>iES$!L4fpo3-$<+0qN=o)9xlj`J(wVePavCeeP z+}iI^yLd!CHF9e79HIgnFElu4Ts6u-lpFV&;QFxY3^=^}yU*vtFH;S_Wrh-R@h>Sm zD5tJ3yeOgDDk`|~M7HFslEhBf=U=H|8hXNCK?op$&vEoB1wR02bP>%7fDr@9aXQ$< zhm6206E~mQx}LAN!g{#Aa^;n##Q;oy2HUsL zm<7`mV7{?47w=j~%ue5GdEMgnpz(^Lo>6vd_jg3oe+7lija9x0k6uDW{6Rw6pVK>d{?-M*az{d01Oa)l6i|z1u>3Q(kcqDE zQC%jzBOWPTaZ0Wt7|A?Rob&8%Gu3l_({(-5r!;HPx6TwFWz}tB+|ad)Utd14j7V#$ zfVNOM-gr6U1vxRWI%ia)I>bC#Z6BFmx78M2%-tvbV*H`Els2$ zVye1Y2?Lf^W*2zEl40NKdnNP7zQwyxTE$i*lPWal>%!^lN6Uz7beg#XCJ7992J z)uB6wS8j6NfI&|9qGcnQ1B81ij@q8F_|!IqS^goQm%xxI%7i+CTUHo#epQuek$Jwy zDh<1AEiP+m^*(ejNv;4+Zj;~WOT&m7^n8@j!geh^ZEZhv^Xyy46RTN7Q!HQLZ{(PcWxlluA$RFn=!=fbYjWaNqjG=1ZylT(A`-GgaWhFsXoPUXO6<|#@$LkM z!vA|tlj*Hl_h2=rc}N(y<8N&j<^NaHb;ncve}C6Cvb%O>Zc6sZ>>93x z+4CxLt;~?wvaT)TmUV3zsf4S@m6a{rtkmyy`+R?Y*28_hU*o*ad7kGvuX9jGgqbA2-H-ySaF;|Zq8A4pe?N$!0+-X^xaa~hK>Dm}_TJTR5C4zc`gj0)l|q24~h zZzvA%tu>N?OdMXj6px^>rA_i9JBBgrHQ-yva|h2~M!tmx&^X3|i7>Y%Yajf)}xU3Eq;+G%~U$1oBB-1K4B1zSOZfw}NnD+s0HF`zS?!H*Ws@nx~%8-J! zY%b0h@`r8}hTt2{hp3v3F4b@;_UySM?40e{nz2sVc`f5LOCDxq6MK162m|HWoD=uA@CJv() z6_aM;{o&>)_p;?=`vh z75_BAYmK}1TJ32xg%Po;88nxHjGZ&5Kv7y;jb;I`O81qgeI8*~k*y}iMTa3fitC>j z)2J&@&ZJ;2orE8LFY@j6DaDJ3D7IR{m5%dsf!nfLfmiD>ej$T&6(m;?zt`OVdA3`n z@2?_Nz!zQ%SE_r_MC$rx)8BD;1iurT8orF7fCSE;jRzr`;<+1HC zkKwU{xn5~S|MHP#6XI43KPSX;Ej|w^C=GS*HGTmO8#Vdf!un=k{oCuP0soDciZ{i| z$#IKkzW)*tekxogAHC|c!sP-sr}NU>N=@2XDcDl9pyykkRXfe56MF`$;t7h*PZpq! zw`%Px&%ukT&sX2?+8e^qZ(>%(zx;@6n*0*}{=7aigKnL!-l4p9tzCAIuQ3xuRh)pU&fx%Rib6H2qL^4Y;}aHTX| za%F@W==KgXN1fB{y^EHHS?jojZq&|F_MLLnL(If-zzHTC!WDxl4yw})R6Lr74 zOeY=~B2|g@-g8VGy>ID&+^!!YAFYw;<{qC0JU&m_}&}TkeL2dhXR5`>c@j82pop%kP^SoQ)4N`BrFqyNu!9$T>j* zR6M>LsQ!YRnGXfg*Zi0(J&T5q2rR*f+jp`j7Yp$L(4+&0#=L_Yt!L(&!I8w_%6Sp7 z&Bs$zFgmsaUErsVrMJJad-J2=cx3x&xE2P=EvS{!$i9gnqhR|%4d?~4LHkN2)!~PC zEQW{Z2Z=Wb@z14AkioOza(p|1Z6$w>3Go5#cC9tN)kFJh;#)>iGt)psLJr@~il@-U zPUM`J6PM^6zm7+Ef$c6=GhE*S@dqhr5RDmYvNV9#a&HDa;{~eV&tpW&-n~_1g5v&- zW#P*5i~GFUW}fZtht2D|pBLSl?DE}=2FAO9K=8%(eX2>riLLN8$8Pc1&PGm^lazV} zO*~jCSB;K>Cvz)~H?->&Wa7VZl#CC4@!nWfG&vy*y_N(nNm%5Fiv;XlAHV}Em zMdD-6cfvr$Lq}L>nMvM1s2jJAY%%h{AqfH(gft(;#?V@$2aY&i)+8JiS+}N z2%<=eiyA6tpklG>ePJSm2&qZCr3#yXHE!FUxgWVvcDtOH27N6ueL4LFPKYs=50T^_ z$k50VP=S}jC=}xoI)-Ri*2GX#GP;u|k*?~SX{4aKpX}Af6P1>?Ps69_{zMAKnx46W zUp`%vrqIK!=iv`#aXrq|Gcrg0Tf^|3i}!IZc~+CkIe+Xeic_ivD%y_a3L;`}0`ZA# zI_p^DR|zyy*5k9??3uXLc*HNq&kk9L3xISj0-i0<`AuW~**wEMRgA54!`l3jKDG@_^qWvwXCaF{BMdUcu#U@EbA&igX@{liU%_9c{zHMGRc z!~mhaXrmV(CbMhNG6bZn-lX|%t;+ZJCf-iH`Zboctz44+XBVNvzz8IY5V*>t(-Yk^ z6sS_v&88YAO|=8xrR>1{Tls!)Hn`_~A$&`DWUy)VTNBV)ib*ZVxvw>8KT1|#ZA#W` zZ+f1obid>BZ>1I`ML{rYVg4Aw_Z}Vl8Tc`@#K&uJAGT`8Rn0GfFe^utd$;i`Eu^a@ z+2Yw9pyRWltCjeO*gTwK(H`^b&L@=mgpRlh{%|2gB+Zup8$TU#7r+uRIL`-ycWxB9 zu#k^QHzX*wa=XMBB7>pmdx>*rnkR^O{CP=#_>eeUhwgIE`_HVmA_ni*g+}{)pmCpGK`6798kU9VCH$dl28t%tc zlu>W-o&`?0(gI#9v;KMV&jpW-gMrd_go`Dz0H>XS?F($G?+0oAGI4s{eAE)J&*8*c z?MyXWjNl&W{9BS8GYV6G8h(U{9uFrjr;L=wK~a=tl7Vs#?ze#Yd4Cb&2=M|cPXLk9 zP&?rn0R2kE&ApMb=9EqP0-Hs<=8hq;7;OJW(N;f#*s0Yz}70mjH%Fg;j>DGfQ#>LnEFnJc^fu{Q8 z3HRZcsVWI#+~rp^8QFjwPdODi-2=caN2Dx@QuA-l6?OEMw(w7)mVs*zn<{&hZ_-v* z;9G?eDK|M&RItU^^+?^;n8VG#md^s$&cK^y{eQ>#)V|TuhWMjK+o&ktEd0q?f=#Tx z<>bqrqkBmrdSS^KAGe9B_=DT4$ZP|oF(h2%<>;> z+ksO<*rSHy@}d-r&P&;X)lYpSh*s_`^J(P02x4hGcTNn57aVR2;ZXM2H^FM5D38xn zOnXfD*>KD^B33e9m*4DaSHH$Tj?L3Q zhwB4u`JQYC2WoUW$Zj}(RdHF1XaNQ&XZHk2i`~?X)3U(SKg5+2|6ab9z9z+0aFaHq zWLXOcD+5e}m4C+L&8#^k)Z|W38k=g12nhis#;D6aMfo)KzTd8WQ1%e%8fG_BFJJ95 z=ezX-?Ien7QSpbj#_IFgnT)6j+AVvcv_|!=ev9p^!WK|{n#M>YbILsR*zp6*mB#&- zuXzHQ@rPz4`v1^ z$fF2F+?vyhOYOlc`AvGK6VDhwu<1Sm=8cxiMGO><4lBvGR_@_Q6vSmB)yfIRhaUGh018xXo$+AjKT2?`DsyOk0Z~{x~ zNn3t-^1-g36RQF-Q7 z*D^SO{`K6qxdU9XE9gpm$zy1h(zPQ?KJ~9du zAZrxB%w1-`Y_MfZ?z=Ga$J%ftQI`%c@$Nb~?syH>nxMOt_Wt*P+gm=_0CNfqrNz42 z^HyS$Zxa0&uVIP|&&k6|)1!{Kyew~-_K+EJ7S<2t;9;0q(S8fh#6Ghclu4@Fih)n4 zmVpDENW0cpe@G2|rPlY$p){Q1t zy|4}9(XQngj_(I^3q99fO{~UQPi@*9B~CcQO7xiV}{R;%E$uIJp@xc`I5;fNnA^@0YYP z>Mn%cdpn|q!LtJIed(%)=_6@;5DMN5`P@o!bCr7%kCACVKD>zT5>V3?va>m+=jbG|z`Qn+Q zY2q6NrvYDKw(w~G5xdU(QOv7P<`fl_))mw^pg~U- zSB6#+Y_juDAYy|?(|}L?2P<|sR~iFmzB0@HGioc*CR_DLs-oQaPgn;9BW!0l@@OG! zg59#cWVsg~oP%F~lrw){eDl^^oj^UWf-%xlsm&c&(7ki>QVleZldL?2lLU2~yT{z~ zOifa+KfHwK5LoDxyx$qxiO96T@MPhQQKN?D^^Xw05Uk^M_QIDK`C*|IlYG z0%E^^4vx~&xl9gD&5h3~bz9h|4Nl!j%67<9)Ue#tAivFJyHfu^ zI=A+hx5}gfhXv+|v8v1W4Wcn|v}SuL%7F--i0&`qLcT(*3zYhy7r#$fIZf+7G>U-! zR?wY<3(%|rJ_`3qp`h%+fWSh_&#kxRWGP8h9+VX#_c+f`Vuv`iB*2YGdGU@R=NpYD z)ji9n^&eWurw4cJ9OmIQqr*HilT=MswaSC2eLrj=Z4Zlvb87_7>V!05u%q-g9)D#f zxGC`qg489Hd=JHMC^pZK@ieg; z?xVYk+|oxjKm`Q)GW8@eSo~vlVM4z66xKEpJTSM{jp<^x1FASO94J0Kxxdo37EC87 z#NGmfO29j?g>vjRx7>OZt3Tm?PXG>g?B!u}BiwsdQBb;Ul;TprsuyBhh$KRxlR;#^Fj+Q z2@S?Bfpof_uG;MtNi2ThUA^+q*AGj{RXV<%p?zG{-`0ET9+bT>r-(QmUzfwb(BRx_ z!F74{C0+(z{iClLGv7BFyyzas!oacyXvUp3;#=?XNqUKiB7$7mwiCC5Q7J7o=2`g1 z?M8(KyogL=_2UFnWdEsHyO`xoGA8PhNwbDog{q zF3xPU7b}ZxgcpB05keb)(-&coVMtk9o;nsP4a|!c(`pgkG?-<@p!WRc2!D;V)6oYA zI94f>tj0${I?b(e(gfCtn!`w98Fe=<5=wWpTqHRAjyWw-rA(muA-0hl{-S zPF*7Y!XI2NrvnC?jp(I(tMUWAWuJX=fDcK*ae--Dtl*YayIZy%%CVcrzMau1c+<$K z4wqgLD?|g+IHoQQ0RB+NRMMXBoaW;sp|%PW4Ae0!bl#)yCm(IE^eS4ygsKG`1Q*(i z&gKM_fH-I^{W{%bPs+$rNfqrZ(lw_%P=+2h0ok)()zr*kSuMlQx|{*(D753+sq;F)t6fIk>s7WKL^!EmehVt+OE$RLFlP`d4y@xUlG`Bs@H)Rxo|JmuW z$zG>fP5inaCuXH%s$~%D!nL5r%3oVfpSe5kMxXgq9u-zlE~W)G@dF>fG~6*UPyZD1 zl_*BdImb~?UB2n26yB5ZM+i*!FB9omMo~Z%tBv7^;5~_u#YYJ_Od76lmf5x)i<;TC z#Y`N61p~kIGt0fcfIrVg;vt--o(yMYaoMe;OThb)r`HBQJw-V8apb1|MJ za%2D7(CsRl>~HjT0%E>>kb&od3)^F@PHfxZB=$@uLzMfhKcw=XVxtu~o-%}%_ymL3 zDtoz0&!JO}UxqMV67oD>)dKB3M9Uf_RTg@zj{jxp0Ld@)3R@;*S-iluLMVwz(6TnJZ;qbHx*;v%3;?)0I)6Z8bJ#ivJnd(Ol_ z7IVW910p)I)0mxKdd@Xu18AsY`v(JG+D6lLKQYUV$lY_z_8mp2YTM8Uf&0YpJGfrVs( zLJLKE^T8%g1YB`>Itck?F&cxGP^tp>{erAhUrDF^VsS=MNYDG{2F!m+r5D8<|3+M$ zRG0>vxqSHkn~ALo}hhz|Jd#;ItN*uF*#0uv->gP7Gvv7L9vL&xZX84@yk zITmVbfqhJ7WVmXoZ`1B3@Pev3nGmFEEs^%A5j8-#u)yJWpaGozWtvfrCChyv@`3DA z+t46zWf#2zSDxNZQJAVMauha$+1YEMp_{<`}; z*rR`r10Q@9Y`oZ9&hG1a<)gDs%~EB-oxi`NH#)a|;99V?Zg*?GKB`DK;|vS z!y6~%_R_}oDG?nbN2wONh1d?ry_g%n8$7iod^>+fKzr^a-Pq9>x*{pC(9SG3n}XMV zggD~4Lr08=ylbv9m)y+`OX-fZz1NnQ`A+9uP@7Qb7X{I+jc*{f?&-lth29@l)upTXR`vZ^EuIGhAK*0*H({UGQk4ar`` zpTJq1np?>>N~?M<^)&|jP7D<$s#d%rVq*9n(poU|w_g@!&9}$$0qDW}yi&X+h#PT@ z(PXgk%g-@xkM~*D&^w!KS^ozvvK^>m`QZBKQ(`4imQC&nEo0g8P@?;&CcIXulDNtP zAXnw}(Q|-eGyvM{FOPyl-}sor^Mg?QH-&=Z4bIxGf4^LYpjD3ifM`819i1RF@l|;_ zJ$;FyNSR2gMB_psmYx*?V3>6V@J1d9)#W`sOz)L7{MD{e?T*KR|1&E=pvEhJ4Kj)V z*yJgQEwd}o5eQs=4@&XDDU0dNoGD(!g2+EqC)d<2?KcQ4D?ckwvOgJtuyYGrRj$t{ zs-jC67a3F703oTOP;(X3=u51#@ZR`)%hA_4PtrKxsWg@|mFiMyi;;Qe^*;h=GwPkg zCCLNNzt@`qfCYp!2%Pi3op@n7nubmL^79JdqVEm8?|rz94RZm!s_SI#>j1zz;DDzI zk>OKt1^BR%iW98^2wT6@Vf}{(dVHy}L+mc}nXy+-E zs;FU`Y8MMhv4PitH`7e97Z(jum93kEP4qgx1QS z!-wlM?B=6yW?tXcflaXRiy%0LwcG74=aq3}F+_!K=UW+$B=25bsEAluv&n9SowXPh zTo`|m%eHNQ7N9l&4SaM_RXur5o@*Qg(Y;E1IH!EsC9af6Vtx9C`;rr?Ma`d6!{C@_G6`ON}YV z;r~$W0|;9H<{^MAP2JPJ2j}K;i<;NGE|SI_!Agsav%K9b53}sv74>M$fBSeN?0S5YaXVc+(P1t? zK?xI6fP#9Uq&B!%5y8?y+20|a8H~wQa2(@`Yo}_+9$zd@CQ|y^SQ5aZ9MBNH(CVcJ zV5`DgGm17C6EYkBRFr}>s$4H;V>&#MQTBkv(lcDlsrjS?_rMpb7jt67Yr<*d4t9vD z5APG-PlRQBJlU!W;MMEPIgtGbL|Spzig0MUCiIkPWS9Y_pZGaVDI zY}PW4+G_oiT(weqoo(BRK2tGX93BE)?ERNTu#P|20H3f1=7Bp$LG*UVZzW(hZ>&$= z_(rwgXkcy|X#ZRwd5jHZ&@z@TmylxVjt6&y_WhGJqpCuWk~`19wQV~ye>Y2h<((mz znKjU1Gz7LERM*Pbd~^i_rSv@_Y|l{dmT(NLHrcc4LzwY%frYkf1^^<0uPhiM55C{L z8~s~WDJmXCX(k!anfLd~0b+txZAaOp|A15qP!!kIFA9H|6ttZ*!AG8E;$&iS;B8l_ z961jNu8HdcFY-2V3#?y8ee6_(<<%#YD3@;{61qW-5t0&Ak82=H6}XL?d`?q9%2e4& zmZ-%ZWASJIf9ghAjtsj$B-1~<9_8%^t|OC!zHPhPHIN-ZGY;jX`iTsHRkp6-Vv^a3 z+CKrAov(&WleYY~O!n zA8)(ukgLDz4H+45ttuS;I5KxsD{$Bt2rL_FW7T7r;=}!L{;}=xrmO-o} zAnI)5%S7_5ydLAO0p{MD295oS@_XL)Jg%>mSZ&-j@H#1S%3NhH=c)*BBT&x*h{<9R zwhl1)| zIx@T6Ps&p@aP**U#k`Qiglq!R3QTrA^G@nv@%Q zP>*K^p;GqrKaKO3j%f`4h)q=;Y_l~S;iDpwTGhcSqj&#Aazq?n%d}~@rRDP6vwO^l zIP4;k!iAD2=z^PcA7AdQotq30eZ<9M+Rm*{fO-INeZg_8L&HzzAT@#UMxNWnVvWlN zpE9>p2jy>Q8D!A4aw^QBA!utcVXFWALYeBJ^>D$gVd}!_{D&J-bxcGj6kD*-rN_}! zLuRZW(ea1%k!O~p1hV`Zz-o80cF%m-9o6PH(yI-!?j?)3MTatY=Z<3l2p+2wHUk%! zPx_mJjx5>fLzU^;qkR>u8?@*3qo84cq4Unb%b)?@HhI3Im% zg2QYqK5?3U3t*frifv~o7im z5?~lTldPmqRL%C(_;||0NYkjh*Hr(W;{%t9Pvvg24Q3bpXb-a=EZ_<&D{vTgRb1OCzU7aCs;|TqL6NVd6@>OoSM8+MCGf$9tKvRVu4XDk z+sIkj{zj(V_#L|odUX8)J$lAj!=td;PMzJWoNI*Y8Pya4*t znz01kZQ-d#)7xtdUj)=A_Rii=ba3kGCEy1rLeQxF!-EJ=0OC=oSX0%xsCul(KT~#Q z7&&K1P#c?UhM+z}iN`_0iT?9L<^U!k@77w5`IN}cZvqNqTM#tF^U2UroYYsy+l*?2 zm)KVxDaC)%b2Ac93zMgZr9mM4dk?g!>~@Kn-X-3xMmKE5iUH@A-t2v_gS3F2mUbeI zW0PZ4bt4>`xcn`b?$^4kTy+{CKKDMxFAae8AoizrZ!lpnG*+M1&WNN`<3*~Z8C ztCarP8SCniNG&~jTel}Deg!8QP&X9x?ooYam>KQeS_xVPZ6E!0AIxf}HlWB-L{lpo zXQ@bkVR8~Jf_yDoeg){TK2DfRI2!>lpVJ`l@>Ps>EbSEn!;RtfQakgCdryy?HAUMC z9e?$=5u6e5@PkX9QN$-;{E>*W$G@PS)4EBRNRKFRB+q#_dl^|kz$UA z3~Vl-rhYl7)c$SU+rwk#;}uaiP`WljKm~sb;rJSR*PPN0W?-OY@Q0Z-jywFkmuLT7 zB5;2w2cmZ1J`<%H%xoRtD2bU&LzTS zd(jMWotMlEpFb)0{jRW}h-)Nco8;papqryCb^O&r`#*}cfg>sr!wgvQFzgC6ItRK_ zEh3jD2(RU=G-#FVwg3$2+4;#O zjsCa_;{n4eTfUBha+aCrAG$5h$WL2hqP;;md&irc;fysm{t3c3K6LmSD zZgHVyRWf6#HmJNiHk)cFcMg$*`L?1^qmw{5k}X^7D6H!>$v?e06=YnAfInY>=mAG%>mrgD`u=Cj`c>(QuD+C0 z6E5+xq4I0pHwPk1Vmv>ZjrQ-1Wq* zyX8hCm|PNLLj47R^)RA44ifLYtJ@ju?sfB&gMy{nnG`t#PS8@r36!Fgc^kkH_V$mN z2~7xodDV(3{gSNFUzVtF%nX)nV2G zYWD|ngaam1s2!f%GVR9}YmGq7Mq6r&klGIB_L${1*41bD=MkMUuR%=nx)R67DB(?G zf|@_=>2pw0RYuYMQ(cvI|3Exhf_Sx+S5O^uQX&^a8!z8Vs5<@7`c6gt-e^@g&lcT0 z_oUOv$xbEdxqK2+dfy?jwbeb&@6$A9Ry9HN3Xt0;`ZdzyR1<*8#_cdXrqnyMg&(Pd z>ely;lDq4A1nxuyvx!0wrFcr=UVv!jVxRJQwUFjjIXq44 zvT9r8lJYJv{pL~^cllj^4ABb^G*>2W2g<%(V=-Q7uW`8g+8UI`43o)Z(R;+|ygj7m zJ+>87wwyO)GfAVM7kWWz+2G6r=~af&OIo9G2X!> zDm%JXtX6oJv-+;AQE{)at5|q!)2)fR>0aVGa52XsuQn#lQcG~3VLpZ zhJ>=N0)wj4Yb?$MSPs5_&=Y0ErenMc+Oow6FBC3n?@}uP8za`{(4~dgwNM$?FF^PF zr8Q2>yYl`>kW`mZ6fNmg+nT$9?%wHWW()q%8L=z4e1S9raF zLtQo+ub+ zgG$3djUsLsj+nSeyr#KxvZbmUluDWM+Y6|GMw`2JA1}+)f4-3@w|xm_J$OaC=swujvW%iDpd!N?k(~*w zDPdu0gXdPOMdxjgV4e@6{Cpr;EeN)I%0Iwidqg?u!2W7EPtmW*@*`b$2 zZQ^;LhCt?q$7<9UY2D7yN{0Lf5T3IU2u~j^HW3vK;?6E^gNBr6an&p5nHr9y-7{)A zb+33YZo!DG`OHz)s4XWAvHb#HxHM^#?Eo!j6M{DLY81ZTJ4S$5UKj`WzgI}DTYfP6 z!Z7xyXUqU{`~y4nY^;Eh51qeZ1xmR8s=3zLl-L7!{#KAW(TSPPIn_6jzPz8sZWq0B zTN&D^iKV2N z8|sN}wFFaM81j{UtRPD@+GM{cs(el=kuVUQJo{t~JSrK*@;|(@NES)dVoG4>p_v_+ zq}#An)&6cweh=cgG!0#0-i&1bc9*I9=T8JOs8##2Zem0PTLsPc|4FS9Qv7*)tC&gj z5Oun?%3bjpV?35Dkd~fIlX69x(J3 zt^7TGczaFpfU|P*0RH!|RW7kW;-ch-2Zyb#9D_7}SemcT8T=LAG$9XOsL5yIF75=1 z^YMJ7SE3A{FlLuF+JgzqXQhMl#d^J$i)L#Xb}S`L9)?v?BRV9FxkxQh{NqOf=H+Cc z49D;w(E$)EQS33t@z{-i+{{zf3f|dFxiaIoGM0Ec!|KIHx8_NhYl31CcuWC)CVwtZ zyX9DC0%(jd__VIBPWkP%b-7HH9{v$cphd29j?@)jd;0xUQpfhr?)9;yg&uzNLMI6O zT_4@Og-LDTv3p$H7Gt2ztJuSD#tMF6sMjQAfzfE_bOWDw2|khWNPBKNWJ3$IlNg3( zcY+uNKl<7gu#2*6T0K`s(g8d_=r61WPKhE&K>a6*-%vpSyNAu0-jZ+GNlkJCV#JS@ z>SC_E?Xjm9K1sNzy>+%6l+wpW9IZHli^a70IL0?Yv4=YfEcp|rodOD)fA?xHDY_yO z)OAJl>sS2kOm>h|9Sts~ti0-5V!EUmX!N-3a7xX8)k<`rc>xVWgJe@@V3su*lqY{y z;6-jo=7X+U+kK~!_NS!n(gdizeOzpx-x2itXYc-xmr0>Aelbax zs1pGE>FCI2%#n0ea|bBF95#33?90oETQ!5y`19X-b;0Jvfz4aq7gQ+wcnP#8SrU8l zJrJNcYK8YO8Na6gMu`U@Vvq$w%c1=C{mUfn{@Em*b+MPAqvfQfHqXt z&(a^$4i2FkY{y0Xc_uaZ3+GIG{}Z5p(61VExkM4IY)}`!f81xR4ICm504sh|3+NP3 zK>psl)0um~h@UaZ9|Y|H!(`RC2*cwp)Ma&pWxW*&9PiZyeLPq|E%3~wLZ6qw{Q@1v zQa}%>eVZ*M6h*0vdip=XhfVS4dDO?pgQ?v;Kd@-euhj`^pN(tzniIV~ruGp>l-8SG_2*4cf~_~+fj_+mM9LY28r7%9gF!j$ z*(fgKXN85`Z9Kns#R`E&C7@;N6!_cU zM;j4~0Ps*$$aSMjV@+TKz!RIefd$>XANPltxXvi}{`k-_*_c%%N!_yIJb!`LH&sGc zOl?h|miu-ez(f>Jqq4ZYc#V)fkni`00BFAtTKByMy+se~#>$ek4{)Gc&d9jZMvsL1C{`^du#B{O^=4!l_6vKL#F7Mrx@%rC_!4FSI z)(|fnCA2?}5$Ok=Wyi{ovnad27XkoHt`hX?DS#R+x<{Z|8lFO^;tT|F;T+PHXHqM^ zOj-IeP~)U~y}~CzqamQiy)3eZ`4Rpy)CRPmH2+=yl5KNJ)J;aJCXfM4$uKM>@gBl{ zD(8fI?CA_A(4yoLk_7r7Eq#0$Gfr^~oT2XlEyR9&d>I7bd~;QYB?o+TPm2IEr{_$e+dHSA?k}gIDoC_&aq^51BeZjA0?^CD*e(u!5XLh!+8YT{-O_d^Qi zDG2n zk02}Ht;kCa;zlHR3pGRnS0_PI6|lH`@J~Ga%()QX-wyeqZ=fJ;vw)h2-IL`5RCeGK zt((6kF*4*`)#Zf{esyCM$ZiGKzP}rUy}99lu94c*bNbo0B-4+ad^}b_eN3TPlndr+ zbQ*pK(hEgEq%Rv6SG>6(OyDKhfgJDLBrr7R>wi?JJ=S~1Secz+>~V6B3>gAyT^OUn z5W4K(15$m!#fiq15B!a*@}@ON(=}pT?@;NOF2jqeN= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.53.0.tgz", + "integrity": "sha512-0ZjA5Hcmaoz5Lj6OG0zhfIyeqzJZnLW2CRJA1W17UwMFGRtZAJ9yJKRvPEDA6gkpsIoQxORTSW6sWFiuYncPNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.53.0.tgz", + "integrity": "sha512-kWNodP75iiEaOtemC9F/hlxNBG5E2QUjN1BusnE6m2b4l7Qh/BUO3fGCVsmKJI65VO4VKGGmT43ICvHtTcJ2JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.53.0.tgz", + "integrity": "sha512-YPN45TXD9Wrse185t/Ta7nktZsqpv97oOjCzp2sblHnCL6rBc9TDeJAg1IGl2UpdwnSD05Zu/5wLB4watOUMyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.53.0.tgz", + "integrity": "sha512-qAcYTDJE6m924FDDUQvdD6vh7DYaqOeSpFS74IP37/JRV0v4cGBauyxTF2WzDnokUylQDbqreoFIJZfg0Fitmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.53.0.tgz", + "integrity": "sha512-fQaY+DkSJOpuUVUe8MQTwrdiKAqkJGhpDarB08duBn/sUv7Bkib6MDRQauCcWTWTe4HIW+EbwQP9R4kci1V/Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.53.0.tgz", + "integrity": "sha512-o72tsiEZGfeS/dxL9IADfzcZWGEwKDEe5CvtrBuT//3JR+SHuTtHRI2ZTf7D7bcKagcbojvO8hnkHdfoakSlYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.53.0.tgz", + "integrity": "sha512-Ds16IyPm/dNJPCU8OzApo2gwGrgWT5BYHhE3NFwZbpCveqyvPDB9sZDDkJ5DsdOGT2aC+R3i0/M1OVXF2qdgPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.53.0.tgz", + "integrity": "sha512-oNbT6z4NwD8Pou9VPINGlN/tlG1afESh2EbxqnP6rwl95xKVD/Zlciis1PpNeO/9U/rrajc1+7DcfKi03tX1KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.53.0.tgz", + "integrity": "sha512-G+KZb/yd+qAOFn/cEvTGeLxQm8aP3a0od50l3z/ylccY+/o4YG3TNcjU1tFQHW4mXC137GPyR7W70R0kRQDLnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.53.0.tgz", + "integrity": "sha512-6aVfYd55Un6IUgPLbo84WfgFZlS3L0vA1ttzXL5vahHewUJ8jYgd89TzlWRTeej7w70mb9RWsVlFYGmJ/diQww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.53.0.tgz", + "integrity": "sha512-ke27DqgzCOlt+RbeEdCxtXxMQOnAOi8ujr2wid0DmDKzR95Kw/f9sBsuhBxtjevCqJRJszfRTLY0B1pbO6IhkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.53.0.tgz", + "integrity": "sha512-GngiOqt2Gq4oLno6yXQVj9om+qSO9SWAoduoTOEg79dKZ62brB8OOIvSJG/vDNoanYi6a7Al9uDZwXvi+bcVTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.53.0.tgz", + "integrity": "sha512-6mF9LZMUk0QqWvrnxkxBqhswwz6Xfiwy6/gmTzL5HrlhdVG3ITAqGV2k3XmVThP1h0Ulc3VQwiNCD7/Nr4JNlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.53.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/@babel/runtime": { @@ -50,6 +347,20 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@changesets/apply-release-plan": { "version": "7.0.14", "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.14.tgz", @@ -953,6 +1264,71 @@ "@codingame/monaco-vscode-view-title-bar-service-override": "25.1.2" } }, + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@egjs/hammerjs": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", + "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/hammerjs": "^2.0.36" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -1612,6 +1988,23 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.86", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.86.tgz", + "integrity": "sha512-t3jck5qPQuK1qy+bRn9eCoDQhIB7XSazKz1Fjp8hcan3XOAsTI5Mq/s3F0ekOKSvMQqkVORYK6ns6o6T9f5EMA==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, "node_modules/@inquirer/external-editor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", @@ -1915,6 +2308,31 @@ "node": ">= 4.0.0" } }, + "node_modules/@matdata/yasgui-graph-plugin": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@matdata/yasgui-graph-plugin/-/yasgui-graph-plugin-1.6.2.tgz", + "integrity": "sha512-tdEIRxWIjnCQBmBSMra0q0EURYP6CPOHUAMI+LMIAvWif2Co9XKaYWWsy7VI+aMpRDw2p62qqBN/+pjloR0lhg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "n3": "^2.0.1", + "vis-network": "^9.1.9" + } + }, + "node_modules/@matdata/yasgui-graph-plugin/node_modules/n3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/n3/-/n3-2.0.3.tgz", + "integrity": "sha512-um/toGVENTarHBYIK2TdH6ByBhW75WpdKpv8iTYt9wF2QfBk8s8a16iaWZFUAAC1BKfGdb99kfgx6pltdDwfKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=12.0" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", @@ -2638,1447 +3056,1350 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@streamparser/json": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz", - "integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==", - "license": "MIT" - }, - "node_modules/@tarekraafat/autocomplete.js": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-7.2.0.tgz", - "integrity": "sha512-p1aEcKC/WHpVBuFyRhXq/ie+mgO4QqCNEsdVIPUBgmNqmxV4dVfqYEpk///9vvKyranUUvrlVu4e2tdzAaXKIg==", - "license": "Apache-2.0" - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/autosuggest-highlight": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@types/autosuggest-highlight/-/autosuggest-highlight-3.2.3.tgz", - "integrity": "sha512-8Mb21KWtpn6PvRQXjsKhrXIcxbSloGqNH50RntwGeJsGPW4xvNhfml+3kKulaKpO/7pgZfOmzsJz7VbepArlGQ==", - "dev": true, - "license": "MIT" + "os": [ + "android" + ] }, - "node_modules/@types/blueimp-md5": { - "version": "2.18.2", - "resolved": "https://registry.npmjs.org/@types/blueimp-md5/-/blueimp-md5-2.18.2.tgz", - "integrity": "sha512-dJ9yRry9Olt5GAWlgCtE5dK9d/Dfhn/V7hna86eEO2Pn76+E8Y0S0n61iEUEGhWXXgtKtHxtZLVNwL8X+vLHzg==", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@types/codemirror": { - "version": "0.0.100", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.100.tgz", - "integrity": "sha512-4jGmu1T8vpQrJCe8cbe3KveiJmK2UAt3rZO2qE2sPoMhGLuwW0cMzFYJLyXebbRJg5G3RbuUXLip1IHPUESkFA==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/tern": "*" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/fs-extra": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", - "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/jsonfile": "*", - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/jquery": { - "version": "3.5.33", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz", - "integrity": "sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/sizzle": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/jsonfile": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", - "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/jsuri": { - "version": "1.3.35", - "resolved": "https://registry.npmjs.org/@types/jsuri/-/jsuri-1.3.35.tgz", - "integrity": "sha512-kaRErZwFnRSpQGdAebfwu8jb5QChcuf9R0U49YFNM2zVmSsbraQgELU+dAWce131Hhs8O8pE/7KOVfav+BFLww==", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/lodash-es": { - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", - "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/mocha": { - "version": "10.0.10", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", - "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/n3": { - "version": "1.26.1", - "resolved": "https://registry.npmjs.org/@types/n3/-/n3-1.26.1.tgz", - "integrity": "sha512-TilYHzpU6ecXVJAbV+6o17Z8ZkWLWx6ZJD3IluaU4RiGHxqjU2or9fopxFHS6iXS6qcl5Mg1K3wSx9L8xxJaJQ==", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "@rdfjs/types": "*", - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/node": { - "version": "24.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz", - "integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/node-static": { - "version": "0.7.12", - "resolved": "https://registry.npmjs.org/@types/node-static/-/node-static-0.7.12.tgz", - "integrity": "sha512-jpQIPcHd5r9jnfVZb/WdxZ3shwigGcS0LC/0vM7PaB+n5HvQSuhUiXVJgNGtwNeUliyqMyjr7Ogz04PEkqPQAw==", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/node/node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] }, - "node_modules/@types/papaparse": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.1.tgz", - "integrity": "sha512-esEO+VISsLIyE+JZBmb89NzsYYbpwV8lmv2rPo6oX5y9KhBaIP7hhHgjuTut54qjdKVMufTEcrh5fUl9+58huw==", + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" - } + "optional": true, + "os": [ + "openharmony" + ] }, - "node_modules/@types/sanitize-html": { - "version": "1.27.2", - "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.27.2.tgz", - "integrity": "sha512-DrH26m7CV6PB4YVckjbSIx+xloB7HBolr9Ctm0gZBffSu5dDV4yJKFQGPquJlReVW+xmg59gx+b/8/qYHxZEuw==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "htmlparser2": "^4.1.0" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/sizzle": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.10.tgz", - "integrity": "sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/tern": { - "version": "0.23.9", - "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", - "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/estree": "*" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "optional": true + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", - "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/type-utils": "8.60.1", - "@typescript-eslint/utils": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.60.1", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 4" + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", - "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", - "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.60.1", - "@typescript-eslint/types": "^8.60.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "@shikijs/types": "2.5.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", - "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@shikijs/types": "2.5.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", - "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", "dev": true, "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", - "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/utils": "8.60.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", - "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } + "license": "MIT" }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", - "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", + "node_modules/@streamparser/json": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz", + "integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==", + "license": "MIT" + }, + "node_modules/@tarekraafat/autocomplete.js": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-7.2.0.tgz", + "integrity": "sha512-p1aEcKC/WHpVBuFyRhXq/ie+mgO4QqCNEsdVIPUBgmNqmxV4dVfqYEpk///9vvKyranUUvrlVu4e2tdzAaXKIg==", + "license": "Apache-2.0" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@typescript-eslint/project-service": "8.60.1", - "@typescript-eslint/tsconfig-utils": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "tslib": "^2.4.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", - "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", + "node_modules/@types/autosuggest-highlight": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@types/autosuggest-highlight/-/autosuggest-highlight-3.2.3.tgz", + "integrity": "sha512-8Mb21KWtpn6PvRQXjsKhrXIcxbSloGqNH50RntwGeJsGPW4xvNhfml+3kKulaKpO/7pgZfOmzsJz7VbepArlGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/blueimp-md5": { + "version": "2.18.2", + "resolved": "https://registry.npmjs.org/@types/blueimp-md5/-/blueimp-md5-2.18.2.tgz", + "integrity": "sha512-dJ9yRry9Olt5GAWlgCtE5dK9d/Dfhn/V7hna86eEO2Pn76+E8Y0S0n61iEUEGhWXXgtKtHxtZLVNwL8X+vLHzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", - "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", + "node_modules/@types/codemirror": { + "version": "0.0.100", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.100.tgz", + "integrity": "sha512-4jGmu1T8vpQrJCe8cbe3KveiJmK2UAt3rZO2qE2sPoMhGLuwW0cMzFYJLyXebbRJg5G3RbuUXLip1IHPUESkFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.1", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@types/tern": "*" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } + "license": "MIT" }, - "node_modules/@volar/language-core": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", - "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", "dev": true, - "license": "MIT", - "dependencies": { - "@volar/source-map": "2.4.28" - } + "license": "MIT" }, - "node_modules/@volar/source-map": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", - "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, - "node_modules/@volar/typescript": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", - "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", "dev": true, "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.28", - "path-browserify": "^1.0.1", - "vscode-uri": "^3.0.8" + "@types/jsonfile": "*", + "@types/node": "*" } }, - "node_modules/@vscode/iconv-lite-umd": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", - "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true, "license": "MIT" }, - "node_modules/@zazuko/yasgui": { - "resolved": "packages/yasgui", - "link": true - }, - "node_modules/@zazuko/yasgui-utils": { - "resolved": "packages/utils", - "link": true - }, - "node_modules/@zazuko/yasqe": { - "resolved": "packages/yasqe", - "link": true - }, - "node_modules/@zazuko/yasr": { - "resolved": "packages/yasr", - "link": true + "node_modules/@types/hammerjs": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", + "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", + "dev": true, + "license": "MIT", + "peer": true }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, "license": "MIT", "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" + "@types/unist": "*" } }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "node_modules/@types/jquery": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz", + "integrity": "sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "dependencies": { + "@types/sizzle": "*" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } + "license": "MIT" }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "@types/node": "*" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/@types/jsuri": { + "version": "1.3.35", + "resolved": "https://registry.npmjs.org/@types/jsuri/-/jsuri-1.3.35.tgz", + "integrity": "sha512-kaRErZwFnRSpQGdAebfwu8jb5QChcuf9R0U49YFNM2zVmSsbraQgELU+dAWce131Hhs8O8pE/7KOVfav+BFLww==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/ansi-escapes": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", - "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "dev": true, "license": "MIT", "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@types/lodash": "*" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "@types/unist": "*" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "dev": true, - "license": "Python-2.0" + "license": "MIT" }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/n3": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@types/n3/-/n3-1.26.1.tgz", + "integrity": "sha512-TilYHzpU6ecXVJAbV+6o17Z8ZkWLWx6ZJD3IluaU4RiGHxqjU2or9fopxFHS6iXS6qcl5Mg1K3wSx9L8xxJaJQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@rdfjs/types": "*", + "@types/node": "*" } }, - "node_modules/autoprefixer": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz", - "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==", + "node_modules/@types/node": { + "version": "24.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz", + "integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "browserslist": "^4.27.0", - "caniuse-lite": "^1.0.30001754", - "fraction.js": "^5.3.4", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "undici-types": "~7.16.0" } }, - "node_modules/autosuggest-highlight": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/autosuggest-highlight/-/autosuggest-highlight-3.3.4.tgz", - "integrity": "sha512-j6RETBD2xYnrVcoV1S5R4t3WxOlWZKyDQjkwnggDPSjF5L4jV98ZltBpvPvbkM1HtoSe5o+bNrTHyjPbieGeYA==", + "node_modules/@types/node-static": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/@types/node-static/-/node-static-0.7.12.tgz", + "integrity": "sha512-jpQIPcHd5r9jnfVZb/WdxZ3shwigGcS0LC/0vM7PaB+n5HvQSuhUiXVJgNGtwNeUliyqMyjr7Ogz04PEkqPQAw==", + "dev": true, "license": "MIT", "dependencies": { - "remove-accents": "^0.4.2" + "@types/mime": "^1", + "@types/node": "*" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/@types/node/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, "license": "MIT" }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz", - "integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==", + "node_modules/@types/papaparse": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.1.tgz", + "integrity": "sha512-esEO+VISsLIyE+JZBmb89NzsYYbpwV8lmv2rPo6oX5y9KhBaIP7hhHgjuTut54qjdKVMufTEcrh5fUl9+58huw==", "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "node_modules/better-path-resolve": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", - "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", + "node_modules/@types/sanitize-html": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.27.2.tgz", + "integrity": "sha512-DrH26m7CV6PB4YVckjbSIx+xloB7HBolr9Ctm0gZBffSu5dDV4yJKFQGPquJlReVW+xmg59gx+b/8/qYHxZEuw==", "dev": true, "license": "MIT", "dependencies": { - "is-windows": "^1.0.0" - }, - "engines": { - "node": ">=4" + "htmlparser2": "^4.1.0" } }, - "node_modules/blueimp-md5": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", - "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "node_modules/@types/sizzle": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.10.tgz", + "integrity": "sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==", + "dev": true, "license": "MIT" }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@types/estree": "*" } }, - "node_modules/braces": { + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/unist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", + "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/type-utils": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.60.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "engines": { + "node": ">= 4" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "node_modules/@typescript-eslint/parser": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", + "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", + "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.60.1", + "@typescript-eslint/types": "^8.60.1", + "debug": "^4.4.3" + }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001760", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", - "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", - "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", + "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/choices.js": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/choices.js/-/choices.js-9.1.0.tgz", - "integrity": "sha512-6NnqiE/MNnNAiMzdW7phJ49nMQylkKMQ6La6PAS1+h1VhrGt38MOPnjzEJ3cRaECieqaGpl9eFGtI2icW27r8A==", - "license": "MIT", - "dependencies": { - "deepmerge": "^4.2.2", - "fuse.js": "^3.4.6", - "redux": "^4.1.2" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", + "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", "dev": true, "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, "engines": { - "node": ">= 14.16.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://paulmillr.com/funding/" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/chromium-bidi": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-16.0.1.tgz", - "integrity": "sha512-J63PGu/9PpeCwLIcKYyzWP6yaVL5pxuBc0shlYCYM8BaAkmlwiQboXO1iNbOgSDbVklEyYFfNEcHD8oOAWacUA==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", + "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">=20.19.0 <22.0.0 || >=22.12.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "devtools-protocol": "*" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "node_modules/@typescript-eslint/types": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", + "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "license": "MIT", "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", + "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^5.0.0" + "@typescript-eslint/project-service": "8.60.1", + "@typescript-eslint/tsconfig-utils": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/cli-truncate": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", - "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "node_modules/@typescript-eslint/utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", + "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", "dev": true, "license": "MIT", "dependencies": { - "slice-ansi": "^7.1.0", - "string-width": "^8.0.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1" }, "engines": { - "node": ">=20" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", + "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@typescript-eslint/types": "8.60.1", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "@volar/source-map": "2.4.28" } }, - "node_modules/codemirror": { - "version": "5.65.20", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.20.tgz", - "integrity": "sha512-i5dLDDxwkFCbhjvL2pNjShsojoL3XHyDwsGv1jqETUoW+lzpBKKqNTUWgQwVAOa0tUm4BwekT455ujafi8payA==", + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, "license": "MIT" }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, + "node_modules/@vscode/iconv-lite-umd": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", + "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", "license": "MIT" }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "node_modules/@vue/compiler-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.35.tgz", + "integrity": "sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==", "dev": true, - "license": "MIT" - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/column-resizer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/column-resizer/-/column-resizer-1.4.0.tgz", - "integrity": "sha512-KM5Jh/UBKwVUr01oEGN/OvxF6gZIEn4c1Qde4iHSqNru9hxq93ao3u93qb9N1E1TZ2Sxjh4x7OHGe8v/P8FgkA==", - "license": "BSD-3-Clause", "dependencies": { - "string-hash": "~1.1.3" - }, - "engines": { - "node": ">=8.0.0" + "@babel/parser": "^7.29.3", + "@vue/shared": "3.5.35", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" } }, - "node_modules/compare-versions": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", - "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "dev": true, - "license": "MIT" + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, - "node_modules/confbox": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", - "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "node_modules/@vue/compiler-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.35.tgz", + "integrity": "sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.35", + "@vue/shared": "3.5.35" + } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/@vue/compiler-sfc": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.35.tgz", + "integrity": "sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.35", + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" } }, - "node_modules/datatables.net": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.5.tgz", - "integrity": "sha512-Qrwc+vuw8GHo42u1usWTuriNAMW0VvLPSW3j8g3GxvatiD8wS/ZGW32VAYLLfmF4Hz0C/fo2KB3xZBfcpqqVTQ==", + "node_modules/@vue/compiler-ssr": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.35.tgz", + "integrity": "sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==", + "dev": true, "license": "MIT", "dependencies": { - "jquery": ">=1.7" + "@vue/compiler-dom": "3.5.35", + "@vue/shared": "3.5.35" } }, - "node_modules/datatables.net-dt": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/datatables.net-dt/-/datatables.net-dt-2.3.5.tgz", - "integrity": "sha512-JJkMNM03RUkv4jwAqyXsDHqUTEvti3+15466QiauzqRCmBOSwrHoglt1sRbvBwsv2isrCObp50JemgfG8R3wEQ==", + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "dev": true, "license": "MIT", "dependencies": { - "datatables.net": "2.3.5", - "jquery": ">=1.7" + "@vue/devtools-kit": "^7.7.9" } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" } }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "rfdc": "^1.4.1" } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "node_modules/@vue/reactivity": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.35.tgz", + "integrity": "sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10" + "dependencies": { + "@vue/shared": "3.5.35" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "node_modules/@vue/runtime-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.35.tgz", + "integrity": "sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==", "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/shared": "3.5.35" } }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "node_modules/@vue/runtime-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.35.tgz", + "integrity": "sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/runtime-core": "3.5.35", + "@vue/shared": "3.5.35", + "csstype": "^3.2.3" } }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "node_modules/@vue/server-renderer": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.35.tgz", + "integrity": "sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==", "dev": true, - "license": "Apache-2.0", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35" }, - "engines": { - "node": ">=0.10" + "peerDependencies": { + "vue": "3.5.35" } }, - "node_modules/devtools-protocol": { - "version": "0.0.1624250", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1624250.tgz", - "integrity": "sha512-YFAat/lOiIk0ARmBweG+ygrEcbZrq5B9urRyUoeQKp53MlidHXE2TmTbxKcaXoQj7u/aX+jebDO4BW55rs0WwA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "node_modules/@vue/shared": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.35.tgz", + "integrity": "sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } + "license": "MIT" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", "dev": true, "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", "dev": true, "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/dom-serializer/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" + "vue": "^3.5.13" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" + "node_modules/@zazuko/yasgui": { + "resolved": "packages/yasgui", + "link": true }, - "node_modules/domhandler": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", - "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/@zazuko/yasgui-utils": { + "resolved": "packages/utils", + "link": true + }, + "node_modules/@zazuko/yasqe": { + "resolved": "packages/yasqe", + "link": true + }, + "node_modules/@zazuko/yasr": { + "resolved": "packages/yasr", + "link": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", "dependencies": { - "domelementtype": "^2.0.1" + "event-target-shim": "^5.0.0" }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": ">=6.5" } }, - "node_modules/dompurify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", - "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", - "license": "(MPL-2.0 OR Apache-2.0)", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/domutils/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "node_modules/algoliasearch": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.53.0.tgz", + "integrity": "sha512-OGW1q6b91CRSSeiOnM8LxuR5NYJ2esvw66jUZ4IIvdv+ItNkx3pwLuyR+jaCdbGee4ov5WgUnyPryyh11xvByQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" + "@algolia/abtesting": "1.19.0", + "@algolia/client-abtesting": "5.53.0", + "@algolia/client-analytics": "5.53.0", + "@algolia/client-common": "5.53.0", + "@algolia/client-insights": "5.53.0", + "@algolia/client-personalization": "5.53.0", + "@algolia/client-query-suggestions": "5.53.0", + "@algolia/client-search": "5.53.0", + "@algolia/ingestion": "1.53.0", + "@algolia/monitoring": "1.53.0", + "@algolia/recommend": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { - "node": ">=8.6" + "node": ">= 14.0.0" } }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", "dev": true, "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, "engines": { "node": ">=18" }, @@ -4086,282 +4407,281 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "license": "MIT" + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/esbuild": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", - "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "color-convert": "^2.0.1" }, "engines": { - "node": ">=18" + "node": ">=8" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.0", - "@esbuild/android-arm": "0.28.0", - "@esbuild/android-arm64": "0.28.0", - "@esbuild/android-x64": "0.28.0", - "@esbuild/darwin-arm64": "0.28.0", - "@esbuild/darwin-x64": "0.28.0", - "@esbuild/freebsd-arm64": "0.28.0", - "@esbuild/freebsd-x64": "0.28.0", - "@esbuild/linux-arm": "0.28.0", - "@esbuild/linux-arm64": "0.28.0", - "@esbuild/linux-ia32": "0.28.0", - "@esbuild/linux-loong64": "0.28.0", - "@esbuild/linux-mips64el": "0.28.0", - "@esbuild/linux-ppc64": "0.28.0", - "@esbuild/linux-riscv64": "0.28.0", - "@esbuild/linux-s390x": "0.28.0", - "@esbuild/linux-x64": "0.28.0", - "@esbuild/netbsd-arm64": "0.28.0", - "@esbuild/netbsd-x64": "0.28.0", - "@esbuild/openbsd-arm64": "0.28.0", - "@esbuild/openbsd-x64": "0.28.0", - "@esbuild/openharmony-arm64": "0.28.0", - "@esbuild/sunos-x64": "0.28.0", - "@esbuild/win32-arm64": "0.28.0", - "@esbuild/win32-ia32": "0.28.0", - "@esbuild/win32-x64": "0.28.0" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/eslint": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz", - "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", + "node_modules/autoprefixer": { + "version": "10.4.22", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz", + "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.2", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" + "browserslist": "^4.27.0", + "caniuse-lite": "^1.0.30001754", + "fraction.js": "^5.3.4", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" }, "bin": { - "eslint": "bin/eslint.js" + "autoprefixer": "bin/autoprefixer" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" + "node": "^10 || ^12 || >=14" }, "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "postcss": "^8.1.0" } }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", - "dev": true, + "node_modules/autosuggest-highlight": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/autosuggest-highlight/-/autosuggest-highlight-3.3.4.tgz", + "integrity": "sha512-j6RETBD2xYnrVcoV1S5R4t3WxOlWZKyDQjkwnggDPSjF5L4jV98ZltBpvPvbkM1HtoSe5o+bNrTHyjPbieGeYA==", "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "dependencies": { + "remove-accents": "^0.4.2" } }, - "node_modules/eslint-plugin-jest": { - "version": "29.15.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.15.2.tgz", - "integrity": "sha512-kEN4r9RZl1xcsb4arGq89LrcVdOUFII/JSCwtTPJyv16mDwmPrcuEQwpxqZHeINvcsd7oK5O/rhdGlxFRaZwvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^8.0.0" - }, - "engines": { - "node": "^20.12.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^8.0.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "jest": "*", - "typescript": ">=4.8.4 <7.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "jest": { - "optional": true + { + "type": "patreon", + "url": "https://www.patreon.com/feross" }, - "typescript": { - "optional": true + { + "type": "consulting", + "url": "https://feross.org/support" } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz", + "integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/eslint-plugin-lodash": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-lodash/-/eslint-plugin-lodash-8.0.0.tgz", - "integrity": "sha512-7DA8485FolmWRzh+8t4S8Pzin2TTuWfb0ZW3j/2fYElgk82ZanFz8vDcvc4BBPceYdX1p/za+tkbO68maDBGGw==", + "node_modules/better-path-resolve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", + "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", "dev": true, "license": "MIT", "dependencies": { - "lodash": "^4.17.21" + "is-windows": "^1.0.0" }, "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": ">=9.0.0" + "node": ">=4" } }, - "node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "node_modules/betterknown": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/betterknown/-/betterknown-1.2.0.tgz", + "integrity": "sha512-9kuLOeVkgAXnKsMn9WAHLLJ45Wn51jPuZlS1XPkia7NoBpqM9EbVvr+A2Wr0g1WAEL5rX7/oM428Akd3wCL3vA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "@types/geojson": "^7946.0.16" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, + "license": "MIT", "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "fill-range": "^7.1.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true, + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, - "engines": { - "node": ">=10" + "bin": { + "browserslist": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" - }, + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4369,681 +4689,756 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/caniuse-lite": { + "version": "1.0.30001760", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", + "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chai": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/choices.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/choices.js/-/choices.js-9.1.0.tgz", + "integrity": "sha512-6NnqiE/MNnNAiMzdW7phJ49nMQylkKMQ6La6PAS1+h1VhrGt38MOPnjzEJ3cRaECieqaGpl9eFGtI2icW27r8A==", + "license": "MIT", "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" + "deepmerge": "^4.2.2", + "fuse.js": "^3.4.6", + "redux": "^4.1.2" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">= 14.16.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "node_modules/chromium-bidi": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-16.0.1.tgz", + "integrity": "sha512-J63PGu/9PpeCwLIcKYyzWP6yaVL5pxuBc0shlYCYM8BaAkmlwiQboXO1iNbOgSDbVklEyYFfNEcHD8oOAWacUA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=20.19.0 <22.0.0 || >=22.12.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "devtools-protocol": "*" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">=4.0" + "node": ">=12" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=0.8.x" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "dev": true, + "node_modules/codemirror": { + "version": "5.65.20", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.20.tgz", + "integrity": "sha512-i5dLDDxwkFCbhjvL2pNjShsojoL3XHyDwsGv1jqETUoW+lzpBKKqNTUWgQwVAOa0tUm4BwekT455ujafi8payA==", "license": "MIT" }, - "node_modules/extendable-error": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", - "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, "engines": { - "node": ">=8.6.0" + "node": ">=0.1.90" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", + "node_modules/column-resizer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/column-resizer/-/column-resizer-1.4.0.tgz", + "integrity": "sha512-KM5Jh/UBKwVUr01oEGN/OvxF6gZIEn4c1Qde4iHSqNru9hxq93ao3u93qb9N1E1TZ2Sxjh4x7OHGe8v/P8FgkA==", + "license": "BSD-3-Clause", "dependencies": { - "is-glob": "^4.0.1" + "string-hash": "~1.1.3" }, "engines": { - "node": ">= 6" + "node": ">=8.0.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "dev": true, - "license": "MIT" + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", "dev": true, "license": "MIT" }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "node_modules/component-emitter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-2.0.0.tgz", + "integrity": "sha512-4m5s3Me2xxlVKG9PkZpQqHQR7bgpnN7joDMJ4yvVkVXngjoITG76IaZmzmywSeRTeTpc6N6r3H3+KyUurV8OYw==", "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "is-what": "^5.2.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "dev": true, + "license": "MIT" + }, + "node_modules/datatables.net": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.5.tgz", + "integrity": "sha512-Qrwc+vuw8GHo42u1usWTuriNAMW0VvLPSW3j8g3GxvatiD8wS/ZGW32VAYLLfmF4Hz0C/fo2KB3xZBfcpqqVTQ==", "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "jquery": ">=1.7" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" + "node_modules/datatables.net-dt": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/datatables.net-dt/-/datatables.net-dt-2.3.5.tgz", + "integrity": "sha512-JJkMNM03RUkv4jwAqyXsDHqUTEvti3+15466QiauzqRCmBOSwrHoglt1sRbvBwsv2isrCObp50JemgfG8R3wEQ==", + "license": "MIT", + "dependencies": { + "datatables.net": "2.3.5", + "jquery": ">=1.7" } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "ms": "^2.1.3" }, "engines": { - "node": ">=16" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "license": "MIT", "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" + "node": ">=0.10" } }, - "node_modules/fs-extra": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", - "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, "engines": { - "node": ">=14.14" + "node": ">=0.10.0" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/fuse.js": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz", - "integrity": "sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==", - "license": "Apache-2.0", "engines": { "node": ">=6" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, + "license": "Apache-2.0", + "optional": true, "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" + "detect-libc": "bin/detect-libc.js" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "dev": true, "license": "MIT", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" + "dequal": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "node_modules/devtools-protocol": { + "version": "0.0.1624250", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1624250.tgz", + "integrity": "sha512-YFAat/lOiIk0ARmBweG+ygrEcbZrq5B9urRyUoeQKp53MlidHXE2TmTbxKcaXoQj7u/aX+jebDO4BW55rs0WwA==", "dev": true, - "license": "ISC" + "license": "BSD-3-Clause" }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.3.1" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "license": "MIT", - "bin": { - "he": "bin/he" + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/htmlparser2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", - "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, "license": "MIT", "dependencies": { "domelementtype": "^2.0.1", - "domhandler": "^3.0.0", - "domutils": "^2.0.0", + "domhandler": "^4.2.0", "entities": "^2.0.0" - } - }, - "node_modules/human-id": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", - "integrity": "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==", - "dev": true, - "license": "MIT", - "bin": { - "human-id": "dist/cli.js" - } - }, - "node_modules/husky": { - "version": "9.1.7", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", - "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", - "dev": true, - "license": "MIT", - "bin": { - "husky": "bin.js" - }, - "engines": { - "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/typicode" + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "domelementtype": "^2.2.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, "funding": [ { "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "url": "https://github.com/sponsors/fb55" } ], - "license": "BSD-3-Clause" + "license": "BSD-2-Clause" }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.0.1" + }, "engines": { "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/immutable": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", - "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", - "dev": true, - "license": "MIT" - }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/dompurify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "get-east-asian-width": "^1.3.1" + "domelementtype": "^2.2.0" }, "engines": { - "node": ">=18" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" } }, - "node_modules/is-subdir": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", - "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", - "dependencies": { - "better-path-resolve": "1.0.0" - }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -5053,2506 +5448,4702 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "node_modules/eslint": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz", + "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", "dev": true, "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, "engines": { - "node": ">=0.10.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/eslint-config-prettier" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/jquery": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "node_modules/eslint-plugin-jest": { + "version": "29.15.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.15.2.tgz", + "integrity": "sha512-kEN4r9RZl1xcsb4arGq89LrcVdOUFII/JSCwtTPJyv16mDwmPrcuEQwpxqZHeINvcsd7oK5O/rhdGlxFRaZwvQ==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "@typescript-eslint/utils": "^8.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": "^20.12.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "jest": "*", + "typescript": ">=4.8.4 <7.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/jschardet": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-3.1.4.tgz", - "integrity": "sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==", - "license": "LGPL-2.1+", + "node_modules/eslint-plugin-lodash": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-lodash/-/eslint-plugin-lodash-8.0.0.tgz", + "integrity": "sha512-7DA8485FolmWRzh+8t4S8Pzin2TTuWfb0ZW3j/2fYElgk82ZanFz8vDcvc4BBPceYdX1p/za+tkbO68maDBGGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + }, "engines": { - "node": ">=0.1.90" + "node": ">=10" + }, + "peerDependencies": { + "eslint": ">=9.0.0" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, - "license": "MIT" + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsuri": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsuri/-/jsuri-1.3.1.tgz", - "integrity": "sha512-LLdAeqOf88/X0hylAI7oSir6QUsz/8kOW0FcJzzu/SJRfORA/oPHycAOthkNp7eLPlTAbqVDFbqNRHkRVzEA3g==", "engines": { - "node": "*" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/kolorist": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", - "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MPL-2.0", + "license": "MIT", "dependencies": { - "detect-libc": "^2.0.3" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 12.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, "engines": { - "node": ">= 12.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/eslint" } }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], + "license": "Apache-2.0", "engines": { - "node": ">= 12.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/eslint" } }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/extendable-error": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", + "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs-extra": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fuse.js": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz", + "integrity": "sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==", + "license": "Apache-2.0", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "node_modules/human-id": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", + "integrity": "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-subdir": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", + "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "better-path-resolve": "1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jschardet": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-3.1.4.tgz", + "integrity": "sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==", + "license": "LGPL-2.1+", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsuri": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsuri/-/jsuri-1.3.1.tgz", + "integrity": "sha512-LLdAeqOf88/X0hylAI7oSir6QUsz/8kOW0FcJzzu/SJRfORA/oPHycAOthkNp7eLPlTAbqVDFbqNRHkRVzEA3g==", + "engines": { + "node": "*" + } + }, + "node_modules/keycharm": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz", + "integrity": "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==", + "dev": true, + "license": "(Apache-2.0 OR MIT)", + "peer": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lint-staged": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", + "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.2", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "nano-spawn": "^2.0.0", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimatch/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=16 || 14 >=14.17" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/modern-tar": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.6.tgz", + "integrity": "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/monaco-editor": { + "name": "@codingame/monaco-vscode-editor-api", + "version": "25.1.2", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-editor-api/-/monaco-vscode-editor-api-25.1.2.tgz", + "integrity": "sha512-dVXoBLRN8vyFHsLY6iYISaNetZ3ispXLut0qL+jvN0e0CEFkUv1F/3EAE7myptrJSS/N1AptrRIxATT3lwFP+Q==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "25.1.2" + } + }, + "node_modules/monaco-languageclient": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/monaco-languageclient/-/monaco-languageclient-10.7.0.tgz", + "integrity": "sha512-oA5cOFixkF4bspVL2zMSn48LvlNR/Cu3vJ8MCVam3PdjobSULGgHtOASuZIi3FgWK42X1z8/6hrG0LCjvNu1Hw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "^25.1.2", + "@codingame/monaco-vscode-configuration-service-override": "^25.1.2", + "@codingame/monaco-vscode-editor-api": "^25.1.2", + "@codingame/monaco-vscode-editor-service-override": "^25.1.2", + "@codingame/monaco-vscode-extension-api": "^25.1.2", + "@codingame/monaco-vscode-extensions-service-override": "^25.1.2", + "@codingame/monaco-vscode-language-pack-cs": "^25.1.2", + "@codingame/monaco-vscode-language-pack-de": "^25.1.2", + "@codingame/monaco-vscode-language-pack-es": "^25.1.2", + "@codingame/monaco-vscode-language-pack-fr": "^25.1.2", + "@codingame/monaco-vscode-language-pack-it": "^25.1.2", + "@codingame/monaco-vscode-language-pack-ja": "^25.1.2", + "@codingame/monaco-vscode-language-pack-ko": "^25.1.2", + "@codingame/monaco-vscode-language-pack-pl": "^25.1.2", + "@codingame/monaco-vscode-language-pack-pt-br": "^25.1.2", + "@codingame/monaco-vscode-language-pack-qps-ploc": "^25.1.2", + "@codingame/monaco-vscode-language-pack-ru": "^25.1.2", + "@codingame/monaco-vscode-language-pack-tr": "^25.1.2", + "@codingame/monaco-vscode-language-pack-zh-hans": "^25.1.2", + "@codingame/monaco-vscode-language-pack-zh-hant": "^25.1.2", + "@codingame/monaco-vscode-languages-service-override": "^25.1.2", + "@codingame/monaco-vscode-localization-service-override": "^25.1.2", + "@codingame/monaco-vscode-log-service-override": "^25.1.2", + "@codingame/monaco-vscode-model-service-override": "^25.1.2", + "@codingame/monaco-vscode-monarch-service-override": "^25.1.2", + "@codingame/monaco-vscode-textmate-service-override": "^25.1.2", + "@codingame/monaco-vscode-theme-defaults-default-extension": "^25.1.2", + "@codingame/monaco-vscode-theme-service-override": "^25.1.2", + "@codingame/monaco-vscode-views-service-override": "^25.1.2", + "@codingame/monaco-vscode-workbench-service-override": "^25.1.2", + "vscode": "npm:@codingame/monaco-vscode-extension-api@^25.1.2", + "vscode-languageclient": "~9.0.1", + "vscode-languageserver-protocol": "~3.17.5", + "vscode-ws-jsonrpc": "~3.5.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/n3": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/n3/-/n3-1.26.0.tgz", + "integrity": "sha512-SQknS0ua90rN+3RHuk8BeIqeYyqIH/+ecViZxX08jR4j6MugqWRjtONl3uANG/crWXnOM2WIqBJtjIhVYFha+w==", + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/nano-spawn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", + "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-static": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/node-static/-/node-static-0.7.11.tgz", + "integrity": "sha512-zfWC/gICcqb74D9ndyvxZWaI1jzcoHmf4UTHWQchBNuNMxdBLJMDiUgZ1tjGLEIe/BMhj2DxKD8HOuc2062pDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": ">=0.6.0", + "mime": "^1.2.9", + "optimist": ">=0.3.4" + }, + "bin": { + "static": "bin/cli.js" + }, + "engines": { + "node": ">= 0.4.1" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, "engines": { - "node": ">= 12.0.0" + "node": ">= 0.8.0" + } + }, + "node_modules/outdent": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", + "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^2.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "engines": { + "node": ">=8" } }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "engines": { + "node": ">=8" } }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=6" } }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=6" } }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], + "license": "BlueOak-1.0.0" + }, + "node_modules/package-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } + }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "license": "MIT" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=8" } }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">= 12.0.0" + "node": ">=16 || 14 >=14.18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lightningcss/node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "Apache-2.0", + "license": "ISC" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/antonk52" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/lint-staged": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", - "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, "license": "MIT", - "dependencies": { - "commander": "^14.0.2", - "listr2": "^9.0.5", - "micromatch": "^4.0.8", - "nano-spawn": "^2.0.0", - "pidtree": "^0.6.0", - "string-argv": "^0.3.2", - "yaml": "^2.8.1" - }, "bin": { - "lint-staged": "bin/lint-staged.js" + "pidtree": "bin/pidtree.js" }, "engines": { - "node": ">=20.17" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" + "node": ">=0.10" } }, - "node_modules/lint-staged/node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=6" } }, - "node_modules/listr2": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", - "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", "dev": true, "license": "MIT", "dependencies": { - "cli-truncate": "^5.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">=20.0.0" + "node": "^10 || ^12 || >=14" } }, - "node_modules/local-pkg": { + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.29.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz", + "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", - "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.3.0", - "quansync": "^0.2.11" + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/antfu" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.6.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "node_modules/proj4": { + "version": "2.20.8", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.20.8.tgz", + "integrity": "sha512-1C8sfT4xY4PAPwk0MroFBTGF4R4bzDXdmPQTGYVLsoNssrZ9odzObxS2dTeGBty8jW8KO7h16C1Hs2JP+ctfFw==", "dev": true, - "license": "MIT" - }, - "node_modules/lodash-es": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", - "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", - "license": "MIT" + "license": "MIT", + "dependencies": { + "mgrs": "1.0.0", + "wkt-parser": "^1.5.5" + }, + "funding": { + "url": "https://github.com/sponsors/ahocevar" + } }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", "dev": true, - "license": "MIT" + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "node_modules/puppeteer": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-25.1.0.tgz", + "integrity": "sha512-7L6/0JM7XStK99lIL4xQySyNEXNfII6pk0BxkI5kKBTOhR7AsoQiv067YTsE/rIXxQiq9ajlO4WcqBjS/FWK1A==", "dev": true, - "license": "MIT", + "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" + "@puppeteer/browsers": "3.0.4", + "chromium-bidi": "16.0.1", + "devtools-protocol": "0.0.1624250", + "lilconfig": "^3.1.3", + "puppeteer-core": "25.1.0", + "typed-query-selector": "^2.12.2" }, - "engines": { - "node": ">=18" + "bin": { + "puppeteer": "lib/puppeteer/node/cli.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=22.12.0" } }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "node_modules/puppeteer-core": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.1.0.tgz", + "integrity": "sha512-jKzy5y4WG6uNuFbTWgW1D7mqoT9o0nllc/6a1DGF775T1mPmgw3scdFEtEq67yVFikavQmbYq6NLfbTfxHSlqQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "3.0.4", + "chromium-bidi": "16.0.1", + "devtools-protocol": "0.0.1624250", + "typed-query-selector": "^2.12.2", + "webdriver-bidi-protocol": "0.4.2", + "ws": "^8.21.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=22.12.0" } }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "node_modules/qlue-ls": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/qlue-ls/-/qlue-ls-2.8.0.tgz", + "integrity": "sha512-QK3JLtc+Ifzrk9W+0uXxN1WSPgUflF+Vd12diAXy/2oX1/0vnmuOpzsZfeL/WOrx34Bebt71VLW0HwLsJurhew==", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "safe-buffer": "^5.1.0" } }, - "node_modules/marked": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", - "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "node_modules/read-yaml-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", + "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", + "dev": true, "license": "MIT", - "bin": { - "marked": "bin/marked.js" + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.6.1", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">= 18" + "node": ">=6" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/read-yaml-file/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 8" + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/read-yaml-file/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">=8.6" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", - "bin": { - "mime": "cli.js" + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" + "node": ">= 14.18.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, - "node_modules/minimatch/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" + "dependencies": { + "@babel/runtime": "^7.9.2" } }, - "node_modules/minimatch/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==", - "dev": true, - "license": "MIT" - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" + "regex-utilities": "^2.3.0" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" - }, - "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.16.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.3" + "regex-utilities": "^2.3.0" } }, - "node_modules/mlly/node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", "dev": true, "license": "MIT" }, - "node_modules/mlly/node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "node_modules/remove-accents": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.4.tgz", + "integrity": "sha512-EpFcOa/ISetVHEXqu+VwI96KZBmq+a8LJnGkaeFw45epGlxIZz5dhEEnNZMsQXgORu3qaMoLX4qJCzOik6ytAg==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/mocha": { - "version": "11.7.5", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", - "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=8" } }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "p-locate": "^5.0.0" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=10" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "yocto-queue": "^0.1.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=10" + "node": "18 || 20 || >=22" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/rolldown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.95.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.95.0.tgz", + "integrity": "sha512-9QMjhLq+UkOg/4bb8Lt8A+hJZvY3t+9xeZMKSBtBEgxrXA3ed5Ts4NDreUkYgJP1BTmrscQE/xYhf7iShow6lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" }, "engines": { - "node": ">=10" + "node": ">=14.0.0" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, - "node_modules/modern-tar": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.6.tgz", - "integrity": "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==", + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", "dev": true, "license": "MIT", + "peer": true + }, + "node_modules/semver": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=18.0.0" + "node": ">=10" } }, - "node_modules/monaco-editor": { - "name": "@codingame/monaco-vscode-editor-api", - "version": "25.1.2", - "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-editor-api/-/monaco-vscode-editor-api-25.1.2.tgz", - "integrity": "sha512-dVXoBLRN8vyFHsLY6iYISaNetZ3ispXLut0qL+jvN0e0CEFkUv1F/3EAE7myptrJSS/N1AptrRIxATT3lwFP+Q==", - "license": "MIT", + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@codingame/monaco-vscode-api": "25.1.2" + "randombytes": "^2.1.0" } }, - "node_modules/monaco-languageclient": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/monaco-languageclient/-/monaco-languageclient-10.7.0.tgz", - "integrity": "sha512-oA5cOFixkF4bspVL2zMSn48LvlNR/Cu3vJ8MCVam3PdjobSULGgHtOASuZIi3FgWK42X1z8/6hrG0LCjvNu1Hw==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { - "@codingame/monaco-vscode-api": "^25.1.2", - "@codingame/monaco-vscode-configuration-service-override": "^25.1.2", - "@codingame/monaco-vscode-editor-api": "^25.1.2", - "@codingame/monaco-vscode-editor-service-override": "^25.1.2", - "@codingame/monaco-vscode-extension-api": "^25.1.2", - "@codingame/monaco-vscode-extensions-service-override": "^25.1.2", - "@codingame/monaco-vscode-language-pack-cs": "^25.1.2", - "@codingame/monaco-vscode-language-pack-de": "^25.1.2", - "@codingame/monaco-vscode-language-pack-es": "^25.1.2", - "@codingame/monaco-vscode-language-pack-fr": "^25.1.2", - "@codingame/monaco-vscode-language-pack-it": "^25.1.2", - "@codingame/monaco-vscode-language-pack-ja": "^25.1.2", - "@codingame/monaco-vscode-language-pack-ko": "^25.1.2", - "@codingame/monaco-vscode-language-pack-pl": "^25.1.2", - "@codingame/monaco-vscode-language-pack-pt-br": "^25.1.2", - "@codingame/monaco-vscode-language-pack-qps-ploc": "^25.1.2", - "@codingame/monaco-vscode-language-pack-ru": "^25.1.2", - "@codingame/monaco-vscode-language-pack-tr": "^25.1.2", - "@codingame/monaco-vscode-language-pack-zh-hans": "^25.1.2", - "@codingame/monaco-vscode-language-pack-zh-hant": "^25.1.2", - "@codingame/monaco-vscode-languages-service-override": "^25.1.2", - "@codingame/monaco-vscode-localization-service-override": "^25.1.2", - "@codingame/monaco-vscode-log-service-override": "^25.1.2", - "@codingame/monaco-vscode-model-service-override": "^25.1.2", - "@codingame/monaco-vscode-monarch-service-override": "^25.1.2", - "@codingame/monaco-vscode-textmate-service-override": "^25.1.2", - "@codingame/monaco-vscode-theme-defaults-default-extension": "^25.1.2", - "@codingame/monaco-vscode-theme-service-override": "^25.1.2", - "@codingame/monaco-vscode-views-service-override": "^25.1.2", - "@codingame/monaco-vscode-workbench-service-override": "^25.1.2", - "vscode": "npm:@codingame/monaco-vscode-extension-api@^25.1.2", - "vscode-languageclient": "~9.0.1", - "vscode-languageserver-protocol": "~3.17.5", - "vscode-ws-jsonrpc": "~3.5.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" + "node": ">=8" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", "dev": true, - "license": "MIT" - }, - "node_modules/n3": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/n3/-/n3-1.26.0.tgz", - "integrity": "sha512-SQknS0ua90rN+3RHuk8BeIqeYyqIH/+ecViZxX08jR4j6MugqWRjtONl3uANG/crWXnOM2WIqBJtjIhVYFha+w==", "license": "MIT", "dependencies": { - "buffer": "^6.0.3", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">=12.0" + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" } }, - "node_modules/nano-spawn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", - "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=20.17" + "node": ">=14" }, "funding": { - "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=8" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-static": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/node-static/-/node-static-0.7.11.tgz", - "integrity": "sha512-zfWC/gICcqb74D9ndyvxZWaI1jzcoHmf4UTHWQchBNuNMxdBLJMDiUgZ1tjGLEIe/BMhj2DxKD8HOuc2062pDQ==", + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "dev": true, "license": "MIT", "dependencies": { - "colors": ">=0.6.0", - "mime": "^1.2.9", - "optimist": ">=0.3.4" - }, - "bin": { - "static": "bin/cli.js" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">= 0.4.1" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/sortablejs": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.6.tgz", + "integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/optimist": { + "node_modules/source-map-support/node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "MIT/X11", - "dependencies": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "dev": true, "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/outdent": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", - "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", + "node_modules/spawndamnit": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", + "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", "dev": true, - "license": "MIT" + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "cross-spawn": "^7.0.5", + "signal-exit": "^4.0.1" + } }, - "node_modules/p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", "dev": true, - "license": "MIT", - "dependencies": { - "p-map": "^2.0.0" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, "engines": { "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/store": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz", + "integrity": "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==", "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.6.19" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" + "node_modules/string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", + "license": "CC0-1.0" }, - "node_modules/package-manager-detector": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", - "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", "dev": true, "license": "MIT", "dependencies": { - "quansync": "^0.2.7" + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/papaparse": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", - "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", - "license": "MIT" - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8.6" + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=8" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, "engines": { - "node": ">=0.10" + "node": ">=4" } }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-types": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", - "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", "dev": true, "license": "MIT", "dependencies": { - "confbox": "^0.2.4", - "exsolve": "^1.0.8", - "pathe": "^2.0.3" + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" } }, - "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "has-flag": "^4.0.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=8" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", "dev": true, "license": "MIT" }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/prettier": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" }, "engines": { - "node": ">=14" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" - } - }, - "node_modules/puppeteer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-25.1.0.tgz", - "integrity": "sha512-7L6/0JM7XStK99lIL4xQySyNEXNfII6pk0BxkI5kKBTOhR7AsoQiv067YTsE/rIXxQiq9ajlO4WcqBjS/FWK1A==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "3.0.4", - "chromium-bidi": "16.0.1", - "devtools-protocol": "0.0.1624250", - "lilconfig": "^3.1.3", - "puppeteer-core": "25.1.0", - "typed-query-selector": "^2.12.2" + "node": ">=12.0.0" }, - "bin": { - "puppeteer": "lib/puppeteer/node/cli.js" + "peerDependencies": { + "picomatch": "^3 || ^4" }, - "engines": { - "node": ">=22.12.0" + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/puppeteer-core": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.1.0.tgz", - "integrity": "sha512-jKzy5y4WG6uNuFbTWgW1D7mqoT9o0nllc/6a1DGF775T1mPmgw3scdFEtEq67yVFikavQmbYq6NLfbTfxHSlqQ==", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "3.0.4", - "chromium-bidi": "16.0.1", - "devtools-protocol": "0.0.1624250", - "typed-query-selector": "^2.12.2", - "webdriver-bidi-protocol": "0.4.2", - "ws": "^8.21.0" - }, + "license": "MIT", "engines": { - "node": ">=22.12.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/qlue-ls": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/qlue-ls/-/qlue-ls-2.8.0.tgz", - "integrity": "sha512-QK3JLtc+Ifzrk9W+0uXxN1WSPgUflF+Vd12diAXy/2oX1/0vnmuOpzsZfeL/WOrx34Bebt71VLW0HwLsJurhew==", - "license": "SEE LICENSE IN LICENSE" - }, - "node_modules/quansync": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", - "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/antfu" - }, - { - "type": "individual", - "url": "https://github.com/sponsors/sxzz" - } - ], - "license": "MIT" - }, - "node_modules/query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", "license": "MIT", "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" + "is-number": "^7.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", "dev": true, "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/read-yaml-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", - "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.5", - "js-yaml": "^3.6.1", - "pify": "^4.0.1", - "strip-bom": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/read-yaml-file/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", "dev": true, "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" + "peerDependencies": { + "typescript": ">=3.7.0" } }, - "node_modules/read-yaml-file/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "prelude-ls": "^1.2.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "node_modules/typed-query-selector": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", + "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=14.17" } }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "node_modules/typescript-eslint": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz", + "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.60.1", + "@typescript-eslint/parser": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1" + }, "engines": { - "node": ">= 14.18.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" - } + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" }, - "node_modules/remove-accents": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.4.tgz", - "integrity": "sha512-EpFcOa/ISetVHEXqu+VwI96KZBmq+a8LJnGkaeFw45epGlxIZz5dhEEnNZMsQXgORu3qaMoLX4qJCzOik6ytAg==", + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, "license": "MIT" }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/resolve-from": { + "node_modules/unist-util-position": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" + "@types/unist": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "dev": true, "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", - "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "glob": "^13.0.3", - "package-json-from-dist": "^1.0.1" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "dev": true, + "license": "MIT", "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" }, "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18.12.0" } }, - "node_modules/rimraf/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "node_modules/unplugin-dts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unplugin-dts/-/unplugin-dts-1.0.2.tgz", + "integrity": "sha512-VbNiMD0LMl/t6nJueGtrCp79N7ZO1nquxj/FUybJDnKwZGsnW2wjdwBSzA3QEHujoxmxZIptsG43hL7LzXE96w==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "@rollup/pluginutils": "^5.1.4", + "@volar/typescript": "^2.4.26", + "compare-versions": "^6.1.1", + "debug": "^4.4.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.1.1", + "magic-string": "^0.30.17", + "unplugin": "^2.3.2" }, - "engines": { - "node": "18 || 20 || >=22" + "peerDependencies": { + "@microsoft/api-extractor": ">=7", + "@rspack/core": "^1", + "@vue/language-core": "~3.1.5", + "esbuild": "*", + "rolldown": "*", + "rollup": ">=3", + "typescript": ">=4", + "vite": ">=3", + "webpack": "^4 || ^5" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "@vue/language-core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "node_modules/unplugin/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.133.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=12" }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/update-browserslist-db": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", + "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ { "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/sass": { - "version": "1.95.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.95.0.tgz", - "integrity": "sha512-9QMjhLq+UkOg/4bb8Lt8A+hJZvY3t+9xeZMKSBtBEgxrXA3ed5Ts4NDreUkYgJP1BTmrscQE/xYhf7iShow6lw==", - "dev": true, "license": "MIT", "dependencies": { - "chokidar": "^4.0.0", - "immutable": "^5.0.2", - "source-map-js": ">=0.6.2 <2.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" + "update-browserslist-db": "cli.js" }, - "optionalDependencies": { - "@parcel/watcher": "^2.4.1" + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/semver": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", - "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/esm/bin/uuid" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/vis-data": { + "version": "7.1.10", + "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.10.tgz", + "integrity": "sha512-23juM9tdCaHTX5vyIQ7XBzsfZU0Hny+gSTwniLrfFcmw9DOm7pi3+h9iEBsoZMp5rX6KNqWwc1MF0fkAmWVuoQ==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, + "license": "(Apache-2.0 OR MIT)", + "peer": true, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/visjs" + }, + "peerDependencies": { + "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "vis-util": "^5.0.1" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/vis-network": { + "version": "9.1.13", + "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.13.tgz", + "integrity": "sha512-HLeHd5KZS92qzO1kC59qMh1/FWAZxMUEwUWBwDMoj6RKj/Ajkrgy/heEYo0Zc8SZNQ2J+u6omvK2+a28GX1QuQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "(Apache-2.0 OR MIT)", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/visjs" + }, + "peerDependencies": { + "@egjs/hammerjs": "^2.0.0", + "component-emitter": "^1.3.0 || ^2.0.0", + "keycharm": "^0.2.0 || ^0.3.0 || ^0.4.0", + "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "vis-data": "^6.3.0 || ^7.0.0", + "vis-util": "^5.0.1" } }, - "node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "node_modules/vis-util": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-5.0.7.tgz", + "integrity": "sha512-E3L03G3+trvc/X4LXvBfih3YIHcKS2WrP0XTdZefr6W6Qi/2nNCqZfe4JFfJU6DcQLm6Gxqj2Pfl+02859oL5A==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, + "license": "(Apache-2.0 OR MIT)", + "peer": true, "engines": { - "node": ">=18" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/visjs" + }, + "peerDependencies": { + "@egjs/hammerjs": "^2.0.0", + "component-emitter": "^1.3.0 || ^2.0.0" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "node_modules/vite": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, "engines": { - "node": ">=12" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/sortablejs": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.6.tgz", - "integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==", - "license": "MIT" - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/vite-plugin-dts": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-5.0.2.tgz", + "integrity": "sha512-lNeHS+dwGju6eRmNvZQt8Shwv9j3m98hbHse/lIbLq9q3yE2DcIOBBYQEVUF6tS0kOmv+VA9Z5FqmzFnGe4U8g==", "dev": true, "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" + "unplugin-dts": "1.0.2" + }, + "peerDependencies": { + "@microsoft/api-extractor": ">=7", + "rollup": ">=3", + "vite": ">=3" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/spawndamnit": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", - "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", + "node_modules/vite-plugin-wasm": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.6.0.tgz", + "integrity": "sha512-mL/QPziiIA4RAA6DkaZZzOstdwbW5jO4Vz7Zenj0wieKWBlNvIvX5L5ljum9lcUX0ShNfBgCNLKTjNkRVVqcsw==", "dev": true, - "license": "SEE LICENSE IN LICENSE", - "dependencies": { - "cross-spawn": "^7.0.5", - "signal-exit": "^4.0.1" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", "license": "MIT", - "engines": { - "node": ">=6" + "peerDependencies": { + "vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/store": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz", - "integrity": "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" + "node_modules/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } } }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "node_modules/vitepress/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=0.6.19" + "node": ">=12" } }, - "node_modules/string-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", - "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", - "license": "CC0-1.0" - }, - "node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "node_modules/vitepress/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/vitepress/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/vitepress/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "node_modules/vitepress/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "node_modules/vitepress/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/vitepress/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/vitepress/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/vitepress/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/vitepress/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/vitepress/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "node_modules/vitepress/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "node_modules/vitepress/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=12" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/vitepress/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "node": ">=12" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/vitepress/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/vitepress/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8.0" + "node": ">=12" } }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "node_modules/vitepress/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" + "node": ">=12" } }, - "node_modules/ts-essentials": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", - "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", + "node_modules/vitepress/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "peerDependencies": { - "typescript": ">=3.7.0" + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/vitepress/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=12" } }, - "node_modules/typed-query-selector": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", - "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "node_modules/vitepress/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=14.17" + "node": ">=12" } }, - "node_modules/typescript-eslint": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz", - "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==", + "node_modules/vitepress/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.60.1", - "@typescript-eslint/parser": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/utils": "8.60.1" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=12" } }, - "node_modules/ufo": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", - "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "node_modules/vitepress/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/vitepress/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "node_modules/vitepress/node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, "engines": { - "node": ">=18.12.0" + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" } }, - "node_modules/unplugin-dts": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unplugin-dts/-/unplugin-dts-1.0.2.tgz", - "integrity": "sha512-VbNiMD0LMl/t6nJueGtrCp79N7ZO1nquxj/FUybJDnKwZGsnW2wjdwBSzA3QEHujoxmxZIptsG43hL7LzXE96w==", + "node_modules/vitepress/node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", "dev": true, "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^5.1.4", - "@volar/typescript": "^2.4.26", - "compare-versions": "^6.1.1", - "debug": "^4.4.0", - "kolorist": "^1.8.0", - "local-pkg": "^1.1.1", - "magic-string": "^0.30.17", - "unplugin": "^2.3.2" + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "@microsoft/api-extractor": ">=7", - "@rspack/core": "^1", - "@vue/language-core": "~3.1.5", - "esbuild": "*", - "rolldown": "*", - "rollup": ">=3", - "typescript": ">=4", - "vite": ">=3", - "webpack": "^4 || ^5" + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" }, "peerDependenciesMeta": { - "@microsoft/api-extractor": { + "async-validator": { "optional": true }, - "@rspack/core": { + "axios": { "optional": true }, - "@vue/language-core": { + "change-case": { "optional": true }, - "esbuild": { + "drauu": { "optional": true }, - "rolldown": { + "focus-trap": { "optional": true }, - "rollup": { + "fuse.js": { "optional": true }, - "vite": { + "idb-keyval": { "optional": true }, - "webpack": { + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { "optional": true } } }, - "node_modules/unplugin/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/vitepress/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { "node": ">=12" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", - "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", - "tinyglobby": "^0.2.17" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitepress/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -7561,33 +10152,23 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { + "less": { "optional": true }, - "less": { + "lightningcss": { "optional": true }, "sass": { @@ -7604,64 +10185,9 @@ }, "terser": { "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-plugin-dts": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-5.0.2.tgz", - "integrity": "sha512-lNeHS+dwGju6eRmNvZQt8Shwv9j3m98hbHse/lIbLq9q3yE2DcIOBBYQEVUF6tS0kOmv+VA9Z5FqmzFnGe4U8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "unplugin-dts": "1.0.2" - }, - "peerDependencies": { - "@microsoft/api-extractor": ">=7", - "rollup": ">=3", - "vite": ">=3" - }, - "peerDependenciesMeta": { - "@microsoft/api-extractor": { - "optional": true - }, - "rollup": { - "optional": true - }, - "vite": { - "optional": true } } }, - "node_modules/vite-plugin-wasm": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.6.0.tgz", - "integrity": "sha512-mL/QPziiIA4RAA6DkaZZzOstdwbW5jO4Vz7Zenj0wieKWBlNvIvX5L5ljum9lcUX0ShNfBgCNLKTjNkRVVqcsw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vscode": { "name": "@codingame/monaco-vscode-extension-api", "version": "25.1.2", @@ -7753,6 +10279,28 @@ "node": ">=14.0.0" } }, + "node_modules/vue": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.35.tgz", + "integrity": "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-sfc": "3.5.35", + "@vue/runtime-dom": "3.5.35", + "@vue/server-renderer": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/webdriver-bidi-protocol": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.2.tgz", @@ -7783,6 +10331,16 @@ "node": ">= 8" } }, + "node_modules/wkt-parser": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.5.5.tgz", + "integrity": "sha512-/zMYi94/7D7fxcOSlVmWn6vnOMj3Gq5d1xvVjaYOS9n6h0qOJ4I7YYVxBWYcH1vq9+suhqzXkn05Yx47zQNUIA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ahocevar" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -8067,6 +10625,18 @@ "node": ">=8" } }, + "node_modules/yasgui-geo-tg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/yasgui-geo-tg/-/yasgui-geo-tg-1.1.2.tgz", + "integrity": "sha512-mP15sP5aG0jJJtnK5P7T4fEoN0sXZLH9YsnK3pKa1p9bqNaFwiL13wQNYwdGSN0GWuk5ZWQl2tfiK7gSkoaumg==", + "dev": true, + "license": "MIT", + "dependencies": { + "betterknown": "^1.1.1", + "leaflet": "^1.9.4", + "proj4": "^2.20.2" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -8090,6 +10660,17 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "packages/utils": { "name": "@zazuko/yasgui-utils", "version": "4.6.1", diff --git a/package.json b/package.json index de57ac86..849c52ae 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,10 @@ "unit-test": "tsc -p ./tsconfig-test.json && mocha $(find ./build/test -name '*-test.js') --require source-map-support/register || true", "puppeteer-test": "tsc -p ./tsconfig-test.json && mocha --timeout 30000 ./build/test/test/run.js --require source-map-support/register", "prerelease": "npm run build && npm run test", - "release": "changeset publish" + "release": "changeset publish", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs" }, "lint-staged": { "*.ts?(x)": [ @@ -36,6 +39,7 @@ "devDependencies": { "@changesets/cli": "^2.29.7", "@codingame/esbuild-import-meta-url-plugin": "^1.0.3", + "@matdata/yasgui-graph-plugin": "^1.6.2", "@types/chai": "^5.2.3", "@types/fs-extra": "^11.0.4", "@types/mocha": "^10.0.10", @@ -60,7 +64,9 @@ "typescript-eslint": "^8.60.1", "vite": "^8.0.16", "vite-plugin-dts": "^5.0.2", - "vite-plugin-wasm": "^3.4.1" + "vite-plugin-wasm": "^3.4.1", + "vitepress": "^1.6.4", + "yasgui-geo-tg": "^1.1.2" }, "prettier": { "printWidth": 120 diff --git a/packages/yasgui/src/darkmode.css b/packages/yasgui/src/darkmode.css index d85523a6..5e3c0df7 100644 --- a/packages/yasgui/src/darkmode.css +++ b/packages/yasgui/src/darkmode.css @@ -99,7 +99,12 @@ html[data-theme="dark"] { .clearEndpointBtn { background-color: var(--yg-d-border); border-color: var(--yg-d-border); - color: var(--yg-d-muted); + color: var(--yg-d-text); + } + .clearEndpointBtn:hover { + opacity: 1; + color: #ffffff; + background-color: var(--yg-d-hover); } .tabMenu { @@ -236,7 +241,12 @@ html[data-theme="dark"] { .clearEndpointBtn { background-color: var(--yg-d-border); border-color: var(--yg-d-border); - color: var(--yg-d-muted); + color: var(--yg-d-text); + } + .clearEndpointBtn:hover { + opacity: 1; + color: #ffffff; + background-color: var(--yg-d-hover); } .tabMenu { diff --git a/packages/yasgui/src/defaults.ts b/packages/yasgui/src/defaults.ts index 94e563dc..676e682e 100644 --- a/packages/yasgui/src/defaults.ts +++ b/packages/yasgui/src/defaults.ts @@ -34,7 +34,6 @@ export default function initialize(): Config { "https://sparql.dblp.org/sparql", "https://dbpedia.org/sparql", "https://query.wikidata.org/sparql", - "https://commons-query.wikimedia.org/sparql", "https://qlever.dev/api/wikidata", "https://qlever.dev/api/wikimedia-commons", "https://qlever.dev/api/osm-planet", diff --git a/packages/yasqe/src/defaults.ts b/packages/yasqe/src/defaults.ts index b833b1f6..3ae8422b 100644 --- a/packages/yasqe/src/defaults.ts +++ b/packages/yasqe/src/defaults.ts @@ -124,7 +124,7 @@ SELECT * WHERE { queryArgument: undefined, //undefined means: get query argument based on query mode endpoint: "https://dbpedia.org/sparql", method: "POST", - acceptHeaderGraph: "application/n-triples,*/*;q=0.9", + acceptHeaderGraph: "text/turtle,application/n-triples,application/rdf+xml,application/trig,application/n-quads", acceptHeaderSelect: "application/sparql-results+json,*/*;q=0.9", acceptHeaderUpdate: "text/plain,*/*;q=0.9", namedGraphs: [], diff --git a/packages/yasqe/src/editor/sparqlTheme.ts b/packages/yasqe/src/editor/sparqlTheme.ts index 0bd1f1a6..551874e5 100644 --- a/packages/yasqe/src/editor/sparqlTheme.ts +++ b/packages/yasqe/src/editor/sparqlTheme.ts @@ -16,53 +16,53 @@ export const sparqlThemeDark = { // colors mirror the TextMate scopes below so highlighting stays consistent semanticHighlighting: true, semanticTokenColors: { - keyword: "#859900", // green - matches keyword.control.sparql - function: "#cb4b16", // orange - matches keyword.operator.function.sparql - variable: "#268bd2", // blue - matches variable.other.sparql - string: "#b58900", // yellow - matches string - number: "#2aa198", // cyan - matches constant.numeric + keyword: "#6c71c4", // purple - matches keyword.control.sparql + function: "#2aa198", // cyan - matches keyword.operator.function.sparql (STRDT, CONCAT, ...) + variable: "#93a1a1", // base1 grey - matches variable.other.sparql + string: "#859900", // green - matches string + number: "#cb4b16", // orange - matches constant.numeric, alt: #FF5600 comment: "#586e75", // base01 - solarized - operator: "#93a1a1", // base1 - matches keyword.symbol - namespace: "#FF5600", // orange - matches variable.prefix.sparql - // namespace: { foreground: "#FF5600", fontStyle: "italic bold" }, + operator: "#2aa198", // cyan - matches keyword.symbol (*, ...) + namespace: "#b58900", // gold - matches variable.prefix.sparql + // namespace: { foreground: "#b58900", fontStyle: "italic bold" }, }, // Used by TextMate grammars (fallback if no semantic colors provided by language server) tokenColors: [ { scope: "keyword.control.sparql", settings: { - foreground: "#859900", // green + foreground: "#6c71c4", // purple // fontStyle: "bold italic", }, }, { scope: "keyword.operator.function.sparql", settings: { - foreground: "#cb4b16", // orange + foreground: "#2aa198", // cyan }, }, { scope: "keyword.operator.prefixdecl.sparql", settings: { - foreground: "#859900", // green + foreground: "#6c71c4", // purple }, }, { scope: "variable.prefix.sparql", settings: { - foreground: "#FF5600", // orange + foreground: "#b58900", // gold }, }, { scope: "variable.reference.sparql", settings: { - foreground: "#FF5600", // orange + foreground: "#b58900", // gold }, }, { scope: "variable.other.sparql", settings: { - foreground: "#268bd2", // blue + foreground: "#93a1a1", // base1 grey }, }, { @@ -74,19 +74,19 @@ export const sparqlThemeDark = { { scope: "constant.numeric", settings: { - foreground: "#2aa198", // cyan + foreground: "#cb4b16", // orange }, }, { scope: "string", settings: { - foreground: "#b58900", // yellow + foreground: "#859900", // green }, }, { scope: "keyword.symbol", settings: { - foreground: "#93a1a1", // light gray + foreground: "#2aa198", // cyan }, }, ], @@ -177,7 +177,7 @@ export const sparqlThemeLight = { { scope: "keyword.symbol", settings: { - foreground: "#000", + foreground: "#000000", }, }, ], From 0eee2f56465483c239492171fbc6296eceb78a59 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Tue, 9 Jun 2026 14:24:18 +0200 Subject: [PATCH 04/48] feat!: Drop UMD support, now only builds Yasgui for ESM-only (which is fine, now ESM imports are supported in plain HTML file ` + + diff --git a/dev/yasqe.html b/dev/yasqe.html index 36ef3d02..fbc3aaf8 100644 --- a/dev/yasqe.html +++ b/dev/yasqe.html @@ -2,8 +2,8 @@ + - YASQE diff --git a/dev/yasr.html b/dev/yasr.html index 6e7a09bc..91291ad1 100644 --- a/dev/yasr.html +++ b/dev/yasr.html @@ -2,8 +2,8 @@ + - YASR diff --git a/docs/docs/plugins.md b/docs/docs/plugins.md index b0517831..b8514028 100644 --- a/docs/docs/plugins.md +++ b/docs/docs/plugins.md @@ -37,7 +37,25 @@ SELECT * WHERE { ?s ?p ?o } LIMIT 10 [`yasgui-geo-tg`](https://github.com/Thib-G/yasgui-geo-tg) ::: -Displays geographic results on an interactive Leaflet map. It reads WKT (`geo:wktLiteral`), GeoJSON, GML and GeoHash literals, and also auto-detects `?lat` / `?lon` numeric columns without WKT. Includes multiple basemaps, drawing tools that generate spatial SPARQL filters, a time slider for temporal data, and export to GeoJSON, KML, CSV and PNG. +Displays geographic results on an interactive [Leaflet](https://leafletjs.com/) map. It reads WKT (`geo:wktLiteral`), GeoJSON, GML and GeoHash literals, and also auto-detects `?lat` / `?lon` numeric columns without WKT. Includes multiple basemaps, drawing tools that generate spatial SPARQL filters, a time slider for temporal data, and export to GeoJSON, KML, CSV and PNG. + +Integrate it: + +```ts +import Yasgui from '@rdfjs/yasgui'; +import GeoPlugin from 'yasgui-geo-tg'; + +Yasgui.Yasr.registerPlugin('geo', GeoPlugin); + +const yasgui = new Yasgui(document.getElementById('yasgui'), { + yasr: { + pluginOrder: ['table', 'response', 'geo'], + defaultPlugin: 'geo', + }, +}); +``` + +Examples queries: ```sparql PREFIX geo: @@ -69,6 +87,23 @@ SELECT ?feature ?wkt WHERE { Renders `CONSTRUCT` / `DESCRIBE` results as an interactive node-edge graph with a force-directed layout. URIs, literals and blank nodes are color-coded, double-click a node to expand it with a `DESCRIBE` query, and use compact mode to hide literals and class nodes. +Integrate it: + +```ts +import Yasgui from '@rdfjs/yasgui'; +import GraphPlugin from '@matdata/yasgui-graph-plugin'; + +Yasgui.Yasr.registerPlugin('graph', GraphPlugin); + +const yasgui = new Yasgui(document.getElementById('yasgui'), { + yasr: { + pluginOrder: ['table', 'response', 'graph'], + }, +}); +``` + +Example query: + ```sparql PREFIX ex: CONSTRUCT { diff --git a/vite.config.ts b/vite.config.ts index 17020e03..154d70ad 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -99,6 +99,7 @@ export default defineConfig({ index: resolve(__dirname, "dev/index.html"), yasqe: resolve(__dirname, "dev/yasqe.html"), yasr: resolve(__dirname, "dev/yasr.html"), + swls: resolve(__dirname, "dev/swls.html"), }, }, }, From 6241fbb9d91b9332eaf029ab08e8f1ca6c65b364 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Wed, 10 Jun 2026 09:44:51 +0200 Subject: [PATCH 11/48] doc: update yasr plugins `helpReference` links (previous one were dead) --- docs/docs/yasgui.md | 19 +------------------ packages/yasr/src/plugins/response/index.ts | 2 +- packages/yasr/src/plugins/table/index.ts | 2 +- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/docs/docs/yasgui.md b/docs/docs/yasgui.md index 63447358..f4d1b64d 100644 --- a/docs/docs/yasgui.md +++ b/docs/docs/yasgui.md @@ -16,14 +16,7 @@ const yasgui = new Yasgui(document.getElementById("yasgui")!, { }); ``` -## One shared editor - -Yasgui uses **one shared Monaco editor** across all tabs (Monaco can only be initialized once per -page) and swaps its content and endpoint on tab switch. - -- `yasgui.yasqe` is that shared [Yasqe](./yasqe) instance. -- `yasgui.yasqe.getLanguageClient()` returns the underlying `monaco-languageclient` so you can send - any LSP request. +`yasgui.yasqe.getLanguageClient()` returns the underlying `monaco-languageclient` so you can send any LSP request. ## Configuration @@ -37,16 +30,6 @@ page) and swaps its content and endpoint on tab switch. | `corsProxy` | `string` | optional CORS proxy URL | | `persistenceId` | `string \| fn \| null` | localStorage namespace; `null` disables persistence | -## The endpoint selector - -Yasgui ships with a catalogue of popular public SPARQL endpoints in the endpoint dropdown, -including DBpedia, Wikidata, UniProt, the QLever services and many life-science datasets. Users can -type any other endpoint URL directly. - -When the active endpoint changes (on load, tab switch, or when the user edits the endpoint), -`onEndpointChange` fires once for the whole app. Use it to point the language server at the active -endpoint, as shown above. - ## CORS Public endpoints usually send the right CORS headers. For endpoints that don't, set a `corsProxy`: diff --git a/packages/yasr/src/plugins/response/index.ts b/packages/yasr/src/plugins/response/index.ts index 840cd6fa..66740a39 100644 --- a/packages/yasr/src/plugins/response/index.ts +++ b/packages/yasr/src/plugins/response/index.ts @@ -32,7 +32,7 @@ export default class Response implements Plugin { private yasr: Yasr; label = "Response"; priority = 2; - helpReference = "https://rdfjs.github.io/Yasgui/docs/introduction"; + helpReference = "https://vemonet.github.io/Yasgui/docs/plugins#response"; private config: DeepReadonly; private overLay: HTMLDivElement | undefined; private cm: EditorView | undefined; diff --git a/packages/yasr/src/plugins/table/index.ts b/packages/yasr/src/plugins/table/index.ts index a8181b0a..9bf494d0 100644 --- a/packages/yasr/src/plugins/table/index.ts +++ b/packages/yasr/src/plugins/table/index.ts @@ -60,7 +60,7 @@ export default class Table implements Plugin { onResize: () => {}; } | undefined; - public helpReference = "https://docs.triply.cc/yasgui/#table"; + public helpReference = "https://vemonet.github.io/Yasgui/docs/plugins#table"; public label = "Table"; public priority = 10; public getIcon() { From e614fb24dd95ae13a4ceee76fdca7b4f5d0252d9 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Wed, 10 Jun 2026 11:16:44 +0200 Subject: [PATCH 12/48] refactor: move from using monaco editor in extended mode, to use it in classic mode - no textmate, VSCode extension host, oniguruma - Bundle size goes from 15MB to 11MB - disable large sourcemaps in packages build --- packages/yasqe/src/editor/editorConfig.ts | 110 +++--- packages/yasqe/src/editor/sparqlGrammar.ts | 393 --------------------- packages/yasqe/src/editor/sparqlMonarch.ts | 233 ++++++++++++ packages/yasqe/src/index.ts | 9 +- vite.config.ts | 29 +- 5 files changed, 312 insertions(+), 462 deletions(-) delete mode 100644 packages/yasqe/src/editor/sparqlGrammar.ts create mode 100644 packages/yasqe/src/editor/sparqlMonarch.ts diff --git a/packages/yasqe/src/editor/editorConfig.ts b/packages/yasqe/src/editor/editorConfig.ts index eaabfc32..0ec246d9 100644 --- a/packages/yasqe/src/editor/editorConfig.ts +++ b/packages/yasqe/src/editor/editorConfig.ts @@ -1,23 +1,35 @@ /** * Monaco Editor setup with SPARQL syntax highlighting. * + * Uses the monaco-languageclient "classic" configuration: no VSCode extension host, no TextMate + * engine and no oniguruma wasm. The SPARQL language, a Monarch tokenizer (fallback highlighting) + * and the light/dark themes are registered directly through the standalone Monaco API. The + * authoritative coloring comes from qlue-ls LSP *semantic tokens*; the Monarch grammar only + * provides highlighting before the language server responds. + * * This module is language-server agnostic: it does NOT create or know about any specific * language server. A ready-to-use LSP `Worker` can be injected by the caller; when provided, * a monaco-languageclient is wired to it (giving completions, diagnostics, formatting, semantic * tokens, etc. — whatever that server supports). When omitted, the editor still works with - * TextMate-based syntax highlighting only. + * Monarch-based syntax highlighting only. */ import { configureDefaultWorkerFactory } from "monaco-languageclient/workerFactory"; import { type EditorAppConfig, EditorApp } from "monaco-languageclient/editorApp"; import { type MonacoVscodeApiConfig, MonacoVscodeApiWrapper } from "monaco-languageclient/vscodeApiWrapper"; import { type LanguageClientConfig, LanguageClientWrapper } from "monaco-languageclient/lcwrapper"; -import { Uri } from "monaco-editor"; +import { Uri, editor, languages } from "monaco-editor"; import { merge } from "lodash-es"; -// Import SPARQL theme and grammar for syntax highlighting +// SPARQL themes (Monaco standalone theme data is derived from these) and classic-mode grammar import { sparqlThemeDark, sparqlThemeLight } from "./sparqlTheme"; -import { sparqlTextmateGrammar, sparqlLanguageConfig } from "./sparqlGrammar"; +import { sparqlMonarchLanguage, sparqlLanguageConfiguration, buildSparqlThemeData } from "./sparqlMonarch"; + +/** Monaco standalone theme names registered for the SPARQL editor. */ +export const SPARQL_THEME_LIGHT = "sparql-light"; +export const SPARQL_THEME_DARK = "sparql-dark"; + +const LANGUAGE_ID = "sparql"; export interface MonacoEditorResult { apiWrapper: MonacoVscodeApiWrapper; @@ -53,28 +65,21 @@ export async function startMonacoEditor( // Built-in themes with any consumer overrides deep-merged on top const lightTheme = merge({}, sparqlThemeLight, themeOverrides?.light ?? {}); const darkTheme = merge({}, sparqlThemeDark, themeOverrides?.dark ?? {}); + const initialThemeName = theme === "dark" ? SPARQL_THEME_DARK : SPARQL_THEME_LIGHT; - // Extension files for SPARQL language support - const extensionFilesOrContents = new Map(); - extensionFilesOrContents.set("/sparql-configuration.json", JSON.stringify(sparqlLanguageConfig)); - extensionFilesOrContents.set("/sparql-grammar.json", JSON.stringify(sparqlTextmateGrammar)); - extensionFilesOrContents.set("/sparql-theme-light.json", JSON.stringify(lightTheme)); - extensionFilesOrContents.set("/sparql-theme-dark.json", JSON.stringify(darkTheme)); - - // MonacoVscodeApiConfig + // Classic monaco-vscode api config: no extension host, no TextMate, no theme service. const vscodeApiConfig: MonacoVscodeApiConfig = { - $type: "extended", + $type: "classic", viewsConfig: { $type: "EditorService", }, userConfiguration: { json: JSON.stringify({ - "workbench.colorTheme": theme === "dark" ? "SPARQL Dark Theme" : "SPARQL Light Theme", "editor.guides.bracketPairsHorizontal": "active", "editor.lightbulb.enabled": "On", "editor.wordBasedSuggestions": "off", "editor.experimental.asyncTokenization": true, - // Use language-server semantic tokens (parser-based) on top of the TextMate grammar + // Use language-server semantic tokens (parser-based) on top of the Monarch fallback grammar "editor.semanticHighlighting.enabled": true, "editor.tabSize": 2, "editor.insertSpaces": true, @@ -85,48 +90,12 @@ export async function startMonacoEditor( }), }, monacoWorkerFactory: configureDefaultWorkerFactory, - extensions: [ - { - config: { - name: "sparql-language", - publisher: "Ioannis Nezis", - version: "1.0.0", - engines: { vscode: "*" }, - contributes: { - languages: [ - { - id: "sparql", - extensions: [".rq", ".sparql"], - aliases: ["sparql", "SPARQL"], - configuration: "/sparql-configuration.json", - }, - ], - themes: [ - { - id: "SPARQL Light Theme", - label: "SPARQL Light Theme", - uiTheme: "vs", - path: "./sparql-theme-light.json", - }, - { - id: "SPARQL Dark Theme", - label: "SPARQL Dark Theme", - uiTheme: "vs-dark", - path: "./sparql-theme-dark.json", - }, - ], - grammars: [ - { - language: "sparql", - scopeName: "source.sparql", - path: "/sparql-grammar.json", - }, - ], - }, - }, - filesOrContents: extensionFilesOrContents, - }, - ], + // Skip the extension-host services entirely (classic mode registers language/grammar/theme + // through the standalone API), which drops the extensionHost worker + extensions service. + advanced: { + loadExtensionServices: false, + // enforceSemanticHighlighting: true, + }, }; // Create and start the monaco-vscode api wrapper @@ -137,9 +106,9 @@ export async function startMonacoEditor( let lcWrapper: LanguageClientWrapper | undefined; if (lsWorker) { const languageClientConfig: LanguageClientConfig = { - languageId: "sparql", + languageId: LANGUAGE_ID, clientOptions: { - documentSelector: [{ language: "sparql" }], + documentSelector: [{ language: LANGUAGE_ID }], workspaceFolder: { index: 0, name: "workspace", @@ -215,7 +184,7 @@ export async function startMonacoEditor( selectionHighlight: true, // was highlightSelectionMatches: { showToken: /\w/ } } as const; - // EditorAppConfig + // EditorAppConfig: classic mode registers the SPARQL language + Monarch grammar + initial theme const editorAppConfig: EditorAppConfig = { codeResources: { modified: { @@ -224,12 +193,31 @@ export async function startMonacoEditor( }, }, editorOptions: merge({}, defaultEditorOptions, editorOptions ?? {}), + languageDef: { + languageExtensionConfig: { + id: LANGUAGE_ID, + extensions: [".rq", ".sparql"], + aliases: ["SPARQL", "sparql"], + }, + monarchLanguage: sparqlMonarchLanguage, + theme: { + name: initialThemeName, + data: buildSparqlThemeData(theme === "dark" ? darkTheme : lightTheme), + }, + }, }; - // Create and start the editor app + // Create and start the editor app (registers language, Monarch grammar and the initial theme) const editorApp = new EditorApp(editorAppConfig); await editorApp.start(container); + // Register both themes + the language configuration (brackets/comments/auto-close) so theme + // switching at runtime works and bracket matching/auto-closing behave correctly. + editor.defineTheme(SPARQL_THEME_LIGHT, buildSparqlThemeData(lightTheme)); + editor.defineTheme(SPARQL_THEME_DARK, buildSparqlThemeData(darkTheme)); + editor.setTheme(initialThemeName); + languages.setLanguageConfiguration(LANGUAGE_ID, sparqlLanguageConfiguration); + return { apiWrapper, editorApp, diff --git a/packages/yasqe/src/editor/sparqlGrammar.ts b/packages/yasqe/src/editor/sparqlGrammar.ts deleted file mode 100644 index 8b04d09c..00000000 --- a/packages/yasqe/src/editor/sparqlGrammar.ts +++ /dev/null @@ -1,393 +0,0 @@ -export const sparqlLanguageConfig = { - comments: { - lineComment: "#", - }, - brackets: [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ], - autoClosingPairs: [ - { - open: "{", - close: "}", - }, - { - open: "[", - close: "]", - }, - { - open: "(", - close: ")", - }, - { - open: "'", - close: "'", - notIn: ["string", "comment"], - }, - { - open: '"', - close: '"', - notIn: ["string"], - }, - { - open: "/**", - close: " */", - notIn: ["string"], - }, - ], - autoCloseBefore: "}])`\n\t", - surroundingPairs: [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ], - colorizedBracketPairs: [ - ["(", ")"], - ["[", "]"], - ["{", "}"], - ["'", "'"], - ['"', '"'], - ], -}; - -export const sparqlTextmateGrammar = { - name: "SPARQL", - scopeName: "source.sparql", - fileTypes: ["sparql"], - patterns: [ - { - include: "#regex", - }, - { - include: "#comments", - }, - { - name: "keyword.operator.prefixdecl.sparql", - match: "\\b(?i:PREFIX)\\b", - }, - { - name: "keyword.operator.function.sparql", - match: - "\\b(?i:FILTER|BIND|MAX|SAMPLE|LANG|STR|RAND|ABS|CEIL|FLOOR|ROUND|CONCAT|STRLEN|UCASE|LCASE|ENCODE_FOR_URI|CONTAINS|STRSTARTS|STRENDS|STRBEFORE|STRAFTER|YEAR|MONTH|DAY|HOURS|MINUTES|SECONDS|TIMEZONE|TZ|NOW|UUID|STRUUID|MD5|SHA1|SHA256|SHA384|SHA512|COALESCE|IF|STRLANG|STRDT|sameTerm|isIRI|isURI|isBLANK|isLITERAL|isNUMERIC|COUNT|SUM|MIN|MAX|AVG|GROUP_CONCAT|SEPARATOR|SUBSTR|REGEX|EXISTS|IN|NOT)\\b", - }, - { - name: "keyword.control.sparql", - match: - "\\b(?i:BASE|SELECT|DISTINCT|REDUCED|FROM|NAMED|WHERE|UNION|OPTIONAL|MINUS|GRAPH|SERVICE|SILENT|VALUES|AS|GROUP|BY|HAVING|ORDER|DESC|ASC|LIMIT|OFFSET|CONSTRUCT|DESCRIBE|ASK|LOAD|INTO|CLEAR|ALL|NAMED|DEFAULT|DROP|ADD|TO|MOVE|COPY|WITH|USING|CREATE)\\b", - }, - { - name: "constant.language.sparql", - match: "\\b(?i:true|false)\\b", - }, - { - name: "constant.numeric", - match: "\\b\\d+(\\.\\d+([eE][\\-+]?\\d+)?)?\\b", - }, - { - name: "constant.other.iri.sparql", - match: '<([^<>"{}|^`\\\u0000-\u0020])*>', - }, - { - name: "variable.other.sparql", - match: "\\?\\w*\\b", - }, - { - name: "variable.curie.sparql", - match: - "\\b([a-zA-Z\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF](?:(?:[A-Za-z0-9-\\u00B7\\u0300-\\u036F\\u203F-\\u2040\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF]|\\.)*)[A-Za-z0-9-\\u00B7\\u0300-\\u036F\\u203F-\\u2040\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF]):((?:[a-zA-Z\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF_]|[0-9]|:|(?:\\u005c)[_~\\.\\-!$&'\\(\\|\\)\\*\\+,;=\\/?#@%])((?:[A-Za-z0-9-\\u00B7\\u0300-\\u036F\\u203F-\\u2040\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF]|\\.|:)*(?:[A-Za-z0-9-\\u00B7\\u0300-\\u036F\\u203F-\\u2040\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF]|:))?)\\b", - captures: { - "1": { - name: "variable.prefix.sparql", - }, - "2": { - name: "variable.reference.sparql", - }, - }, - }, - { - name: "variable.prefix.sparql", - match: - "\\b[a-zA-Z\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF](?:(?:[A-Za-z0-9-\\u00B7\\u0300-\\u036F\\u203F-\\u2040\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF]|\\.)*)[A-Za-z0-9-\\u00B7\\u0300-\\u036F\\u203F-\\u2040\\u00c0-\\u00d6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD\\u10000-\\uEFFFF]:", - }, - { - name: "keyword.symbol.sparql", - match: "(\\{|\\}|\\:|\\]|\\[|\\(|\\)|>\\=|<\\=|\\!\\=|<|>|\\=|\\*|\\+|/|\\|\\||\\&\\&|\\!|\\;|\\.)", - }, - { - name: "string.quoted.double.sparql", - begin: '"', - end: '"', - patterns: [ - { - include: "#string-character-escape", - }, - ], - }, - { - name: "string.quoted.single.sparql", - begin: "'", - end: "'", - patterns: [ - { - include: "#string-character-escape", - }, - ], - }, - ], - repository: { - iri: { - patterns: [ - { - name: "comment.block.sparql", - match: "iri", - }, - ], - }, - comments: { - patterns: [ - { - begin: "(^\\s+)?#", - beginCaptures: { - "1": { - name: "punctuation.whitespace.comment.leading.cs", - }, - }, - end: "$", - name: "comment.line.sparql", - }, - ], - }, - "string-character-escape": { - name: "constant.character.escape.sparql", - match: - "\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|u\\{[0-9A-Fa-f]+\\}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.|$)", - }, - regex: { - patterns: [ - { - name: "string.regexp.ts", - begin: - "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([a-z]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", - beginCaptures: { - "1": { - name: "punctuation.definition.string.begin.ts", - }, - }, - end: "(/)([a-z]*)", - endCaptures: { - "1": { - name: "punctuation.definition.string.end.ts", - }, - "2": { - name: "keyword.other.ts", - }, - }, - patterns: [ - { - include: "#regexp", - }, - ], - }, - { - name: "string.regexp.ts", - begin: - "((?", - captures: { - "0": { - name: "keyword.other.back-reference.regexp", - }, - "1": { - name: "variable.other.regexp", - }, - }, - }, - { - name: "keyword.operator.quantifier.regexp", - match: "[?+*]|\\{(\\d+,\\d+|\\d+,|,\\d+|\\d+)\\}\\??", - }, - { - name: "keyword.operator.or.regexp", - match: "\\|", - }, - { - name: "meta.group.assertion.regexp", - begin: "(\\()((\\?=)|(\\?!)|(\\?<=)|(\\?))?", - beginCaptures: { - "0": { - name: "punctuation.definition.group.regexp", - }, - "1": { - name: "punctuation.definition.group.no-capture.regexp", - }, - "2": { - name: "variable.other.regexp", - }, - }, - end: "\\)", - endCaptures: { - "0": { - name: "punctuation.definition.group.regexp", - }, - }, - patterns: [ - { - include: "#regexp", - }, - ], - }, - { - name: "constant.other.character-class.set.regexp", - begin: "(\\[)(\\^)?", - beginCaptures: { - "1": { - name: "punctuation.definition.character-class.regexp", - }, - "2": { - name: "keyword.operator.negation.regexp", - }, - }, - end: "(\\])", - endCaptures: { - "1": { - name: "punctuation.definition.character-class.regexp", - }, - }, - patterns: [ - { - name: "constant.other.character-class.range.regexp", - match: - "(?:.|(\\\\(?:[0-7]{3}|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}))|(\\\\c[A-Z])|(\\\\.))\\-(?:[^\\]\\\\]|(\\\\(?:[0-7]{3}|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}))|(\\\\c[A-Z])|(\\\\.))", - captures: { - "1": { - name: "constant.character.numeric.regexp", - }, - "2": { - name: "constant.character.control.regexp", - }, - "3": { - name: "constant.character.escape.backslash.regexp", - }, - "4": { - name: "constant.character.numeric.regexp", - }, - "5": { - name: "constant.character.control.regexp", - }, - "6": { - name: "constant.character.escape.backslash.regexp", - }, - }, - }, - { - include: "#regex-character-class", - }, - ], - }, - { - include: "#regex-character-class", - }, - ], - }, - "regex-character-class": { - patterns: [ - { - name: "constant.other.character-class.regexp", - match: "\\\\[wWsSdDtrnvf]|\\.", - }, - { - name: "constant.character.numeric.regexp", - match: "\\\\([0-7]{3}|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4})", - }, - { - name: "constant.character.control.regexp", - match: "\\\\c[A-Z]", - }, - { - name: "constant.character.escape.backslash.regexp", - match: "\\\\.", - }, - ], - }, - }, -}; - -export const sparqlTextmateTest = { - scopeName: "source.myLanguage", - patterns: [ - { - name: "keyword.control.myLanguage", - match: "\\b(if|else|for|while)\\b", - }, - { - name: "string.quoted.double.myLanguage", - begin: '"', - end: '"', - }, - ], -}; diff --git a/packages/yasqe/src/editor/sparqlMonarch.ts b/packages/yasqe/src/editor/sparqlMonarch.ts new file mode 100644 index 00000000..b24a6fda --- /dev/null +++ b/packages/yasqe/src/editor/sparqlMonarch.ts @@ -0,0 +1,233 @@ +/** + * Classic-mode SPARQL highlighting (no VSCode extension host / TextMate / oniguruma). + * + * A Monarch tokenizer provides the *fallback* syntax highlighting shown before the language + * server responds. The authoritative coloring still comes from qlue-ls LSP semantic tokens. + * Both are driven by the SAME theme `rules` table: Monarch emits token names that match the + * standard LSP semantic token types (keyword, function, variable, string, number, comment, + * operator, namespace), so one set of rules colors both. + */ + +import type * as monaco from "monaco-editor"; + +/** Control/structural SPARQL keywords (case-insensitive). */ +const SPARQL_KEYWORDS = [ + "BASE", + "PREFIX", + "SELECT", + "DISTINCT", + "REDUCED", + "FROM", + "NAMED", + "WHERE", + "UNION", + "OPTIONAL", + "MINUS", + "GRAPH", + "SERVICE", + "SILENT", + "VALUES", + "AS", + "GROUP", + "BY", + "HAVING", + "ORDER", + "DESC", + "ASC", + "LIMIT", + "OFFSET", + "CONSTRUCT", + "DESCRIBE", + "ASK", + "LOAD", + "INTO", + "CLEAR", + "ALL", + "DEFAULT", + "DROP", + "ADD", + "TO", + "MOVE", + "COPY", + "WITH", + "USING", + "CREATE", + "INSERT", + "DELETE", + "DATA", + "true", + "false", + "a", +]; + +/** SPARQL built-in functions / aggregates (case-insensitive). */ +const SPARQL_FUNCTIONS = [ + "FILTER", + "BIND", + "MAX", + "SAMPLE", + "LANG", + "STR", + "RAND", + "ABS", + "CEIL", + "FLOOR", + "ROUND", + "CONCAT", + "STRLEN", + "UCASE", + "LCASE", + "ENCODE_FOR_URI", + "CONTAINS", + "STRSTARTS", + "STRENDS", + "STRBEFORE", + "STRAFTER", + "YEAR", + "MONTH", + "DAY", + "HOURS", + "MINUTES", + "SECONDS", + "TIMEZONE", + "TZ", + "NOW", + "UUID", + "STRUUID", + "MD5", + "SHA1", + "SHA256", + "SHA384", + "SHA512", + "COALESCE", + "IF", + "STRLANG", + "STRDT", + "sameTerm", + "isIRI", + "isURI", + "isBLANK", + "isLITERAL", + "isNUMERIC", + "COUNT", + "SUM", + "MIN", + "AVG", + "GROUP_CONCAT", + "SEPARATOR", + "SUBSTR", + "REGEX", + "EXISTS", + "IN", + "NOT", + "BOUND", +]; + +/** Monarch language definition used for the pre-LSP fallback tokenization. */ +export const sparqlMonarchLanguage: monaco.languages.IMonarchLanguage = { + ignoreCase: true, + defaultToken: "", + keywords: SPARQL_KEYWORDS, + functions: SPARQL_FUNCTIONS, + tokenizer: { + root: [ + // Comments + [/#.*$/, "comment"], + // Variables: ?var or $var + [/[?$]\w+/, "variable"], + // IRIs: <...> + [/<[^<>"{}|^`\s]*>/, "operator"], + // Prefixed-name prefix part (incl. trailing colon): foaf: / rdf: + [/[A-Za-z_][\w.\-]*:/, "namespace"], + // Numbers + [/\d+(\.\d+([eE][\-+]?\d+)?)?/, "number"], + // Strings (double / single quoted, with escapes) + [/"([^"\\]|\\.)*"/, "string"], + [/'([^'\\]|\\.)*'/, "string"], + // Identifiers -> keyword / function / plain + [ + /[a-zA-Z_]\w*/, + { + cases: { + "@keywords": "keyword", + "@functions": "function", + "@default": "identifier", + }, + }, + ], + // Brackets / punctuation / operators + [/[{}()\[\].;,]/, "operator"], + [/[*+\/<>=!&|^~-]+/, "operator"], + ], + }, +}; + +/** Brackets, comments and auto-closing pairs (classic equivalent of the language configuration). */ +export const sparqlLanguageConfiguration: monaco.languages.LanguageConfiguration = { + comments: { lineComment: "#" }, + brackets: [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ], + autoClosingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: "'", close: "'", notIn: ["string", "comment"] }, + { open: '"', close: '"', notIn: ["string"] }, + ], + surroundingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: "'", close: "'" }, + { open: '"', close: '"' }, + ], +}; + +/** Source theme shape (subset of a VSCode theme) used to derive a standalone Monaco theme. */ +interface SparqlThemeSource { + type: string; + colors: Record; + semanticTokenColors: Record; +} + +/** + * Normalize a hex color to the 6/8-digit form (no leading `#`) that Monaco's standalone + * `defineTheme` requires for token-color rules. Monaco's strict token-color parser rejects the + * CSS shorthand (e.g. `#219`), so expand 3/4-digit shorthand to its full form. + */ +function normalizeHex(color: string): string { + const h = color.replace(/^#/, ""); + if (h.length === 3 || h.length === 4) { + return h + .split("") + .map((ch) => ch + ch) + .join(""); + } + return h; +} + +/** + * Build a standalone Monaco theme (IStandaloneThemeData) from one of the SPARQL theme objects. + * The `rules` are keyed by the LSP semantic token type names, which also match the Monarch + * token names emitted above, so a single table colors both the fallback and the LSP tokens. + */ +export function buildSparqlThemeData(theme: SparqlThemeSource): monaco.editor.IStandaloneThemeData { + const rules: monaco.editor.ITokenThemeRule[] = []; + for (const [token, value] of Object.entries(theme.semanticTokenColors)) { + const fg = typeof value === "string" ? value : value.foreground; + if (!fg) continue; + const rule: monaco.editor.ITokenThemeRule = { token, foreground: normalizeHex(fg) }; + const fontStyle = typeof value === "object" ? value.fontStyle : undefined; + if (fontStyle) rule.fontStyle = fontStyle; + rules.push(rule); + } + return { + base: theme.type === "dark" ? "vs-dark" : "vs", + inherit: true, + colors: theme.colors, + rules, + }; +} diff --git a/packages/yasqe/src/index.ts b/packages/yasqe/src/index.ts index 564d703e..68a1e8a9 100644 --- a/packages/yasqe/src/index.ts +++ b/packages/yasqe/src/index.ts @@ -313,12 +313,11 @@ export class Yasqe extends EventEmitter { */ public async setTheme(theme: "light" | "dark"): Promise { document.documentElement.dataset.theme = theme; - // Use the theme names matching the extensions defined in editorConfig.ts - const themeName = theme === "dark" ? "SPARQL Dark Theme" : "SPARQL Light Theme"; - // Use VS Code API to change the theme at runtime (monaco-languageclient approach) + // Classic mode: switch theme via the standalone Monaco API (themes registered in editorConfig.ts) try { - const vscode = await import("vscode"); - await vscode.workspace.getConfiguration("workbench").update("colorTheme", themeName, true); + const { SPARQL_THEME_DARK, SPARQL_THEME_LIGHT } = await import("./editor/editorConfig"); + const monaco = await import("monaco-editor"); + monaco.editor.setTheme(theme === "dark" ? SPARQL_THEME_DARK : SPARQL_THEME_LIGHT); } catch (error) { console.error("Failed to switch theme:", error); } diff --git a/vite.config.ts b/vite.config.ts index 154d70ad..cac2cfe0 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -16,6 +16,29 @@ const alias = [ { find: /^@zazuko\/yasgui-utils$/, replacement: resolve(__dirname, "packages/utils/src/index.ts") }, ]; +// The editor runs monaco-languageclient in `classic` mode so these `extended` service overrides are not used at runtime +// But codeSplitting:false would still inline every `(await import(pkg)).default` in the bundle (~5 MB of dead code) +const STUBBED_MONACO_SERVICES = new Set([ + "@codingame/monaco-vscode-textmate-service-override", + "@codingame/monaco-vscode-theme-service-override", + "@codingame/monaco-vscode-languages-service-override", + "@codingame/monaco-vscode-views-service-override", + "@codingame/monaco-vscode-workbench-service-override", + "@codingame/monaco-vscode-theme-defaults-default-extension", +]); +const STUB_VIRTUAL_ID = "\0monaco-service-stub"; +const monacoServiceStubPlugin = { + name: "monaco-service-stub", + enforce: "pre" as const, + // Exact match only: subpath imports like ".../worker" must resolve normally, not to the stub. + resolveId(id: string) { + return STUBBED_MONACO_SERVICES.has(id) ? STUB_VIRTUAL_ID : null; + }, + load(id: string) { + return id === STUB_VIRTUAL_ID ? "export default () => ({});" : null; + }, +}; + // Monaco (the @codingame/monaco-vscode-* packages) is ESM-only and loads its workers/wasm via // `new URL(..., import.meta.url)`. yasqe pulls it in, so any package depending on yasqe needs the // wasm plugin, ES-format workers and the import.meta.url esbuild rewrite (dev) to resolve those assets. @@ -34,7 +57,7 @@ export default defineConfig({ rolldownOptions: usesMonaco ? { plugins: [importMetaUrlPlugin] } : undefined, }, worker: { - // Monaco + qlue-ls language-server workers are ES modules and load wasm + // Monaco and qlue-ls language-server workers are ES modules and load wasm format: "es", plugins: () => [wasm()], // Emit each worker as 1 self-contained file. @@ -42,7 +65,7 @@ export default defineConfig({ rolldownOptions: { output: { codeSplitting: false } }, }, plugins: [ - ...(usesMonaco ? [wasm()] : []), + ...(usesMonaco ? [wasm(), monacoServiceStubPlugin] : []), ...(libPackage ? [ dts({ @@ -63,7 +86,7 @@ export default defineConfig({ copyPublicDir: false, // Monaco/qlue-ls need esnext (top-level await in the wasm glue), other packages keep es2020 target: usesMonaco ? "esnext" : "es2020", - sourcemap: true, + sourcemap: false, cssCodeSplit: false, // esbuild is more lenient than the default lightningcss minifier (CSS uses nesting) cssMinify: "esbuild", From 9d57ef2aa4130d34e97566ad7f74d08b55a4e343 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Wed, 10 Jun 2026 11:59:59 +0200 Subject: [PATCH 13/48] test: update some tests to work with the new monaco editor, make the vitepress index footer more visible --- docs/.vitepress/config.mts | 18 +--- docs/.vitepress/theme/demo.css | 45 ++++----- docs/index.md | 2 +- test/run.ts | 167 ++++++++++++++++++--------------- tsconfig-test.json | 1 + 5 files changed, 117 insertions(+), 116 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 501218cc..6f900654 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -6,7 +6,6 @@ import importMetaUrlPlugin from "@codingame/esbuild-import-meta-url-plugin"; export default defineConfig({ title: "Yasgui", description: "Yet Another SPARQL GUI · a Monaco-based SPARQL query editor and result viewer", - // Deployed under https://rdfjs.github.io/Yasgui/ base: "/Yasgui/", cleanUrls: true, lastUpdated: true, @@ -64,17 +63,13 @@ export default defineConfig({ text: "Edit this page on GitHub", }, footer: { - message: 'Documentation', + message: 'Documentation · Source code', copyright: "MIT License", }, }, vite: { - // The demo imports the @zazuko/* packages' pre-built, self-contained ESM bundles - // (build/yasgui.js, Monaco bundled in), exactly as a real consumer does. Run - // `npm run build:lib` before building/serving the docs so those bundles exist. - // We deliberately do NOT alias to TypeScript source: that pulls Monaco's deep - // @codingame/monaco-vscode-api/vscode/* imports into the build, which the older Vite - // that VitePress bundles cannot resolve. + // The demo imports the @zazuko/* packages' pre-built + // Run `npm run build:lib` before building/serving the docs so those bundles exist css: { preprocessorOptions: { scss: { api: "modern-compiler" }, @@ -88,15 +83,12 @@ export default defineConfig({ plugins: () => [wasm()], }, optimizeDeps: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any esbuildOptions: { plugins: [importMetaUrlPlugin as any] }, - // The pre-built editor bundles ship their own internal chunks/assets; let Vite serve - // them as-is rather than re-bundling the heavy Monaco code. + // The pre-built editor bundles ship their own internal chunks/assets exclude: ["@zazuko/yasgui", "@zazuko/yasqe", "@zazuko/yasr"], }, ssr: { - // The demo is client-only (mounted in onMounted, behind ), so the editor - // packages and their Monaco dependencies must never enter the server bundle. + // The demo is client-only, so the editor deps must not enter the server bundle external: [ "@zazuko/yasgui", "@zazuko/yasqe", diff --git a/docs/.vitepress/theme/demo.css b/docs/.vitepress/theme/demo.css index d78c8c17..ae47c4ae 100644 --- a/docs/.vitepress/theme/demo.css +++ b/docs/.vitepress/theme/demo.css @@ -1,3 +1,12 @@ +/* Sticky footer: page fills at least the viewport */ +.yasgui-home { + min-height: 100dvh; + display: flex; + flex-direction: column; +} +.yasgui-home .VPContent { + flex: 1 1 auto; +} .yasgui-home .VPPage { padding: 0; } @@ -9,42 +18,24 @@ height: auto; } -/* Make the demo a fixed-height viewport so the result area can flex. */ -.yasgui-demo, -.yasgui-demo__root { - height: calc(100vh - var(--vp-nav-height, 64px)); -} -.yasgui-demo .yasgui { - display: flex; - flex-direction: column; - height: 100%; -} -.yasgui-demo .yasgui > *:last-child, /* tabPanelsEl */ -.yasgui-demo .yasgui .tabPanel.active, -.yasgui-demo .yasgui .tabPanel.active > *:last-child, /* yasrWrapperEl */ -.yasgui-demo .yasgui .yasr { - flex: 1 1 auto; - min-height: 0; - display: flex; - flex-direction: column; -} +/* YASR and results: natural block height, no overflow clipping */ +.yasgui-demo .yasgui .yasr, .yasgui-demo .yasgui .yasr_results { - flex: 1 1 auto; - min-height: 0; + height: auto; + overflow: visible; } -/* The graph plugin hardcodes 500px; fill the leftover space, with a floor so it - stays usable when the editor is large. */ + +/* Graph plugin */ .yasgui-demo .yasgui-graph-plugin-container { - height: 100%; min-height: 30vh; } -/* Fix Geo plugin that was hidding footer */ +/* Geo plugin/leaflet: needs an explicit height to render */ .yasgui-demo .yasr_results .leaflet-container { - min-height: 0; + min-height: 300px; } -/* VitePress dark mode uses class="dark" on , while Yasgui's own dark theme keys off [data-theme="dark"]*/ +/* VitePress dark mode uses class="dark" on , while Yasgui's own dark theme keys off [data-theme="dark"] */ .dark .yasgui .clearEndpointBtn { background-color: var(--yg-d-border); border-color: var(--yg-d-border); diff --git a/docs/index.md b/docs/index.md index c04caf2b..7a3ccf9f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- layout: page -title: Yasgui · SPARQL query editor +# title: Yasgui · SPARQL query editor pageClass: yasgui-home sidebar: false aside: false diff --git a/test/run.ts b/test/run.ts index dad9c606..19a91ed0 100644 --- a/test/run.ts +++ b/test/run.ts @@ -42,6 +42,8 @@ describe("Yasqe", function () { beforeEach(async () => { page = await getPage(browser, "yasqe.html"); await page.evaluate(() => localStorage.clear()); + // Wait for the Monaco editor async init to finish (editor is created in initEditor() which is not awaited in the constructor) + await page.waitForFunction(() => !!(window as any).yasqe?.editor, { timeout: 15000 }); }); afterEach(async () => { @@ -60,16 +62,19 @@ describe("Yasqe", function () { }); async function waitForAutocompletionPopup(shouldNotHaveLength?: number): Promise { - if (shouldNotHaveLength) { + if (shouldNotHaveLength !== undefined) { await page.waitForFunction( - `document.querySelector('.CodeMirror-hints').children.length !== ${shouldNotHaveLength}`, - { timeout: 600 }, + (n: number) => document.querySelectorAll(".suggest-widget.visible .monaco-list-row").length !== n, + { timeout: 2000 }, + shouldNotHaveLength, ); } else { - await page.waitForSelector(`.CodeMirror-hints`, { timeout: 600 }); - await wait(20); + await page.waitForFunction(() => document.querySelector(".suggest-widget")?.classList.contains("visible"), { + timeout: 2000, + }); + await wait(50); } - return page.evaluate(() => document.querySelector(".CodeMirror-hints")?.children.length); + return page.evaluate(() => document.querySelectorAll(".suggest-widget.visible .monaco-list-row").length); } async function issueAutocompletionKeyCombination() { @@ -78,90 +83,103 @@ describe("Yasqe", function () { await page.keyboard.up("Control"); } - // TODO: rewrite for the Monaco/qlue-ls API (autoformat -> LSP formatting). - describe.skip("Autoformatting", function () { + // Autoformatting via LSP document-format action (qlue-ls provides textDocument/formatting). + describe("Autoformatting", function () { + // Each test gets a fresh page (from the outer beforeEach); wait for the LSP to connect per-page. + beforeEach(async function () { + this.timeout(15000); + // qlue-ls logs "initialization completed" once ready; poll until language client is live + await page.waitForFunction(() => !!(window as any).yasqe?.getLanguageClient?.(), { + timeout: 12000, + polling: 200, + }); + await wait(500); // let the client settle before issuing format requests + }); it("With literal", async function () { - const value = await page.evaluate(() => { + this.timeout(10000); + const value = await page.evaluate(async () => { window.yasqe.setValue( `PREFIX rdf: select * { ?a rdf:b ?c ; rdf:d "e" ; rdf:f rdf:g .}`, ); - window.yasqe.autoformat(); + await new Promise((r) => setTimeout(r, 400)); + await window.yasqe.editor.getAction("editor.action.formatDocument")?.run(); + await new Promise((r) => setTimeout(r, 1000)); return window.yasqe.getValue(); }); - expect(value).to.equal(`PREFIX rdf: -select * { - ?a rdf:b ?c ; - rdf:d "e" ; - rdf:f rdf:g . -}`); + expect(value).to.equal( + `PREFIX rdf: \nSELECT * {\n ?a rdf:b ?c ;\n rdf:d "e" ;\n rdf:f rdf:g .\n}`, + ); }); it("With group concat", async function () { - const value = await page.evaluate(() => { + this.timeout(10000); + const value = await page.evaluate(async () => { window.yasqe.setValue(`select (group_concat(str(?a); separator='" "') as ?b) { }`); - window.yasqe.autoformat(); + await new Promise((r) => setTimeout(r, 400)); + await window.yasqe.editor.getAction("editor.action.formatDocument")?.run(); + await new Promise((r) => setTimeout(r, 1000)); return window.yasqe.getValue(); }); - expect(value).to.equal(`select (group_concat(str(?a); separator='" "') as ?b) { -}`); + expect(value).to.equal(`SELECT (GROUP_CONCAT(STR(?a); SEPARATOR='" "') AS ?b) {}`); }); }); - // TODO: rewrite for the Monaco/qlue-ls API (getDoc/setCursor + .CodeMirror-hints are gone). - describe.skip("Autoadd prefixes", function () { + // Autoadd prefixes: qlue-ls automatically inserts a PREFIX declaration when a known namespace + // prefix is typed as a SPARQL prefix reference (e.g. "foaf:"). + describe("Autoadd prefixes", function () { + // Each test gets a fresh page; wait for LSP per-page. + beforeEach(async function () { + this.timeout(15000); + await page.waitForFunction(() => !!(window as any).yasqe?.getLanguageClient?.(), { + timeout: 12000, + polling: 200, + }); + await wait(500); + }); //note: this test also covers the infinite loop issue described here: //https://github.com/TriplyDB/YASGUI.YASQE/issues/135 it("Should autoadd foaf prefix", async function () { - await page.evaluate(() => { - const query = `# prefix # -PREFIX geo: select -* where { `; - window.yasqe.setValue(query); + this.timeout(10000); + const query = `# prefix #\nPREFIX geo: select\n* where { `; + await page.evaluate((q: string) => { + window.yasqe.setValue(q); window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: window.yasqe.getDoc().lineCount(), ch: 0 }); - return window.yasqe.getDoc().getCursor(); - }); + const m = window.yasqe.editor.getModel(); + const lastLine = m.getLineCount(); + // Move to end of last line so "foaf:" is typed in SPARQL triple position + window.yasqe.editor.setPosition({ lineNumber: lastLine, column: m.getLineMaxColumn(lastLine) }); + }, query); await type("foaf:"); await page.waitForFunction( - () => { - return window.yasqe.getValue().indexOf("PREFIX foaf: ") >= 0; - }, - { - polling: 10, - }, + () => window.yasqe.getValue().indexOf("PREFIX foaf: ") >= 0, + { polling: 10, timeout: 5000 }, ); }); - it("should show prefix completions after adding new prefix", async () => { + // TODO: rewrite for qlue-ls - the old test relied on a custom test autocompleter that knew about + // "testa: ". qlue-ls does not have this prefix built-in. To restore this + // test, configure a qlue-ls backend with test data that includes testa: completions. + it.skip("should show prefix completions after adding new prefix", async () => { await page.evaluate(() => { - const query = `# prefix # -PREFIX geo: select -* where { ?sub `; + const query = `# prefix #\nPREFIX geo: select\n* where { ?sub `; window.yasqe.setValue(query); window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: window.yasqe.getDoc().lineCount(), ch: 0 }); - return window.yasqe.getDoc().getCursor(); + const m = window.yasqe.editor.getModel(); + const lastLine = m.getLineCount(); + window.yasqe.editor.setPosition({ lineNumber: lastLine, column: m.getLineMaxColumn(lastLine) }); }); await type("testa:"); - await page.waitForFunction( - () => { - return window.yasqe.getValue().indexOf("PREFIX testa: ") >= 0; - }, - { - polling: 10, - }, - ); - //Note from Laurens: This is an invalid test. We should not expect a popup here (this is the property-autocompleter). - //Reason: we didn't configure yasgui to auto-show the lov property completions - //Leaving it here as it doesn't warrant a new issue yet. + await page.waitForFunction(() => window.yasqe.getValue().indexOf("PREFIX testa: ") >= 0, { + polling: 10, + }); await waitForAutocompletionPopup(); }); - it("path traversal should change the correct segment", async () => { - await page.evaluate(() => { - const query = - "PREFIX testa: select * where { ?s testa:someprop/testa:/testa:someotherprop"; - window.yasqe.setValue(query); + // TODO: rewrite for qlue-ls - path-traversal completion used the old custom testa: autocompleter. + // Needs a configured backend with testa: term data to work. + it.skip("path traversal should change the correct segment", async () => { + const query = "PREFIX testa: select * where { ?s testa:someprop/testa:/testa:someotherprop"; + await page.evaluate((q: string) => { + window.yasqe.setValue(q); window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: query.indexOf(":/testa:someotherprop") }); - return window.yasqe.getDoc().getCursor(); - }); + window.yasqe.editor.setPosition({ lineNumber: 1, column: q.indexOf(":/testa:someotherprop") + 1 }); + }, query); await issueAutocompletionKeyCombination(); await waitForAutocompletionPopup(); await page.keyboard.press("Enter"); @@ -170,28 +188,27 @@ PREFIX geo: select "PREFIX testa: select * where { ?s testa:someprop/testa:0/testa:someotherprop", ); }); - it("path traversal should search with the correct path segment", async () => { - await page.evaluate(() => { - const query = - "PREFIX testa: select * where { ?s testa:someprop/testa:someotherprop/testa;"; - window.yasqe.setValue(query); + // TODO: rewrite for qlue-ls - same as above, needs testa: backend data. + it.skip("path traversal should search with the correct path segment", async () => { + const query = "PREFIX testa: select * where { ?s testa:someprop/testa:someotherprop/testa;"; + await page.evaluate((q: string) => { + window.yasqe.setValue(q); window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: query.indexOf(";") }); - return window.yasqe.getDoc().getCursor(); - }); + window.yasqe.editor.setPosition({ lineNumber: 1, column: q.indexOf(";") + 1 }); + }, query); await issueAutocompletionKeyCombination(); try { const hasAutocomplete = await waitForAutocompletionPopup(); - expect(hasAutocomplete).to.be.undefined("", "Expected codemirror hint to not be there"); + expect(hasAutocomplete).to.be.undefined("", "Expected no suggestion popup here"); } catch (e) { - // We expect the timeout to trigger here - if ((e as any).name !== "TimeoutError") { - throw e; - } + if ((e as any).name !== "TimeoutError") throw e; } }); }); - // TODO: rewrite for the Monaco/qlue-ls API (getCompleteToken/autocompleters + .CodeMirror-hints are gone). + // Autocompleting: Monaco uses the LSP suggest widget (.suggest-widget) instead of .CodeMirror-hints. + // Cursor positioning uses editor.setPosition({ lineNumber, column }) (1-indexed, unlike CodeMirror's 0-indexed lines). + // Tests that relied on CodeMirror-internal APIs (getCompleteToken, autocompleters) or on the old LOV/custom + // test autocomplete data are kept as it.skip with a TODO explaining what is needed to restore them. describe.skip("Autocompleting", function () { const getCompleteToken = () => { return page.evaluate(() => window.yasqe.getCompleteToken()); diff --git a/tsconfig-test.json b/tsconfig-test.json index 64ecf2b4..23f6fa3d 100644 --- a/tsconfig-test.json +++ b/tsconfig-test.json @@ -2,6 +2,7 @@ "extends": "./tsconfig-build.json", "compilerOptions": { "outDir": "./build/test", + "target": "es2017", "module": "commonjs", "moduleResolution": "node", "esModuleInterop": true, From 9b600021744b95134c6b26de7d92ddfe516b7e37 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Wed, 10 Jun 2026 12:55:41 +0200 Subject: [PATCH 14/48] refactor: replace default SPARQL endpoint dbpedia by https://sparql.dblp.org/sparql - dblp is much faster for completion queries, and contain generic qualitative data about publications - improve yasr plugins docs - comment out skipped tests for clarity --- .changeset/migrate-to-monaco.md | 2 + dev/qluels.ts | 2 +- docs/.vitepress/theme/qluels.ts | 2 +- docs/docs/getting-started.md | 2 +- docs/docs/plugins.md | 98 ++- docs/docs/request-config.md | 2 +- docs/docs/yasgui.md | 2 +- docs/docs/yasqe.md | 4 +- docs/docs/yasr.md | 2 +- packages/yasqe/src/defaults.ts | 2 +- test/run.ts | 1081 +++++++++++++++---------------- 11 files changed, 647 insertions(+), 552 deletions(-) diff --git a/.changeset/migrate-to-monaco.md b/.changeset/migrate-to-monaco.md index 7471e27f..6ed27199 100644 --- a/.changeset/migrate-to-monaco.md +++ b/.changeset/migrate-to-monaco.md @@ -10,9 +10,11 @@ - Use a wasm language server for diagnostics, syntax highlight, autocompletion, code actions, etc - In the `dev/index.html` implement a demo using the Qlue-LS language server - Delete code in yasqe related to built-in autocomplete and grammar (since this now handled by the language server) + - Update some of the puppeteer tests - Add docs website with VitePress - main page is a bare Yasgui with qlue-ls language server - documentation to use yasgui is defined in markdown files available at `/docs` - Enable light/dark theme - Enable to import the main JS and CSS files from `@zazuko/yasgui` and `@zazuko/yasgui/style.css` - Update CodeMirror used by the YASR component to display raw response JSON from v5 to v6 +- Update default SPARQL endpoint from dbpedia to https://sparql.dblp.org/sparql (faster for completion queries) diff --git a/dev/qluels.ts b/dev/qluels.ts index d3dcdf8c..838e0453 100644 --- a/dev/qluels.ts +++ b/dev/qluels.ts @@ -274,7 +274,7 @@ export async function configureQlueLsBackend(languageClient: any, endpoint: stri // Shared code for the demo pages /** Default SPARQL endpoint used across the demo pages. */ -export const DEMO_ENDPOINT = "https://dbpedia.org/sparql"; +export const DEMO_ENDPOINT = "https://sparql.dblp.org/sparql"; export type DevTheme = "light" | "dark"; diff --git a/docs/.vitepress/theme/qluels.ts b/docs/.vitepress/theme/qluels.ts index d7055a45..a92ea52e 100644 --- a/docs/.vitepress/theme/qluels.ts +++ b/docs/.vitepress/theme/qluels.ts @@ -274,7 +274,7 @@ export async function configureQlueLsBackend(languageClient: any, endpoint: stri // Shared code for the demo pages /** Default SPARQL endpoint used across the demo pages. */ -export const DEMO_ENDPOINT = "https://dbpedia.org/sparql"; +export const DEMO_ENDPOINT = "https://sparql.dblp.org/sparql"; export type DevTheme = "light" | "dark"; diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md index 65785151..d25dde8a 100644 --- a/docs/docs/getting-started.md +++ b/docs/docs/getting-started.md @@ -134,7 +134,7 @@ import "@zazuko/yasgui/style.css"; import { createQlueLsWorker, configureQlueLsBackend } from "./qlue-ls"; const yasgui = new Yasgui(document.getElementById("yasgui")!, { - requestConfig: { endpoint: "https://dbpedia.org/sparql" }, + requestConfig: { endpoint: "https://sparql.dblp.org/sparql" }, // Provide the language server worker (forwarded to the shared editor) languageServerWorker: createQlueLsWorker, diff --git a/docs/docs/plugins.md b/docs/docs/plugins.md index b8514028..16d9d9ca 100644 --- a/docs/docs/plugins.md +++ b/docs/docs/plugins.md @@ -4,6 +4,44 @@ Yasr renders a SPARQL response through one of several plugins. The right plugin The **Table**, **Boolean**, **Response** and **Error** plugins are built in. **Graph** and **Geo** are community plugins registered by the demo (see [Yasr](./yasr#result-view-plugins)). +## Configuring plugins + +Plugins are configured through the `yasr` slot of the Yasgui config. Three options control which plugins are available and how they are ordered: + +```ts +import Yasgui from '@zazuko/yasgui'; +import '@zazuko/yasgui/style.css'; + +const yasgui = new Yasgui(document.getElementById('yasgui'), { + yasr: { + // Tab order in the result area (plugins not listed are appended alphabetically) + pluginOrder: ['table', 'response'], + // Plugin selected when no better match is found for a response + defaultPlugin: 'table', + // Per-plugin configuration, keyed by the name used at registration + plugins: { + table: { + enabled: true, + // Initial values for the plugin's own settings (also adjustable in the UI) + dynamicConfig: { pageSize: 50, compact: false }, + }, + response: { + enabled: true, + dynamicConfig: { maxLines: 60 }, + }, + // Disable a built-in plugin entirely + boolean: { enabled: false }, + }, + }, +}); +``` + +Notes: + +- The key in `plugins` is the name passed to `Yasgui.Yasr.registerPlugin(name, …)` (`table`, `response`, `boolean`, plus any community plugins you register). +- `dynamicConfig` seeds the plugin's per-tab settings. Values the user later changes through the plugin UI are persisted to `localStorage` and take precedence on the next load. +- Defaults: `pluginOrder` is `['table', 'response']` and `defaultPlugin` is `'table'`. + ## Table Renders `SELECT` results as an interactive table: sortable columns, real-time search filtering, virtual scrolling for large result sets, and cell selection with copy to clipboard (Markdown, CSV, TSV). @@ -15,6 +53,24 @@ SELECT ?item ?itemLabel WHERE { } LIMIT 100 ``` +Config (`yasr.plugins.table.dynamicConfig`): + +| Option | Type | Default | Description | +|---|---|---|---| +| `pageSize` | `number` | `50` | Rows shown per page. | +| `compact` | `boolean` | `false` | Compact rendering: hide the first index column and collapse IRI brackets. | +| `isEllipsed` | `boolean` | `true` | Truncate long cell values with an ellipsis (expand on click). | + +```ts +const yasgui = new Yasgui(document.getElementById('yasgui'), { + yasr: { + plugins: { + table: { dynamicConfig: { pageSize: 100, compact: true } }, + }, + }, +}); +``` + ## Boolean Shows the outcome of an `ASK` query as a clear, color-coded `TRUE` (green) or `FALSE` (red). @@ -31,6 +87,22 @@ Shows the raw endpoint response with syntax highlighting (JSON, XML, Turtle…), SELECT * WHERE { ?s ?p ?o } LIMIT 10 ``` +Config (`yasr.plugins.response.dynamicConfig`): + +| Option | Type | Default | Description | +|---|---|---|---| +| `maxLines` | `number` | `30` | Maximum number of lines rendered before the output is truncated (the full payload is still available via copy/download). | + +```ts +const yasgui = new Yasgui(document.getElementById('yasgui'), { + yasr: { + plugins: { + response: { dynamicConfig: { maxLines: 100 } }, + }, + }, +}); +``` + ## Geo ::: warning External plugin @@ -41,8 +113,10 @@ Displays geographic results on an interactive [Leaflet](https://leafletjs.com/) Integrate it: +Register the plugin (under the name `geo`) **before** constructing Yasgui, then add it to `pluginOrder`: + ```ts -import Yasgui from '@rdfjs/yasgui'; +import Yasgui from '@zazuko/yasgui'; import GeoPlugin from 'yasgui-geo-tg'; Yasgui.Yasr.registerPlugin('geo', GeoPlugin); @@ -55,6 +129,8 @@ const yasgui = new Yasgui(document.getElementById('yasgui'), { }); ``` +The map controls (basemap, color, clustering, export, drawing, time slider…) are driven from the plugin UI; there are no programmatic config options to pass through `yasr.plugins.geo`. + Examples queries: ```sparql @@ -90,7 +166,7 @@ Renders `CONSTRUCT` / `DESCRIBE` results as an interactive node-edge graph with Integrate it: ```ts -import Yasgui from '@rdfjs/yasgui'; +import Yasgui from '@zazuko/yasgui'; import GraphPlugin from '@matdata/yasgui-graph-plugin'; Yasgui.Yasr.registerPlugin('graph', GraphPlugin); @@ -102,6 +178,24 @@ const yasgui = new Yasgui(document.getElementById('yasgui'), { }); ``` +Graph settings (compact mode, edge style, node size…) are adjustable from the plugin's ⚙ panel and persisted to `localStorage`. To change the defaults, subclass the plugin and register your variant: + +```ts +class MyGraphPlugin extends GraphPlugin { + constructor(yasr) { + super(yasr); + this.settings.compactMode = true; // hide literal/class nodes (default: false) + this.settings.edgeStyle = 'straight'; // 'curved' | 'straight' (default: 'curved') + this.settings.nodeSize = 'large'; // 'small' | 'medium' | 'large' (default: 'medium') + this.settings.predicateDisplay = 'label';// 'label' | 'icon' | 'hidden' (default: 'icon') + this.settings.physicsEnabled = false; // disable force-directed layout (default: true) + this.settings.showNodeLabels = true; // display node labels (default: true) + } +} + +Yasgui.Yasr.registerPlugin('graph', MyGraphPlugin); +``` + Example query: ```sparql diff --git a/docs/docs/request-config.md b/docs/docs/request-config.md index 952a77f1..5e0c85da 100644 --- a/docs/docs/request-config.md +++ b/docs/docs/request-config.md @@ -8,7 +8,7 @@ Every field may be a value **or** a `(yasqe) => value` function, so you can comp ```ts new Yasgui(el, { requestConfig: { - endpoint: "https://dbpedia.org/sparql", + endpoint: "https://sparql.dblp.org/sparql", method: "POST", headers: () => ({ Authorization: `Bearer ${getToken()}` }), withCredentials: false, diff --git a/docs/docs/yasgui.md b/docs/docs/yasgui.md index f4d1b64d..a34d9514 100644 --- a/docs/docs/yasgui.md +++ b/docs/docs/yasgui.md @@ -9,7 +9,7 @@ import "@zazuko/yasgui/style.css"; import { createQlueLsWorker, configureQlueLsBackend } from "./qlue-ls"; const yasgui = new Yasgui(document.getElementById("yasgui")!, { - requestConfig: { endpoint: "https://dbpedia.org/sparql" }, + requestConfig: { endpoint: "https://sparql.dblp.org/sparql" }, languageServerWorker: createQlueLsWorker, onEndpointChange: (yasgui, endpoint) => configureQlueLsBackend(yasgui.yasqe?.getLanguageClient(), endpoint), diff --git a/docs/docs/yasqe.md b/docs/docs/yasqe.md index f8e7ff9e..f3310ea8 100644 --- a/docs/docs/yasqe.md +++ b/docs/docs/yasqe.md @@ -10,10 +10,10 @@ import { createQlueLsWorker, configureQlueLsBackend } from "./qlue-ls"; const yasqe = new Yasqe(document.getElementById("yasqe")!, { value: "SELECT * WHERE { ?s ?p ?o } LIMIT 10", - requestConfig: { endpoint: "https://dbpedia.org/sparql" }, + requestConfig: { endpoint: "https://sparql.dblp.org/sparql" }, languageServerWorker: createQlueLsWorker, onLanguageClientReady: (languageClient) => - configureQlueLsBackend(languageClient, "https://dbpedia.org/sparql"), + configureQlueLsBackend(languageClient, "https://sparql.dblp.org/sparql"), }); yasqe.on("query", (yasqe, req) => console.log("running", req)); diff --git a/docs/docs/yasr.md b/docs/docs/yasr.md index f3843b54..87f550e2 100644 --- a/docs/docs/yasr.md +++ b/docs/docs/yasr.md @@ -12,7 +12,7 @@ import "@zazuko/yasqe/style.css"; import "@zazuko/yasr/style.css"; const yasqe = new Yasqe(document.getElementById("yasqe")!, { - requestConfig: { endpoint: "https://dbpedia.org/sparql" }, + requestConfig: { endpoint: "https://sparql.dblp.org/sparql" }, }); const yasr = new Yasr(document.getElementById("yasr")!, { // resolve prefixed names in results using the query's PREFIX declarations diff --git a/packages/yasqe/src/defaults.ts b/packages/yasqe/src/defaults.ts index 3ae8422b..2bd7606b 100644 --- a/packages/yasqe/src/defaults.ts +++ b/packages/yasqe/src/defaults.ts @@ -122,7 +122,7 @@ SELECT * WHERE { }; const requestConfig: PlainRequestConfig = { queryArgument: undefined, //undefined means: get query argument based on query mode - endpoint: "https://dbpedia.org/sparql", + endpoint: "https://sparql.dblp.org/sparql", method: "POST", acceptHeaderGraph: "text/turtle,application/n-triples,application/rdf+xml,application/trig,application/n-quads", acceptHeaderSelect: "application/sparql-results+json,*/*;q=0.9", diff --git a/test/run.ts b/test/run.ts index 19a91ed0..73c2ae56 100644 --- a/test/run.ts +++ b/test/run.ts @@ -7,15 +7,13 @@ import * as chai from "chai"; import { it, describe, before, beforeEach, after, afterEach } from "mocha"; const expect = chai.expect; import Yasqe from "@zazuko/yasqe"; -//@ts-ignore ignore unused warning -import { setup, destroy, closePage, getPage, makeScreenshot, inspectLive, wait } from "./utils"; +import { setup, destroy, closePage, getPage, wait } from "./utils"; declare var window: Window & { Yasqe: typeof Yasqe; // TODO: typed as `any` until these tests are rewritten for the Monaco/qlue-ls API. - // The skipped suites below still call the removed CodeMirror API (getDoc, autoformat, - // getCompleteToken, autocompleters); `any` keeps them compiling until then. - yasqe: any; + yasqe: Yasqe; + // yasqe: any; }; describe("Yasqe", function () { @@ -61,27 +59,27 @@ describe("Yasqe", function () { expect(value).to.contain("SELECT"); }); - async function waitForAutocompletionPopup(shouldNotHaveLength?: number): Promise { - if (shouldNotHaveLength !== undefined) { - await page.waitForFunction( - (n: number) => document.querySelectorAll(".suggest-widget.visible .monaco-list-row").length !== n, - { timeout: 2000 }, - shouldNotHaveLength, - ); - } else { - await page.waitForFunction(() => document.querySelector(".suggest-widget")?.classList.contains("visible"), { - timeout: 2000, - }); - await wait(50); - } - return page.evaluate(() => document.querySelectorAll(".suggest-widget.visible .monaco-list-row").length); - } - - async function issueAutocompletionKeyCombination() { - await page.keyboard.down("Control"); - await page.keyboard.press("Space"); - await page.keyboard.up("Control"); - } + // async function waitForAutocompletionPopup(shouldNotHaveLength?: number): Promise { + // if (shouldNotHaveLength !== undefined) { + // await page.waitForFunction( + // (n: number) => document.querySelectorAll(".suggest-widget.visible .monaco-list-row").length !== n, + // { timeout: 2000 }, + // shouldNotHaveLength, + // ); + // } else { + // await page.waitForFunction(() => document.querySelector(".suggest-widget")?.classList.contains("visible"), { + // timeout: 2000, + // }); + // await wait(50); + // } + // return page.evaluate(() => document.querySelectorAll(".suggest-widget.visible .monaco-list-row").length); + // } + + // async function issueAutocompletionKeyCombination() { + // await page.keyboard.down("Control"); + // await page.keyboard.press("Space"); + // await page.keyboard.up("Control"); + // } // Autoformatting via LSP document-format action (qlue-ls provides textDocument/formatting). describe("Autoformatting", function () { @@ -153,521 +151,522 @@ describe("Yasqe", function () { { polling: 10, timeout: 5000 }, ); }); - // TODO: rewrite for qlue-ls - the old test relied on a custom test autocompleter that knew about - // "testa: ". qlue-ls does not have this prefix built-in. To restore this - // test, configure a qlue-ls backend with test data that includes testa: completions. - it.skip("should show prefix completions after adding new prefix", async () => { - await page.evaluate(() => { - const query = `# prefix #\nPREFIX geo: select\n* where { ?sub `; - window.yasqe.setValue(query); - window.yasqe.focus(); - const m = window.yasqe.editor.getModel(); - const lastLine = m.getLineCount(); - window.yasqe.editor.setPosition({ lineNumber: lastLine, column: m.getLineMaxColumn(lastLine) }); - }); - await type("testa:"); - await page.waitForFunction(() => window.yasqe.getValue().indexOf("PREFIX testa: ") >= 0, { - polling: 10, - }); - await waitForAutocompletionPopup(); - }); - // TODO: rewrite for qlue-ls - path-traversal completion used the old custom testa: autocompleter. - // Needs a configured backend with testa: term data to work. - it.skip("path traversal should change the correct segment", async () => { - const query = "PREFIX testa: select * where { ?s testa:someprop/testa:/testa:someotherprop"; - await page.evaluate((q: string) => { - window.yasqe.setValue(q); - window.yasqe.focus(); - window.yasqe.editor.setPosition({ lineNumber: 1, column: q.indexOf(":/testa:someotherprop") + 1 }); - }, query); - await issueAutocompletionKeyCombination(); - await waitForAutocompletionPopup(); - await page.keyboard.press("Enter"); - const newValue = await page.evaluate(() => window.yasqe.getValue()); - expect(newValue).to.equal( - "PREFIX testa: select * where { ?s testa:someprop/testa:0/testa:someotherprop", - ); - }); - // TODO: rewrite for qlue-ls - same as above, needs testa: backend data. - it.skip("path traversal should search with the correct path segment", async () => { - const query = "PREFIX testa: select * where { ?s testa:someprop/testa:someotherprop/testa;"; - await page.evaluate((q: string) => { - window.yasqe.setValue(q); - window.yasqe.focus(); - window.yasqe.editor.setPosition({ lineNumber: 1, column: q.indexOf(";") + 1 }); - }, query); - await issueAutocompletionKeyCombination(); - try { - const hasAutocomplete = await waitForAutocompletionPopup(); - expect(hasAutocomplete).to.be.undefined("", "Expected no suggestion popup here"); - } catch (e) { - if ((e as any).name !== "TimeoutError") throw e; - } - }); + // // TODO: rewrite for qlue-ls - the old test relied on a custom test autocompleter that knew about + // // "testa: ". qlue-ls does not have this prefix built-in. To restore this + // // test, configure a qlue-ls backend with test data that includes testa: completions. + // it.skip("should show prefix completions after adding new prefix", async () => { + // await page.evaluate(() => { + // const query = `# prefix #\nPREFIX geo: select\n* where { ?sub `; + // window.yasqe.setValue(query); + // window.yasqe.focus(); + // const m = window.yasqe.editor.getModel(); + // const lastLine = m.getLineCount(); + // window.yasqe.editor.setPosition({ lineNumber: lastLine, column: m.getLineMaxColumn(lastLine) }); + // }); + // await type("testa:"); + // await page.waitForFunction(() => window.yasqe.getValue().indexOf("PREFIX testa: ") >= 0, { + // polling: 10, + // }); + // await waitForAutocompletionPopup(); + // }); + // // TODO: rewrite for qlue-ls - path-traversal completion used the old custom testa: autocompleter. + // // Needs a configured backend with testa: term data to work. + // it.skip("path traversal should change the correct segment", async () => { + // const query = "PREFIX testa: select * where { ?s testa:someprop/testa:/testa:someotherprop"; + // await page.evaluate((q: string) => { + // window.yasqe.setValue(q); + // window.yasqe.focus(); + // window.yasqe.editor.setPosition({ lineNumber: 1, column: q.indexOf(":/testa:someotherprop") + 1 }); + // }, query); + // await issueAutocompletionKeyCombination(); + // await waitForAutocompletionPopup(); + // await page.keyboard.press("Enter"); + // const newValue = await page.evaluate(() => window.yasqe.getValue()); + // expect(newValue).to.equal( + // "PREFIX testa: select * where { ?s testa:someprop/testa:0/testa:someotherprop", + // ); + // }); + // // TODO: rewrite for qlue-ls - same as above, needs testa: backend data. + // it.skip("path traversal should search with the correct path segment", async () => { + // const query = "PREFIX testa: select * where { ?s testa:someprop/testa:someotherprop/testa;"; + // await page.evaluate((q: string) => { + // window.yasqe.setValue(q); + // window.yasqe.focus(); + // window.yasqe.editor.setPosition({ lineNumber: 1, column: q.indexOf(";") + 1 }); + // }, query); + // await issueAutocompletionKeyCombination(); + // try { + // const hasAutocomplete = await waitForAutocompletionPopup(); + // expect(hasAutocomplete).to.be.undefined("", "Expected no suggestion popup here"); + // } catch (e) { + // if ((e as any).name !== "TimeoutError") throw e; + // } + // }); }); - // Autocompleting: Monaco uses the LSP suggest widget (.suggest-widget) instead of .CodeMirror-hints. - // Cursor positioning uses editor.setPosition({ lineNumber, column }) (1-indexed, unlike CodeMirror's 0-indexed lines). - // Tests that relied on CodeMirror-internal APIs (getCompleteToken, autocompleters) or on the old LOV/custom - // test autocomplete data are kept as it.skip with a TODO explaining what is needed to restore them. - describe.skip("Autocompleting", function () { - const getCompleteToken = () => { - return page.evaluate(() => window.yasqe.getCompleteToken()); - }; - const getCompleteTokenAt = (character: number, line?: number) => { - return page.evaluate( - (at: { character: number; line?: number }) => { - window.yasqe.getDoc().setCursor({ line: at.line || window.yasqe.getCursor().line, ch: at.character }); - return window.yasqe.getCompleteToken(); - }, - { character: character, line: line }, - ); - }; - it("Should only trigger get request when needed", async () => { - // Setting up - await page.evaluate(() => { - const query = `PREFIX testa: select * where {?x a (window as any).showCount++); - window.yasqe.on("autocompletionClose", () => (window as any).hideCount++); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: query.length }); - return window.yasqe.getDoc().getCursor(); - }); - const getHideCount = () => - page.evaluate(() => { - return (window as any).hideCount; - }); - const getShowCount = () => - page.evaluate(() => { - return (window as any).showCount; - }); - await issueAutocompletionKeyCombination(); - await waitForAutocompletionPopup(); - expect(await getShowCount()).to.equal(1); - expect(await getHideCount()).to.equal(0); - await type("ps://"); - await wait(200); - expect(await getShowCount()).to.be.lessThan(7); - expect(await getHideCount()).to.equal(0); - }).timeout(5000); - it("Should show the same results irregardless of where the cursor is", async () => { - await page.evaluate(() => { - const query = `select * where { ?s { - const query = `select * where { ?s { - const query = `select * where { ?s { - const query = `select * where { ?s { - await page.evaluate(() => { - const query = "select * where { ?subject ?predicate ?s}"; - window.yasqe.setValue(query); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: query.length - 2 }); - return window.yasqe.getDoc().getCursor(); - }); - await issueAutocompletionKeyCombination(); - await waitForAutocompletionPopup(); - - await page.keyboard.press("Enter"); - const newValue = await page.evaluate(() => window.yasqe.getValue()); - expect(newValue).to.equal("select * where { ?subject ?predicate ?subject}"); - }); - - it("Should scope to only one part of a path expression", async () => { - const oneLineQuery = "PREFIX testa: select * where { ?subject testa:/testa:2/testa:3"; - await page.evaluate(() => { - const oneLineQuery = "PREFIX testa: select * where { ?subject testa:/testa:2/testa:3"; - window.yasqe.setValue(oneLineQuery); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: oneLineQuery.length - 1 }); - }); - let token = await getCompleteToken(); - expect(token.string).to.equal("testa:3"); - token = await getCompleteTokenAt(oneLineQuery.length - 8); - expect(token.string).to.equal("testa:2"); - token = await getCompleteTokenAt(oneLineQuery.length - 16); - expect(token.string).to.equal("testa:"); - - //token is now in beginning of property path - await issueAutocompletionKeyCombination(); - await waitForAutocompletionPopup(); - - await page.keyboard.press("Enter"); - const newValue = await page.evaluate(() => window.yasqe.getValue()); - expect(newValue).to.equal( - "PREFIX testa: select * where { ?subject testa:0/testa:2/testa:3", - ); - }); - - it("Should deal with infinished full iri", async () => { - await page.evaluate(() => { - const oneLineQuery = "select * where { ?subject window.yasqe.getValue()); - expect(newValue).to.equal("select * where { ?subject ?s"); - }); - - it("Should autocomplete from middle of unfinished iri", async () => { - await page.evaluate(() => { - const oneLineQuery = "select * where { ?subject window.yasqe.getValue()); - expect(newValue).to.equal("select * where { ?subject "); - - await page.evaluate(() => { - const oneLineQuery = "select * where { ?subject { - it("Should expand to the end", async () => { - await page.evaluate(() => { - const oneLineQuery = "select * where { ?subject { - await page.evaluate(() => { - const oneLineQuery = "select * where { bind("; - window.yasqe.setValue(oneLineQuery); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: oneLineQuery.indexOf("(") + 1 }); - }); - const token = await getCompleteToken(); - expect(token.state.possibleCurrent).contains( - "IRI_REF", - `IRI_REF not found in list: "${token.state.possibleCurrent.join('", "')}"`, - ); - }); - it("Autocompleter should show literal suggestion directly after function #156", async () => { - await page.evaluate(() => { - const oneLineQuery = 'select * where { bind("'; - window.yasqe.setValue(oneLineQuery); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: oneLineQuery.indexOf('"') + 1 }); - }); - const token = await getCompleteToken(); - expect(token.state.possibleCurrent).contains( - "IRI_REF", - `IRI_REF not found in list: "${token.state.possibleCurrent.join('", "')}"`, - ); - }); - it("Autocompleter should show correct results after closing bracket", async () => { - await page.evaluate(() => { - const oneLineQuery = "select * where { ?s ?p ?o }"; - window.yasqe.setValue(oneLineQuery); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: oneLineQuery.indexOf("}") + 1 }); - }); - const token = await getCompleteToken(); - expect(token.state.possibleCurrent).contains( - "LIMIT", - `LIMIT not found in list: "${token.state.possibleCurrent.join('", "')}"`, - ); - }); - }); - /** - * This test is tricky, as it uses the LOV API in our test. I.e, if this test fails, first check whether LOV is actually up - */ - describe("Async property autocompletion", function () { - function focusOnAutocompletionPos() { - return page.evaluate(() => { - const query = `PREFIX geo: select * where {?x geo: ?y}`; - window.yasqe.setValue(query); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: query.indexOf(": ?y") + 1 }); - return window.yasqe.getDoc().getCursor(); - }); - } - it("Async autocompletions trickle down when typing", async () => { - // Set the new query, and focus on location where we want to autocomplete - await page.evaluate(() => { - const query = `select * where {?x ?y}`; - window.yasqe.setValue(query); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: query.indexOf("#> ?y") + 1 }); - return window.yasqe.getDoc().getCursor(); - }); - - // Issue autocompletion shortcut - await issueAutocompletionKeyCombination(); - // Wait for hint div to appear - const allInitialResults = await waitForAutocompletionPopup(); - - // Widdle the list down - await type("asWK"); - const newResults = await waitForAutocompletionPopup(allInitialResults); - expect(newResults).is.lessThan(allInitialResults || 0); - }); - - it("Should not append the string we just typed (#1479)", async function () { - /** - * Set the new query, and focus on location where we want to autocomplete - */ - await focusOnAutocompletionPos(); - - /** - * Issue autocompletion shortcut - */ - await issueAutocompletionKeyCombination(); - - /** - * Wait for hint div to appear - */ - const allInitialResults = await waitForAutocompletionPopup(); - - /** - * Type a string to reduce autocompletion list - */ - await type("asW"); - - /** - * Wait for the hint div to be updated to only match suggestions starting with `rcc` - */ - const numResults = await waitForAutocompletionPopup(allInitialResults); - expect(numResults).to.be.lessThan(10); - /** - * Select the first suggestion - */ - await page.keyboard.press("Enter"); - - /** - * Check whether that suggestion is now correctly included in yasqe - */ - await wait(20); - const newValue = await page.evaluate(() => window.yasqe.getValue()); - expect(newValue.trim()).to.equal( - `PREFIX geo: select * where {?x geo:asWKT ?y}`, - ); - }); - it("Should only include matching strings", async function () { - /** - * Set the new query, and focus on location where we want to autocomplete - */ - await focusOnAutocompletionPos(); - - /** - * Issue autocompletion shortcut - */ - await issueAutocompletionKeyCombination(); - - /** - * Wait for hint div to appear - */ - const allResults = await waitForAutocompletionPopup(); - - /** - * Type a string to reduce autocompletion list - */ - await type("defau"); - /** - * Wait for the hint div to be updated to only match suggestions starting with `rcc` - */ - const numResults = await waitForAutocompletionPopup(allResults); - await wait(20); - expect(numResults).to.equal(1); - /** - * Select the first suggestion - */ - await page.keyboard.press("Enter"); - - /** - * Check whether that suggestion is now correctly included in yasqe - */ - const newValue = await page.evaluate(() => window.yasqe.getValue()); - expect(newValue).to.equal( - "PREFIX geo: select * where {?x geo:defaultGeometry ?y}", - ); - }); - }); - describe("Async class autocompletion", function () { - function focusOnAutocompletionPos() { - return page.evaluate(() => { - const query = `PREFIX testb: select * where {?x a { - await focusOnAutocompletionPos(); - - try { - const hasAutocomplete = await page.waitForSelector(`.CodeMirror-hints`, { timeout: 200 }); - expect(hasAutocomplete).to.be.undefined("", "Expected codemirror hint to not be there"); - } catch (e) { - // We expect the timeout to trigger here - if ((e as any).name !== "TimeoutError") { - throw e; - } - } - }); - it("Should auto open when autocompleter is Async and ontype is enabled", async () => { - await page.evaluate(() => { - (window.yasqe.autocompleters["class-local"] as any).config.autoShow = true; - }); - await focusOnAutocompletionPos(); - await page.waitForSelector(`.CodeMirror-hints`); - }); - }); - describe("Async prefix autocompletion", function () { - function focusOnAutocompletionPos() { - return page.evaluate(() => { - window.yasqe.setValue(""); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor({ line: 0, ch: 0 }); - return window.yasqe.getDoc().getCursor(); - }); - } - - it("Should autocomplete", async function () { - // await inspectLive(this); - /** - * Set the new query, and focus on location where we want to autocomplete - */ - await focusOnAutocompletionPos(); - /** - * Issue autocompletion shortcut - */ - await type("prefix t"); - /** - * Wait for hint div to appear - */ - const results = await waitForAutocompletionPopup(); - expect(results).to.equal(3); - - /** - * Type a string to reduce autocompletion list - */ - await type("esta"); - - const filteredResults = await waitForAutocompletionPopup(3); - expect(filteredResults).to.equal(1); - /** - * Select the first suggestion - */ - await page.keyboard.press("Enter"); - - /** - * Check whether that suggestion is now correctly included in yasqe - */ - const newValue = await page.evaluate(() => window.yasqe.getValue()); - expect(newValue).to.equal("prefix testa: "); - }); - }); - - describe("Single line autocompletion", function () { - async function executeFirstLineAutocompletion(query: string) { - await page.evaluate((query) => { - window.yasqe.setValue(query); - window.yasqe.focus(); - window.yasqe.getDoc().setCursor(1, 0); - return window.yasqe.getDoc().getCursor(); - }, query); - await page.keyboard.press("End"); - - await issueAutocompletionKeyCombination(); - await waitForAutocompletionPopup(); - await page.keyboard.press("Enter"); - return page.evaluate(() => window.yasqe.getValue()); - } - - it("Should autocomplete with multiple statements on one line", async function () { - const query = `SELECT * WHERE { - ?a ?b. ?a "); - }); - - it("Should autocomplete with single-line Predicate-Object lists", async function () { - const query = `SELECT * WHERE { - ?a ?b ; "); - }); - - it("Should autocomplete with single-line Object lists", async function () { - const query = `SELECT * WHERE { - ?a a ?b, "); - }); - }); - }); + // // Autocompleting: Monaco uses the LSP suggest widget (.suggest-widget) instead of .CodeMirror-hints. + // // Cursor positioning uses editor.setPosition({ lineNumber, column }) (1-indexed, unlike CodeMirror's 0-indexed lines). + // // Tests that relied on CodeMirror-internal APIs (getCompleteToken, autocompleters) or on the old LOV/custom + // // test autocomplete data are kept as it.skip with a TODO explaining what is needed to restore them. + // describe.skip("Autocompleting", function () { + // const getCompleteToken = () => { + // return page.evaluate(() => window.yasqe.getCompleteToken()); + // }; + // const getCompleteTokenAt = (character: number, line?: number) => { + // return page.evaluate( + // (at: { character: number; line?: number }) => { + // window.yasqe.getDoc().setCursor({ line: at.line || window.yasqe.getCursor().line, ch: at.character }); + // return window.yasqe.getCompleteToken(); + // }, + // { character: character, line: line }, + // ); + // }; + // it("Should only trigger get request when needed", async () => { + // // Setting up + // await page.evaluate(() => { + // const query = `PREFIX testa: select * where {?x a (window as any).showCount++); + // window.yasqe.on("autocompletionClose", () => (window as any).hideCount++); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: query.length }); + // return window.yasqe.getDoc().getCursor(); + // }); + // const getHideCount = () => + // page.evaluate(() => { + // return (window as any).hideCount; + // }); + // const getShowCount = () => + // page.evaluate(() => { + // return (window as any).showCount; + // }); + // await issueAutocompletionKeyCombination(); + // await waitForAutocompletionPopup(); + // expect(await getShowCount()).to.equal(1); + // expect(await getHideCount()).to.equal(0); + // await type("ps://"); + // await wait(200); + // expect(await getShowCount()).to.be.lessThan(7); + // expect(await getHideCount()).to.equal(0); + // }).timeout(5000); + // it("Should show the same results irregardless of where the cursor is", async () => { + // await page.evaluate(() => { + // const query = `select * where { ?s { + // const query = `select * where { ?s { + // const query = `select * where { ?s { + // const query = `select * where { ?s { + // await page.evaluate(() => { + // const query = "select * where { ?subject ?predicate ?s}"; + // window.yasqe.setValue(query); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: query.length - 2 }); + // return window.yasqe.getDoc().getCursor(); + // }); + // await issueAutocompletionKeyCombination(); + // await waitForAutocompletionPopup(); + + // await page.keyboard.press("Enter"); + // const newValue = await page.evaluate(() => window.yasqe.getValue()); + // expect(newValue).to.equal("select * where { ?subject ?predicate ?subject}"); + // }); + + // it("Should scope to only one part of a path expression", async () => { + // const oneLineQuery = "PREFIX testa: select * where { ?subject testa:/testa:2/testa:3"; + + // await page.evaluate(() => { + // const oneLineQuery = "PREFIX testa: select * where { ?subject testa:/testa:2/testa:3"; + // window.yasqe.setValue(oneLineQuery); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: oneLineQuery.length - 1 }); + // }); + // let token = await getCompleteToken(); + // expect(token.string).to.equal("testa:3"); + // token = await getCompleteTokenAt(oneLineQuery.length - 8); + // expect(token.string).to.equal("testa:2"); + // token = await getCompleteTokenAt(oneLineQuery.length - 16); + // expect(token.string).to.equal("testa:"); + + // //token is now in beginning of property path + // await issueAutocompletionKeyCombination(); + // await waitForAutocompletionPopup(); + + // await page.keyboard.press("Enter"); + // const newValue = await page.evaluate(() => window.yasqe.getValue()); + // expect(newValue).to.equal( + // "PREFIX testa: select * where { ?subject testa:0/testa:2/testa:3", + // ); + // }); + + // it("Should deal with infinished full iri", async () => { + // await page.evaluate(() => { + // const oneLineQuery = "select * where { ?subject window.yasqe.getValue()); + // expect(newValue).to.equal("select * where { ?subject ?s"); + // }); + + // it("Should autocomplete from middle of unfinished iri", async () => { + // await page.evaluate(() => { + // const oneLineQuery = "select * where { ?subject window.yasqe.getValue()); + // expect(newValue).to.equal("select * where { ?subject "); + + // await page.evaluate(() => { + // const oneLineQuery = "select * where { ?subject { + // it("Should expand to the end", async () => { + // await page.evaluate(() => { + // const oneLineQuery = "select * where { ?subject { + // await page.evaluate(() => { + // const oneLineQuery = "select * where { bind("; + // window.yasqe.setValue(oneLineQuery); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: oneLineQuery.indexOf("(") + 1 }); + // }); + // const token = await getCompleteToken(); + // expect(token.state.possibleCurrent).contains( + // "IRI_REF", + // `IRI_REF not found in list: "${token.state.possibleCurrent.join('", "')}"`, + // ); + // }); + // it("Autocompleter should show literal suggestion directly after function #156", async () => { + // await page.evaluate(() => { + // const oneLineQuery = 'select * where { bind("'; + // window.yasqe.setValue(oneLineQuery); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: oneLineQuery.indexOf('"') + 1 }); + // }); + // const token = await getCompleteToken(); + // expect(token.state.possibleCurrent).contains( + // "IRI_REF", + // `IRI_REF not found in list: "${token.state.possibleCurrent.join('", "')}"`, + // ); + // }); + // it("Autocompleter should show correct results after closing bracket", async () => { + // await page.evaluate(() => { + // const oneLineQuery = "select * where { ?s ?p ?o }"; + // window.yasqe.setValue(oneLineQuery); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: oneLineQuery.indexOf("}") + 1 }); + // }); + // const token = await getCompleteToken(); + // expect(token.state.possibleCurrent).contains( + // "LIMIT", + // `LIMIT not found in list: "${token.state.possibleCurrent.join('", "')}"`, + // ); + // }); + // }); + // /** + // * This test is tricky, as it uses the LOV API in our test. I.e, if this test fails, first check whether LOV is actually up + // */ + // describe("Async property autocompletion", function () { + // function focusOnAutocompletionPos() { + // return page.evaluate(() => { + // const query = `PREFIX geo: select * where {?x geo: ?y}`; + // window.yasqe.setValue(query); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: query.indexOf(": ?y") + 1 }); + // return window.yasqe.getDoc().getCursor(); + // }); + // } + // it("Async autocompletions trickle down when typing", async () => { + // // Set the new query, and focus on location where we want to autocomplete + // await page.evaluate(() => { + // const query = `select * where {?x ?y}`; + // window.yasqe.setValue(query); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: query.indexOf("#> ?y") + 1 }); + // return window.yasqe.getDoc().getCursor(); + // }); + + // // Issue autocompletion shortcut + // await issueAutocompletionKeyCombination(); + // // Wait for hint div to appear + // const allInitialResults = await waitForAutocompletionPopup(); + + // // Widdle the list down + // await type("asWK"); + // const newResults = await waitForAutocompletionPopup(allInitialResults); + // expect(newResults).is.lessThan(allInitialResults || 0); + // }); + + // it("Should not append the string we just typed (#1479)", async function () { + // /** + // * Set the new query, and focus on location where we want to autocomplete + // */ + // await focusOnAutocompletionPos(); + + // /** + // * Issue autocompletion shortcut + // */ + // await issueAutocompletionKeyCombination(); + + // /** + // * Wait for hint div to appear + // */ + // const allInitialResults = await waitForAutocompletionPopup(); + + // /** + // * Type a string to reduce autocompletion list + // */ + // await type("asW"); + + // /** + // * Wait for the hint div to be updated to only match suggestions starting with `rcc` + // */ + // const numResults = await waitForAutocompletionPopup(allInitialResults); + // expect(numResults).to.be.lessThan(10); + // /** + // * Select the first suggestion + // */ + // await page.keyboard.press("Enter"); + + // /** + // * Check whether that suggestion is now correctly included in yasqe + // */ + // await wait(20); + // const newValue = await page.evaluate(() => window.yasqe.getValue()); + // expect(newValue.trim()).to.equal( + // `PREFIX geo: select * where {?x geo:asWKT ?y}`, + // ); + // }); + // it("Should only include matching strings", async function () { + // /** + // * Set the new query, and focus on location where we want to autocomplete + // */ + // await focusOnAutocompletionPos(); + + // /** + // * Issue autocompletion shortcut + // */ + // await issueAutocompletionKeyCombination(); + + // /** + // * Wait for hint div to appear + // */ + // const allResults = await waitForAutocompletionPopup(); + + // /** + // * Type a string to reduce autocompletion list + // */ + // await type("defau"); + // /** + // * Wait for the hint div to be updated to only match suggestions starting with `rcc` + // */ + // const numResults = await waitForAutocompletionPopup(allResults); + // await wait(20); + // expect(numResults).to.equal(1); + // /** + // * Select the first suggestion + // */ + // await page.keyboard.press("Enter"); + + // /** + // * Check whether that suggestion is now correctly included in yasqe + // */ + // const newValue = await page.evaluate(() => window.yasqe.getValue()); + // expect(newValue).to.equal( + // "PREFIX geo: select * where {?x geo:defaultGeometry ?y}", + // ); + // }); + // }); + // describe("Async class autocompletion", function () { + // function focusOnAutocompletionPos() { + // return page.evaluate(() => { + // const query = `PREFIX testb: select * where {?x a { + // await focusOnAutocompletionPos(); + + // try { + // const hasAutocomplete = await page.waitForSelector(`.CodeMirror-hints`, { timeout: 200 }); + // expect(hasAutocomplete).to.be.undefined("", "Expected codemirror hint to not be there"); + // } catch (e) { + // // We expect the timeout to trigger here + // if ((e as any).name !== "TimeoutError") { + // throw e; + // } + // } + // }); + // it("Should auto open when autocompleter is Async and ontype is enabled", async () => { + // await page.evaluate(() => { + // (window.yasqe.autocompleters["class-local"] as any).config.autoShow = true; + // }); + // await focusOnAutocompletionPos(); + // await page.waitForSelector(`.CodeMirror-hints`); + // }); + // }); + // describe("Async prefix autocompletion", function () { + // function focusOnAutocompletionPos() { + // return page.evaluate(() => { + // window.yasqe.setValue(""); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor({ line: 0, ch: 0 }); + // return window.yasqe.getDoc().getCursor(); + // }); + // } + + // it("Should autocomplete", async function () { + // // await inspectLive(this); + // /** + // * Set the new query, and focus on location where we want to autocomplete + // */ + // await focusOnAutocompletionPos(); + // /** + // * Issue autocompletion shortcut + // */ + // await type("prefix t"); + // /** + // * Wait for hint div to appear + // */ + // const results = await waitForAutocompletionPopup(); + // expect(results).to.equal(3); + + // /** + // * Type a string to reduce autocompletion list + // */ + // await type("esta"); + + // const filteredResults = await waitForAutocompletionPopup(3); + // expect(filteredResults).to.equal(1); + // /** + // * Select the first suggestion + // */ + // await page.keyboard.press("Enter"); + + // /** + // * Check whether that suggestion is now correctly included in yasqe + // */ + // const newValue = await page.evaluate(() => window.yasqe.getValue()); + // expect(newValue).to.equal("prefix testa: "); + // }); + // }); + + // describe("Single line autocompletion", function () { + // async function executeFirstLineAutocompletion(query: string) { + // await page.evaluate((query) => { + // window.yasqe.setValue(query); + // window.yasqe.focus(); + // window.yasqe.getDoc().setCursor(1, 0); + // return window.yasqe.getDoc().getCursor(); + // }, query); + // await page.keyboard.press("End"); + + // await issueAutocompletionKeyCombination(); + // await waitForAutocompletionPopup(); + // await page.keyboard.press("Enter"); + // return page.evaluate(() => window.yasqe.getValue()); + // } + + // it("Should autocomplete with multiple statements on one line", async function () { + // const query = `SELECT * WHERE { + // ?a ?b. ?a "); + // }); + + // it("Should autocomplete with single-line Predicate-Object lists", async function () { + // const query = `SELECT * WHERE { + // ?a ?b ; "); + // }); + + // it("Should autocomplete with single-line Object lists", async function () { + // const query = `SELECT * WHERE { + // ?a a ?b, "); + // }); + // }); + // }); }); From 9379da978e339f34b047473dfe23953b715556e0 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Wed, 10 Jun 2026 13:36:41 +0200 Subject: [PATCH 15/48] doc: improve `fallbackPrefixMap` for demo vitepress deployment --- .../theme/components/YasguiDemo.vue | 5 +- docs/.vitepress/theme/qluels.ts | 250 +++++++++++------- 2 files changed, 152 insertions(+), 103 deletions(-) diff --git a/docs/.vitepress/theme/components/YasguiDemo.vue b/docs/.vitepress/theme/components/YasguiDemo.vue index af28be7d..8ffd7c8c 100644 --- a/docs/.vitepress/theme/components/YasguiDemo.vue +++ b/docs/.vitepress/theme/components/YasguiDemo.vue @@ -30,7 +30,7 @@ function toggleTheme() { onMounted(async () => { const { default: Yasgui } = await import("@zazuko/yasgui"); await import("@zazuko/yasgui/style.css"); - const { createQlueLsWorker, configureQlueLsBackend, DEMO_ENDPOINT } = await import("../qluels"); + const { createQlueLsWorker, configureQlueLsBackend, DEMO_ENDPOINT, fallbackPrefixMap } = await import("../qluels"); syncTheme(isDark.value); @@ -52,13 +52,14 @@ onMounted(async () => { // Yasgui and Yasqe are language-server agnostic: they receive a ready LSP worker and // expose the resulting language client. Everything qlue-ls specific lives in ../qluels. yasguiInstance = new Yasgui(container.value!, { - corsProxy: "", requestConfig: { endpoint: DEMO_ENDPOINT }, yasqe: { theme: isDark.value ? "dark" : "light" }, + yasr: { prefixes: fallbackPrefixMap }, languageServerWorker: createQlueLsWorker, onEndpointChange: (yg: any, endpoint: string) => configureQlueLsBackend(yg.yasqe?.getLanguageClient(), endpoint), }); + (window as any).__yg = yasguiInstance; loading.value = false; }); diff --git a/docs/.vitepress/theme/qluels.ts b/docs/.vitepress/theme/qluels.ts index a92ea52e..a9a35071 100644 --- a/docs/.vitepress/theme/qluels.ts +++ b/docs/.vitepress/theme/qluels.ts @@ -54,16 +54,160 @@ export function createQlueLsWorker(): Promise { }); } +async function fetchPrefixMap(endpoint: string): Promise { + const prefixes: PrefixMap = {}; + try { + const url = new URL(endpoint); + url.searchParams.set( + "query", + `PREFIX sh: + SELECT DISTINCT ?prefix ?namespace + WHERE { [] sh:namespace ?namespace ; sh:prefix ?prefix} + ORDER BY ?prefix`, + ); + const response = await fetch(url.toString(), { + method: "GET", + signal: AbortSignal.timeout(5000), + headers: { Accept: "application/sparql-results+json" }, + }); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const bindings = (await response.json()).results.bindings; + const used = new Set(); + bindings.forEach((b: any) => { + const prefix = b.prefix.value; + const namespace = b.namespace.value; + if (!used.has(namespace) && !prefixes[prefix]) { + prefixes[prefix] = namespace; + used.add(namespace); + } + }); + } catch (error: any) { + console.warn(`Error retrieving prefixes from ${endpoint}:`, error?.message || error); + } + return Object.keys(prefixes).length === 0 ? fallbackPrefixMap : prefixes; +} + +/** Build a flat qlue-ls BackendConfiguration for an endpoint, fetching prefixes when needed. */ +export async function createBackendConf(endpoint: string): Promise { + const prefixMap = await fetchPrefixMap(endpoint); + return { + name: endpoint, + url: endpoint, + prefixMap, + queries: completionQueries, + default: false, + }; +} + +// Avoid re-registering the same backend repeatedly +let lastBackendEndpoint: string | undefined; + +/** + * Register a SPARQL endpoint with the qlue-ls language client and make it the default backend, + */ +export async function configureQlueLsBackend(languageClient: any, endpoint: string): Promise { + if (!languageClient || !endpoint || endpoint === lastBackendEndpoint) return; + lastBackendEndpoint = endpoint; + try { + const backend = await createBackendConf(endpoint); + languageClient.sendNotification("qlueLs/addBackend", backend); + languageClient.sendNotification("qlueLs/updateDefaultBackend", { backendName: backend.name }); + } catch (error) { + lastBackendEndpoint = undefined; // allow retry + console.error("Failed to configure qlue-ls backend for", endpoint, error); + } +} + +// Shared code for the demo pages + +/** Default SPARQL endpoint used across the demo pages. */ +export const DEMO_ENDPOINT = "https://sparql.dblp.org/sparql"; + +export type DevTheme = "light" | "dark"; + +/** + * Wire the demo page's light/dark switcher: start from the OS preference, and on toggle set the + * `[data-theme]` attribute (which drives the CSS) and call `onThemeChange` (e.g. editor.setTheme). + */ +export function setupThemeToggle(onThemeChange?: (theme: DevTheme) => void): DevTheme { + let currentTheme: DevTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + document.documentElement.dataset.theme = currentTheme; + const button = document.getElementById("darkModeToggle"); + const render = () => { + if (!button) return; + button.textContent = currentTheme === "dark" ? "☀️" : "🌙"; + const title = currentTheme === "dark" ? "Switch to light theme" : "Switch to dark theme"; + button.setAttribute("title", title); + button.setAttribute("aria-label", title); + }; + render(); + button?.addEventListener("click", () => { + currentTheme = currentTheme === "dark" ? "light" : "dark"; + document.documentElement.dataset.theme = currentTheme; + render(); + onThemeChange?.(currentTheme); + }); + return currentTheme; +} + /** Minimal fallback prefixes used when an endpoint exposes none. */ export const fallbackPrefixMap: PrefixMap = { + activitystreams: "https://www.w3.org/ns/activitystreams#", + bd: "http://www.bigdata.com/rdf#", + bibo: "http://purl.org/ontology/bibo/", + busco: "http://busco.ezlab.org/schema#", + chebi: "http://purl.obolibrary.org/obo/CHEBI_", + cito: "http://purl.org/spar/cito/", + csvw: "http://purl.org/csvw/vocab#", + dblp: "https://dblp.org/rdf/schema#", + dc: "http://purl.org/dc/elements/1.1/", + dcat: "http://www.w3.org/ns/dcat#", + dcmit: "http://purl.org/dc/dcmitype/", + dcterms: "http://purl.org/dc/terms/", + ECO: "http://purl.obolibrary.org/obo/ECO_", + ensembl: "http://rdf.ebi.ac.uk/resource/ensembl/", + hydra: "http://www.w3.org/ns/hydra/core#", + faldo: "http://biohackathon.org/resource/faldo#", + foaf: "http://xmlns.com/foaf/0.1/", + genex: "http://purl.org/genex#", + geo: "http://www.opengis.net/ont/geosparql#", + geof: "http://www.opengis.net/def/function/geosparql/", + go: "http://purl.obolibrary.org/obo/GO_", + mondo: "http://purl.obolibrary.org/obo/MONDO_", + np: "http://www.nanopub.org/nschema#", + npx: "http://purl.org/nanopub/x/", + obo: "http://purl.obolibrary.org/obo/", + oboInOwl: "http://www.geneontology.org/formats/oboInOwl#", + osmwiki: "https://www.openstreetmap.org/wiki/Key:", + owl: "http://www.w3.org/2002/07/owl#", + pav: "http://purl.org/pav/", + prov: "http://www.w3.org/ns/prov#", + pubmed: "http://purl.uniprot.org/pubmed/", + qudt: "http://qudt.org/schema/qudt/", rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", rdfs: "http://www.w3.org/2000/01/rdf-schema#", - owl: "http://www.w3.org/2002/07/owl#", - xsd: "http://www.w3.org/2001/XMLSchema#", + rh: "http://rdf.rhea-db.org/", + schema: "http://schema.org/", + sd: "http://www.w3.org/ns/sparql-service-description#", + sh: "http://www.w3.org/ns/shacl#", + shex: "http://www.w3.org/ns/shex#", + sio: "http://semanticscience.org/resource/", + sioc: "http://rdfs.org/sioc/ns#", skos: "http://www.w3.org/2004/02/skos/core#", - foaf: "http://xmlns.com/foaf/0.1/", - dc: "http://purl.org/dc/elements/1.1/", - dcterms: "http://purl.org/dc/terms/", + sp: "http://spinrdf.org/sp#", + stato: "http://purl.obolibrary.org/obo/STATO_", + taxon: "http://purl.uniprot.org/taxonomy/", + time: "http://www.w3.org/2006/time#", + uniparc: "http://purl.uniprot.org/uniparc/", + uniprot: "http://purl.uniprot.org/uniprot/", + up: "http://purl.uniprot.org/core/", + vcard: "http://www.w3.org/2006/vcard/ns#", + voag: "http://voag.linkedmodel.org/schema/voag#", + void: "http://rdfs.org/ns/void#", + wd: "http://www.wikidata.org/entity/", + wdt: "http://www.wikidata.org/prop/direct/", + wikibase: "http://wikiba.se/ontology#", + xsd: "http://www.w3.org/2001/XMLSchema#", }; /** @@ -206,99 +350,3 @@ SELECT ?qlue_ls_entity ?qlue_ls_label ?qlue_ls_count WHERE { ORDER BY DESC(?qlue_ls_count) LIMIT {{ limit }} OFFSET {{ offset }}`, }; - -async function fetchPrefixMap(endpoint: string): Promise { - const prefixes: PrefixMap = {}; - try { - const url = new URL(endpoint); - url.searchParams.set( - "query", - `PREFIX sh: - SELECT DISTINCT ?prefix ?namespace - WHERE { [] sh:namespace ?namespace ; sh:prefix ?prefix} - ORDER BY ?prefix`, - ); - const response = await fetch(url.toString(), { - method: "GET", - signal: AbortSignal.timeout(5000), - headers: { Accept: "application/sparql-results+json" }, - }); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - const bindings = (await response.json()).results.bindings; - const used = new Set(); - bindings.forEach((b: any) => { - const prefix = b.prefix.value; - const namespace = b.namespace.value; - if (!used.has(namespace) && !prefixes[prefix]) { - prefixes[prefix] = namespace; - used.add(namespace); - } - }); - } catch (error: any) { - console.warn(`Error retrieving prefixes from ${endpoint}:`, error?.message || error); - } - return Object.keys(prefixes).length === 0 ? fallbackPrefixMap : prefixes; -} - -/** Build a flat qlue-ls BackendConfiguration for an endpoint, fetching prefixes when needed. */ -export async function createBackendConf(endpoint: string): Promise { - const prefixMap = await fetchPrefixMap(endpoint); - return { - name: endpoint, - url: endpoint, - prefixMap, - queries: completionQueries, - default: false, - }; -} - -// Avoid re-registering the same backend repeatedly -let lastBackendEndpoint: string | undefined; - -/** - * Register a SPARQL endpoint with the qlue-ls language client and make it the default backend, - */ -export async function configureQlueLsBackend(languageClient: any, endpoint: string): Promise { - if (!languageClient || !endpoint || endpoint === lastBackendEndpoint) return; - lastBackendEndpoint = endpoint; - try { - const backend = await createBackendConf(endpoint); - languageClient.sendNotification("qlueLs/addBackend", backend); - languageClient.sendNotification("qlueLs/updateDefaultBackend", { backendName: backend.name }); - } catch (error) { - lastBackendEndpoint = undefined; // allow retry - console.error("Failed to configure qlue-ls backend for", endpoint, error); - } -} - -// Shared code for the demo pages - -/** Default SPARQL endpoint used across the demo pages. */ -export const DEMO_ENDPOINT = "https://sparql.dblp.org/sparql"; - -export type DevTheme = "light" | "dark"; - -/** - * Wire the demo page's light/dark switcher: start from the OS preference, and on toggle set the - * `[data-theme]` attribute (which drives the CSS) and call `onThemeChange` (e.g. editor.setTheme). - */ -export function setupThemeToggle(onThemeChange?: (theme: DevTheme) => void): DevTheme { - let currentTheme: DevTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; - document.documentElement.dataset.theme = currentTheme; - const button = document.getElementById("darkModeToggle"); - const render = () => { - if (!button) return; - button.textContent = currentTheme === "dark" ? "☀️" : "🌙"; - const title = currentTheme === "dark" ? "Switch to light theme" : "Switch to dark theme"; - button.setAttribute("title", title); - button.setAttribute("aria-label", title); - }; - render(); - button?.addEventListener("click", () => { - currentTheme = currentTheme === "dark" ? "light" : "dark"; - document.documentElement.dataset.theme = currentTheme; - render(); - onThemeChange?.(currentTheme); - }); - return currentTheme; -} From 383d0cda9b47ceccba1eec18584e6bf5d5dcb743 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Wed, 10 Jun 2026 15:40:35 +0200 Subject: [PATCH 16/48] fix: fix small visual issues in settings panel --- docs/.vitepress/config.mts | 2 +- packages/yasgui/src/TabPanel.scss | 19 +++++++++++++++++++ packages/yasgui/src/darkmode.css | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 6f900654..856e689f 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -41,7 +41,7 @@ export default defineConfig({ text: "Configuration", items: [ { text: "Language server", link: "/docs/language-server" }, - { text: "Result plugins", link: "/docs/plugins" }, + { text: "Results plugins", link: "/docs/plugins" }, { text: "Request configuration", link: "/docs/request-config" }, { text: "Theming", link: "/docs/theming" }, { text: "Monaco editor options", link: "/docs/editor-options" }, diff --git a/packages/yasgui/src/TabPanel.scss b/packages/yasgui/src/TabPanel.scss index 0b2c8bd6..67af4768 100644 --- a/packages/yasgui/src/TabPanel.scss +++ b/packages/yasgui/src/TabPanel.scss @@ -64,6 +64,10 @@ color: black; border-color: black; } + + & + .selectorButton { + margin-left: 8px; + } } } @@ -86,12 +90,27 @@ font-weight: bold; font-size: small; } + + select { + background: #fff; + color: rgba(0, 0, 0, 0.87); + border: 1px solid rgba(0, 0, 0, 0.26); + border-radius: 6px; + padding: 4px 6px; + cursor: pointer; + + &:focus { + outline: none; + border-color: #337ab7; + } + } } } .textSetting { display: flex; flex-direction: column; + gap: 4px; // Explicit colors so the fields stay legible regardless of the ambient // `color-scheme` (host pages may toggle it independently of our theme). diff --git a/packages/yasgui/src/darkmode.css b/packages/yasgui/src/darkmode.css index bb51de71..1e9fb438 100644 --- a/packages/yasgui/src/darkmode.css +++ b/packages/yasgui/src/darkmode.css @@ -139,6 +139,14 @@ html[data-theme="dark"] { .tabMenu .textSetting input[type="text"]:focus { border-color: var(--yg-d-link); } + .tabMenu .selector select { + background: var(--yg-d-raised); + color: var(--yg-d-text); + border-color: var(--yg-d-border-strong); + } + .tabMenu .selector select:focus { + border-color: var(--yg-d-link); + } .yasgui_textfield > input, .yasgui_textfield > textarea { @@ -293,6 +301,14 @@ html[data-theme="dark"] { .tabMenu .textSetting input[type="text"]:focus { border-color: var(--yg-d-link); } + .tabMenu .selector select { + background: var(--yg-d-raised); + color: var(--yg-d-text); + border-color: var(--yg-d-border-strong); + } + .tabMenu .selector select:focus { + border-color: var(--yg-d-link); + } .yasgui_textfield > input, .yasgui_textfield > textarea { From 3956efba20fe94c4aa8a42c4f017461094441913 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Wed, 10 Jun 2026 16:31:33 +0200 Subject: [PATCH 17/48] doc: improve types of yasqe fields, add auto generated API reference to docs website --- .gitignore | 2 + docs/.vitepress/config.mts | 19 +- .../theme/components/YasguiDemo.vue | 3 +- docs/docs/api.md | 47 ---- package-lock.json | 204 +++++++++++++++++- package.json | 7 +- packages/utils/src/index.ts | 4 + packages/yasgui/src/index.ts | 4 + packages/yasqe/src/defaults.ts | 57 ----- packages/yasqe/src/editor/editorConfig.ts | 2 +- packages/yasqe/src/index.ts | 57 +++-- packages/yasr/src/index.ts | 4 + test/run.ts | 12 +- typedoc.json | 21 ++ 14 files changed, 300 insertions(+), 143 deletions(-) delete mode 100644 docs/docs/api.md create mode 100644 typedoc.json diff --git a/.gitignore b/.gitignore index 8757127e..ff6c75ad 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ build yarn.lock cache/ dist/ +# Generated by TypeDoc +docs/api/ diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 856e689f..77a41fcd 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,6 +1,7 @@ import { defineConfig } from "vitepress"; import wasm from "vite-plugin-wasm"; import importMetaUrlPlugin from "@codingame/esbuild-import-meta-url-plugin"; +import typedocSidebar from "../api/typedoc-sidebar.json"; // https://vitepress.dev/reference/site-config export default defineConfig({ @@ -16,10 +17,11 @@ export default defineConfig({ themeConfig: { // https://vitepress.dev/reference/default-theme-config logo: "/yasgui.png", - // nav: [ - // { text: "Demo", link: "/" }, - // { text: "API", link: "/docs/api" }, - // ], + nav: [ + { text: "Editor", link: "/" }, + { text: "Documentation", link: "/docs/introduction" }, + { text: "API Reference", link: "/api/" }, + ], sidebar: { "/docs/": [ { @@ -50,11 +52,18 @@ export default defineConfig({ { text: "Reference", items: [ - { text: "Yasqe API", link: "/docs/api" }, + { text: "API reference", link: "/api/" }, { text: "Build from source", link: "/docs/build" }, ], }, ], + "/api/": [ + { + text: "API Reference", + items: [{ text: "Overview", link: "/api/" }], + }, + ...typedocSidebar, + ], }, socialLinks: [{ icon: "github", link: "https://github.com/rdfjs/Yasgui" }], search: { provider: "local" }, diff --git a/docs/.vitepress/theme/components/YasguiDemo.vue b/docs/.vitepress/theme/components/YasguiDemo.vue index 8ffd7c8c..c588fd8c 100644 --- a/docs/.vitepress/theme/components/YasguiDemo.vue +++ b/docs/.vitepress/theme/components/YasguiDemo.vue @@ -8,6 +8,7 @@ let yasguiInstance: any = null; diff --git a/docs/.vitepress/theme/qluels.ts b/docs/.vitepress/theme/qluels.ts index d1143179..19a41e25 100644 --- a/docs/.vitepress/theme/qluels.ts +++ b/docs/.vitepress/theme/qluels.ts @@ -8,6 +8,7 @@ * language server (e.g. SemanticWebLanguageServer/swls) only touches this file. */ import QlueLsWorker from "./qluels.worker?worker"; +import type { MonacoLanguageClient } from "monaco-languageclient"; export type SparqlEngine = "QLever" | "GraphDB" | "Virtuoso" | "MillenniumDB" | "Blazegraph" | "Jena"; @@ -54,6 +55,9 @@ export function createQlueLsWorker(): Promise { }); } +/** + * Fetch a prefix map from the given SPARQL endpoint by querying for prefixes declared with SHACL + */ async function fetchPrefixMap(endpoint: string): Promise { const prefixes: PrefixMap = {}; try { @@ -99,6 +103,13 @@ export async function createBackendConf(endpoint: string): Promise void): Dev return currentTheme; } +// Qlue-ls settings + +/** + * qlue-ls server settings pushed once the language client is ready. Mirrors the keys the qlue-ls + * VS Code extension reads from its `config.get(...)` calls; here we send the demo defaults. + */ +export const qlueLsSettings = { + format: { + alignPredicates: false, + alignPrefixes: false, + separatePrologue: true, + capitalizeKeywords: true, + insertSpaces: true, + tabSize: 2, + whereNewLine: false, + filterSameLine: true, + lineLength: 120, + contractTriples: true, + keepEmptyLines: false, + }, + completion: { + timeoutMs: 10000, + resultSizeLimit: 50, + subjectCompletionTriggerLength: 3, + objectCompletionSuffix: true, + variableCompletionLimit: 10, + sameSubjectSemicolon: true, + }, + prefixes: { + addMissing: true, + removeUnused: false, + }, +}; + /** Minimal fallback prefixes used when an endpoint exposes none. */ export const fallbackPrefixMap: PrefixMap = { activitystreams: "https://www.w3.org/ns/activitystreams#", diff --git a/packages/yasgui/src/index.ts b/packages/yasgui/src/index.ts index 3aee47d0..0d7424cb 100644 --- a/packages/yasgui/src/index.ts +++ b/packages/yasgui/src/index.ts @@ -195,10 +195,16 @@ export class Yasgui extends EventEmitter { consumeShareLink: null, // handled by the parent yasgui instance, not yasqe createShareableLink: () => this.getActiveTab()?.getShareableLink() || "", requestConfig: () => (this.getActiveTab()?.getProcessedRequestConfig() ?? {}) as any, - // Forward the consumer-provided language server to the shared editor languageServerWorker: this.config.languageServerWorker, // Once the language client is ready, notify for the active tab endpoint - onLanguageClientReady: () => { + onLanguageClientReady: (languageClient, yasqe) => { + // Yasgui overwrites the consumer defined yasqe.onLanguageClientReady, so chain back to it + // `as` needed because PartialConfig wraps function-valued keys in Partial<>, dropping their call signature. + ( + this.config.yasqe?.onLanguageClientReady as + | ((lc: typeof languageClient, yq: typeof yasqe) => void) + | undefined + )?.(languageClient, yasqe); const endpoint = this.getActiveTab()?.getEndpoint(); if (endpoint) this.emitEndpointChange(endpoint); }, From 30474608583bcefecd257fefbb790844c8e99871 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Thu, 11 Jun 2026 09:44:56 +0200 Subject: [PATCH 21/48] refactor: use `DeepPartial` for partial configs in yasgui/yasqe/yasr - allows arbitrarily nested partials (previous implementation partiality was going 2 levels deep) - function signatures are preserved, dropping the `onLanguageClientReady` cast. - add `DeepPartial`/`DeepReadonly` into `yasgui-utils`, drop `ts-essentials` dependency --- package-lock.json | 13 +---------- packages/utils/src/index.ts | 1 + packages/utils/src/types.ts | 24 +++++++++++++++++++++ packages/yasgui/src/index.ts | 19 +++++----------- packages/yasqe/src/index.ts | 5 ++--- packages/yasr/package.json | 3 +-- packages/yasr/src/index.ts | 3 ++- packages/yasr/src/plugins/response/index.ts | 2 +- packages/yasr/src/plugins/table/index.ts | 2 +- 9 files changed, 38 insertions(+), 34 deletions(-) create mode 100644 packages/utils/src/types.ts diff --git a/package-lock.json b/package-lock.json index f78e77dd..be17d5d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9268,16 +9268,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/ts-essentials": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", - "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "typescript": ">=3.7.0" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -11065,8 +11055,7 @@ "@types/n3": "^1.1.5", "@types/node": "^22.5.4", "@types/papaparse": "^5.3.2", - "@types/sanitize-html": "^1.20.2", - "ts-essentials": "^7.0.1" + "@types/sanitize-html": "^1.20.2" }, "engines": { "node": ">= 8" diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 5fab0ce0..f19f5408 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -4,6 +4,7 @@ */ import DOMPurify from "dompurify"; export { default as Storage } from "./Storage"; +export type { DeepPartial, DeepReadonly } from "./types"; const { sanitize } = DOMPurify; diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts new file mode 100644 index 00000000..36d919ee --- /dev/null +++ b/packages/utils/src/types.ts @@ -0,0 +1,24 @@ +/** Shared recursive type helpers. */ + +type Primitive = string | number | boolean | bigint | symbol | null | undefined; +// Types that should be passed through untouched by the recursive helpers below. +// Keeping Function here is what preserves call signatures (e.g. config callbacks). +type Builtin = Primitive | Function | Date | Error | RegExp; + +/** Recursively makes every property of `T` optional, leaving Builtin types intact. */ +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends object + ? { [K in keyof T]?: DeepPartial } + : T; + +/** Recursively marks every property of `T` as readonly, leaving Builtin types intact. */ +export type DeepReadonly = T extends Builtin + ? T + : T extends Array + ? ReadonlyArray> + : T extends object + ? { readonly [K in keyof T]: DeepReadonly } + : T; diff --git a/packages/yasgui/src/index.ts b/packages/yasgui/src/index.ts index 0d7424cb..4f732080 100644 --- a/packages/yasgui/src/index.ts +++ b/packages/yasgui/src/index.ts @@ -1,5 +1,5 @@ /** - * Yasgui · the full SPARQL app (editor + results + tab management). + * Yasgui · the full SPARQL app (editor + results + endpoint/tab management). * @module Yasgui */ import { EventEmitter } from "events"; @@ -14,6 +14,7 @@ import TabElements from "./TabElements"; import { default as Yasqe, PartialConfig as YasqeConfig, RequestConfig } from "@zazuko/yasqe"; import { default as Yasr, Config as YasrConfig } from "@zazuko/yasr"; import { addClass, removeClass } from "@zazuko/yasgui-utils"; +import type { DeepPartial } from "@zazuko/yasgui-utils"; import "./index.scss"; import "./darkmode.css"; import "@zazuko/yasr/src/scss/global.scss"; @@ -62,9 +63,7 @@ export interface Config { */ onEndpointChange?: (yasgui: Yasgui, endpoint: string) => void; } -export type PartialConfig = { - [P in keyof Config]?: Config[P] extends object ? Partial : Config[P]; -}; +export type PartialConfig = DeepPartial; export type TabJson = PersistedTabJson; @@ -199,12 +198,7 @@ export class Yasgui extends EventEmitter { // Once the language client is ready, notify for the active tab endpoint onLanguageClientReady: (languageClient, yasqe) => { // Yasgui overwrites the consumer defined yasqe.onLanguageClientReady, so chain back to it - // `as` needed because PartialConfig wraps function-valued keys in Partial<>, dropping their call signature. - ( - this.config.yasqe?.onLanguageClientReady as - | ((lc: typeof languageClient, yq: typeof yasqe) => void) - | undefined - )?.(languageClient, yasqe); + this.config.yasqe?.onLanguageClientReady?.(languageClient, yasqe); const endpoint = this.getActiveTab()?.getEndpoint(); if (endpoint) this.emitEndpointChange(endpoint); }, @@ -260,17 +254,14 @@ export class Yasgui extends EventEmitter { this.updateEditorsContent(tab); } - /** Load a tab's query, request config and endpoint backend into the shared editor. */ + /** Load a tab query, request config and endpoint backend into the shared editor. */ public updateEditorsContent(tab: Tab) { if (!this.yasqe) return; const tabConfig = tab.getPersistedJson(); this.yasqe.setValue(tabConfig.yasqe.value); - // Use this tab's persisted height, falling back to the default so a tab without a saved - // height doesn't inherit the previously shown tab's height. this.yasqe.setSize(tabConfig.yasqe.editorHeight || this.yasqe.config.editorHeight); this.yasqe.config.requestConfig = () => tab.getProcessedRequestConfig() as any; this.yasqe.config.createShareableLink = () => tab.getShareableLink(); - // The active endpoint is now this tab's endpoint this.emitEndpointChange(tab.getEndpoint()); } diff --git a/packages/yasqe/src/index.ts b/packages/yasqe/src/index.ts index fef6e30b..5dca3620 100644 --- a/packages/yasqe/src/index.ts +++ b/packages/yasqe/src/index.ts @@ -7,6 +7,7 @@ import { Storage as YStorage } from "@zazuko/yasgui-utils"; import * as queryString from "query-string"; import { drawSvgStringAsElement, addClass, removeClass } from "@zazuko/yasgui-utils"; import { merge } from "lodash-es"; +import type { DeepPartial } from "@zazuko/yasgui-utils"; import * as Sparql from "./sparql"; import * as imgs from "./imgs"; @@ -873,9 +874,7 @@ export interface RequestConfig { export type PlainRequestConfig = { [K in keyof RequestConfig]: Exclude[K], Function>; }; -export type PartialConfig = { - [P in keyof Config]?: Config[P] extends object ? Partial : Config[P]; -}; +export type PartialConfig = DeepPartial; export interface Config { /** Initial query value. */ value: string; diff --git a/packages/yasr/package.json b/packages/yasr/package.json index 4dffc294..1425267f 100644 --- a/packages/yasr/package.json +++ b/packages/yasr/package.json @@ -62,8 +62,7 @@ "@types/n3": "^1.1.5", "@types/node": "^22.5.4", "@types/papaparse": "^5.3.2", - "@types/sanitize-html": "^1.20.2", - "ts-essentials": "^7.0.1" + "@types/sanitize-html": "^1.20.2" }, "publishConfig": { "access": "public" diff --git a/packages/yasr/src/index.ts b/packages/yasr/src/index.ts index 31aa78ee..0c7fbb3a 100644 --- a/packages/yasr/src/index.ts +++ b/packages/yasr/src/index.ts @@ -4,6 +4,7 @@ */ import { EventEmitter } from "events"; import { merge, filter, mapValues, uniqueId } from "lodash-es"; +import type { DeepPartial } from "@zazuko/yasgui-utils"; import getDefaults from "./defaults"; import { Plugin } from "./plugins"; import { @@ -55,7 +56,7 @@ export class Yasr extends EventEmitter { // Utils public utils = { addScript: addScript, addCSS: addCss, sanitize: sanitize }; - constructor(parent: HTMLElement, conf: Partial = {}, data?: any) { + constructor(parent: HTMLElement, conf: DeepPartial = {}, data?: any) { super(); if (!parent) throw new Error("No parent passed as argument. Dont know where to draw YASR"); this.rootEl = document.createElement("div"); diff --git a/packages/yasr/src/plugins/response/index.ts b/packages/yasr/src/plugins/response/index.ts index 66740a39..aeabe655 100644 --- a/packages/yasr/src/plugins/response/index.ts +++ b/packages/yasr/src/plugins/response/index.ts @@ -10,7 +10,7 @@ import { foldGutter, syntaxHighlighting, defaultHighlightStyle } from "@codemirr import { json } from "@codemirror/lang-json"; import { drawSvgStringAsElement, addClass, removeClass, drawFontAwesomeIconAsSvg } from "@zazuko/yasgui-utils"; import * as faAlignIcon from "@fortawesome/free-solid-svg-icons/faAlignLeft"; -import { DeepReadonly } from "ts-essentials"; +import type { DeepReadonly } from "@zazuko/yasgui-utils"; import * as imgs from "../../imgs"; const responseTheme = EditorView.theme({ diff --git a/packages/yasr/src/plugins/table/index.ts b/packages/yasr/src/plugins/table/index.ts index 9bf494d0..d913aed7 100644 --- a/packages/yasr/src/plugins/table/index.ts +++ b/packages/yasr/src/plugins/table/index.ts @@ -12,7 +12,7 @@ import type { Plugin, DownloadInfo } from "../"; import Yasr from "../../"; import { drawSvgStringAsElement, drawFontAwesomeIconAsSvg, addClass, removeClass } from "@zazuko/yasgui-utils"; import * as faTableIcon from "@fortawesome/free-solid-svg-icons/faTable"; -import { DeepReadonly } from "ts-essentials"; +import type { DeepReadonly } from "@zazuko/yasgui-utils"; import { cloneDeep } from "lodash-es"; import sanitize from "../../helpers/sanitize"; import type { Api, ConfigColumns, CellMetaSettings, Config } from "datatables.net"; From f83dbbac08f1c20807a8ca5e9114b78bb16660bb Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Thu, 11 Jun 2026 11:34:50 +0200 Subject: [PATCH 22/48] refactor: create `languageServers/` folder in `yasqe` for helper types/functions for various language servers - enable to provide sane defaults and make configuring the language servers easier without adding dependencies - added `languageServers/qlueLs.ts` with helpers for the qlue-ls language server - updated `dev/` and vitepress deployments to use these helpers --- .changeset/migrate-to-monaco.md | 2 + dev/index.html | 6 +- dev/qluels.ts | 306 ------------------ dev/swls.html | 6 +- dev/utils.ts | 47 +++ dev/yasqe.html | 6 +- dev/yasr.html | 6 +- .../theme/components/YasguiDemo.vue | 17 +- docs/.vitepress/theme/utils.ts | 45 +++ docs/docs/build.md | 3 +- docs/docs/getting-started.md | 40 +-- docs/docs/language-server.md | 130 ++++---- docs/docs/yasgui.md | 9 +- docs/docs/yasqe.md | 13 +- packages/yasgui/src/index.ts | 1 + packages/yasqe/src/index.ts | 1 + packages/yasqe/src/languageServers/index.ts | 20 ++ .../yasqe/src/languageServers/qlue-ls.ts | 283 ++++++++-------- typedoc.json | 3 + 19 files changed, 396 insertions(+), 548 deletions(-) delete mode 100644 dev/qluels.ts create mode 100644 dev/utils.ts create mode 100644 docs/.vitepress/theme/utils.ts create mode 100644 packages/yasqe/src/languageServers/index.ts rename docs/.vitepress/theme/qluels.ts => packages/yasqe/src/languageServers/qlue-ls.ts (68%) diff --git a/.changeset/migrate-to-monaco.md b/.changeset/migrate-to-monaco.md index 6ed27199..c5f4013e 100644 --- a/.changeset/migrate-to-monaco.md +++ b/.changeset/migrate-to-monaco.md @@ -8,6 +8,7 @@ - Migrate from CodeMirror 5 to Monaco editor (VSCode) with a language server - `Yasqe` now extends `EventEmitter` instead of `CodeMirror` - Use a wasm language server for diagnostics, syntax highlight, autocompletion, code actions, etc + - Ship qlue-ls plumbing (settings, types, backend/endpoint registration) in yasqe under the `qlueLs` namespace, so consumers can wire up the language server easily - In the `dev/index.html` implement a demo using the Qlue-LS language server - Delete code in yasqe related to built-in autocomplete and grammar (since this now handled by the language server) - Update some of the puppeteer tests @@ -18,3 +19,4 @@ - Enable to import the main JS and CSS files from `@zazuko/yasgui` and `@zazuko/yasgui/style.css` - Update CodeMirror used by the YASR component to display raw response JSON from v5 to v6 - Update default SPARQL endpoint from dbpedia to https://sparql.dblp.org/sparql (faster for completion queries) +- Improve partial config implementation diff --git a/dev/index.html b/dev/index.html index 7974b62e..93eec29d 100644 --- a/dev/index.html +++ b/dev/index.html @@ -22,8 +22,8 @@
diff --git a/dev/qluels.ts b/dev/qluels.ts deleted file mode 100644 index 838e0453..00000000 --- a/dev/qluels.ts +++ /dev/null @@ -1,306 +0,0 @@ -/// -/** - * Consumer-side qlue-ls configuration for the demo pages. - * - * yasqe/yasgui are language-server agnostic: they only receive a ready LSP `Worker` and expose the - * resulting language client. Everything qlue-ls specific (the WASM worker, the backend/completion - * query config, the `qlueLs/addBackend` extension) lives here, so swapping to another SPARQL - * language server (e.g. SemanticWebLanguageServer/swls) only touches this file. - */ -import QlueLsWorker from "./qluels.worker?worker"; - -export type SparqlEngine = "QLever" | "GraphDB" | "Virtuoso" | "MillenniumDB" | "Blazegraph" | "Jena"; - -export interface PrefixMap { - [key: string]: string; -} - -/** Valid qlue-ls completion query template keys (the `CompletionTemplate` enum, camelCase). */ -export type CompletionTemplate = - | "hover" - | "subjectCompletion" - | "predicateCompletionContextSensitive" - | "predicateCompletionContextInsensitive" - | "objectCompletionContextSensitive" - | "objectCompletionContextInsensitive" - | "valuesCompletionContextSensitive" - | "valuesCompletionContextInsensitive"; - -export type CompletionQueries = Partial>; - -/** Qlue-ls endpoint backend configuration */ -export interface BackendConfiguration { - name: string; - url: string; - healthCheckUrl?: string; - engine?: SparqlEngine; - requestMethod?: "GET" | "POST"; - prefixMap: PrefixMap; - default: boolean; - queries: CompletionQueries; - additionalData?: unknown; -} - -/** - * Create a qlue-ls language-server worker and resolve once its WASM is initialized. - * Pass the result to Yasqe/Yasgui via the `languageServerWorker` config option. - */ -export function createQlueLsWorker(): Promise { - return new Promise((resolve) => { - const worker = new QlueLsWorker({ name: "qlue-ls" }); - worker.onmessage = (event) => { - if (event.data?.type === "ready") resolve(worker); - }; - }); -} - -/** Minimal fallback prefixes used when an endpoint exposes none. */ -export const fallbackPrefixMap: PrefixMap = { - rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - rdfs: "http://www.w3.org/2000/01/rdf-schema#", - owl: "http://www.w3.org/2002/07/owl#", - xsd: "http://www.w3.org/2001/XMLSchema#", - skos: "http://www.w3.org/2004/02/skos/core#", - foaf: "http://xmlns.com/foaf/0.1/", - dc: "http://purl.org/dc/elements/1.1/", - dcterms: "http://purl.org/dc/terms/", -}; - -/** - * Completion query templates (qlue-ls Jinja-like templating) used to resolve term completions - * against the endpoint. Generic enough to work on most label-bearing datasets. - */ -const completionQueries: CompletionQueries = { - // TODO: improve default completionQueries - subjectCompletion: `{% include "prefix_declarations" %} -PREFIX rdfs: -PREFIX rdf: -SELECT ?qlue_ls_entity (SAMPLE(?label) as ?qlue_ls_label) WHERE { - ?qlue_ls_entity rdf:type ?type ; rdfs:label ?label . - {% if search_term_uncompressed %} - FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) - {% elif search_term %} - FILTER REGEX(?label, "^{{ search_term }}") - {% endif %} -} -GROUP BY ?qlue_ls_entity -ORDER BY DESC(COUNT(?qlue_ls_entity)) -LIMIT {{ limit }} OFFSET {{ offset }}`, - predicateCompletionContextInsensitive: `{% include "prefix_declarations" %} -SELECT ?qlue_ls_entity ?qlue_ls_score WHERE { - { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_score) WHERE - { - {{local_context}} - } - GROUP BY ?qlue_ls_entity } - {% if search_term_uncompressed %} - FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) - {% elif search_term %} - FILTER REGEX(STR(?qlue_ls_entity), "{{ search_term }}", "i") - {% endif %} -} ORDER BY DESC(?qlue_ls_score) -LIMIT {{ limit }} OFFSET {{ offset }}`, - objectCompletionContextInsensitive: `{% include "prefix_declarations" %} -PREFIX rdfs: -SELECT ?qlue_ls_entity (MIN(?name) AS ?qlue_ls_label) (MIN(?alias) AS ?qlue_ls_alias) (MAX(?count) AS ?qlue_ls_count) WHERE { - { - { SELECT ?qlue_ls_entity ?name ?alias ?count WHERE { - { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?count) WHERE { - {{local_context}} - } GROUP BY ?qlue_ls_entity } - ?qlue_ls_entity rdfs:label ?name BIND(?name AS ?alias) - {% if search_term_uncompressed %} - FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) - {% elif search_term %} - FILTER REGEX(STR(?alias), "^{{ search_term }}") - {% endif %} - } } - } UNION { - { SELECT ?qlue_ls_entity ?name ?alias ?count WHERE { - { SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?count) WHERE { - {{local_context}} - } GROUP BY ?qlue_ls_entity } - BIND(?qlue_ls_entity AS ?name) BIND(?qlue_ls_entity AS ?alias) - {% if search_term_uncompressed %} - FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) - {% elif search_term %} - FILTER REGEX(STR(?alias), "^{{ search_term }}") - {% endif %} - } } - } -} GROUP BY ?qlue_ls_entity ORDER BY DESC(?qlue_ls_count) -LIMIT {{ limit }} OFFSET {{ offset }}`, - predicateCompletionContextSensitive: `{% include "prefix_declarations" %} -PREFIX rdfs: -{% if subject is not variable %} - -SELECT ?qlue_ls_entity (SAMPLE(?qlue_ls_label_or_null) AS ?qlue_ls_label) ?qlue_ls_count WHERE { - { - SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { - {{ local_context }} - } - GROUP BY ?qlue_ls_entity - } - OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } - BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?label) - {% if search_term_uncompressed %} - FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) - {% elif search_term %} - FILTER REGEX(STR(?label), "{{ search_term }}", "i") - {% endif %} -} -GROUP BY ?qlue_ls_entity ?qlue_ls_count -ORDER BY DESC(?qlue_ls_count) - -{% else %} - -SELECT ?qlue_ls_entity (SAMPLE(?qlue_ls_label_or_null) AS ?qlue_ls_label) ?qlue_ls_count WHERE { - {% if not context %} - { - SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { - {{ local_context }} - } - GROUP BY ?qlue_ls_entity - } - {% else %} - { - SELECT ?qlue_ls_entity (COUNT(DISTINCT {{ subject }}) AS ?qlue_ls_count) WHERE { - {{ context }} {{ local_context }} - } - GROUP BY ?qlue_ls_entity - } - {% endif %} - OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } - BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?label) - {% if search_term_uncompressed %} - FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) - {% elif search_term %} - FILTER REGEX(STR(?label), "{{ search_term }}", "i") - {% endif %} -} -GROUP BY ?qlue_ls_entity ?qlue_ls_count -ORDER BY DESC(?qlue_ls_count) - -{% endif %} -LIMIT {{ limit }} OFFSET {{ offset }}`, - objectCompletionContextSensitive: `{% include "prefix_declarations" %} -PREFIX rdfs: -SELECT ?qlue_ls_entity ?qlue_ls_label ?qlue_ls_count WHERE { - { - SELECT ?qlue_ls_entity (COUNT(?qlue_ls_entity) AS ?qlue_ls_count) WHERE { - {{ context }} {{ local_context }} . - } - GROUP BY ?qlue_ls_entity - } - OPTIONAL { - ?qlue_ls_entity rdf:type [ rdfs:label ?qlue_ls_label_or_null ] . - } - OPTIONAL { ?qlue_ls_entity rdfs:label ?qlue_ls_label_or_null } - BIND (COALESCE(?qlue_ls_label_or_null, ?qlue_ls_entity) AS ?qlue_ls_label) - {% if search_term_uncompressed %} - FILTER (REGEX(STR(?qlue_ls_entity), "^{{ search_term_uncompressed }}")) - {% elif search_term %} - FILTER REGEX(STR(?qlue_ls_label), "^{{ search_term }}") - {% endif %} -} -ORDER BY DESC(?qlue_ls_count) -LIMIT {{ limit }} OFFSET {{ offset }}`, -}; - -async function fetchPrefixMap(endpoint: string): Promise { - const prefixes: PrefixMap = {}; - try { - const url = new URL(endpoint); - url.searchParams.set( - "query", - `PREFIX sh: - SELECT DISTINCT ?prefix ?namespace - WHERE { [] sh:namespace ?namespace ; sh:prefix ?prefix} - ORDER BY ?prefix`, - ); - const response = await fetch(url.toString(), { - method: "GET", - signal: AbortSignal.timeout(5000), - headers: { Accept: "application/sparql-results+json" }, - }); - if (!response.ok) throw new Error(`HTTP ${response.status}`); - const bindings = (await response.json()).results.bindings; - const used = new Set(); - bindings.forEach((b: any) => { - const prefix = b.prefix.value; - const namespace = b.namespace.value; - if (!used.has(namespace) && !prefixes[prefix]) { - prefixes[prefix] = namespace; - used.add(namespace); - } - }); - } catch (error: any) { - console.warn(`Error retrieving prefixes from ${endpoint}:`, error?.message || error); - } - return Object.keys(prefixes).length === 0 ? fallbackPrefixMap : prefixes; -} - -/** Build a flat qlue-ls BackendConfiguration for an endpoint, fetching prefixes when needed. */ -export async function createBackendConf(endpoint: string): Promise { - const prefixMap = await fetchPrefixMap(endpoint); - return { - name: endpoint, - url: endpoint, - prefixMap, - queries: completionQueries, - default: false, - }; -} - -// Avoid re-registering the same backend repeatedly -let lastBackendEndpoint: string | undefined; - -/** - * Register a SPARQL endpoint with the qlue-ls language client and make it the default backend, - */ -export async function configureQlueLsBackend(languageClient: any, endpoint: string): Promise { - if (!languageClient || !endpoint || endpoint === lastBackendEndpoint) return; - lastBackendEndpoint = endpoint; - try { - const backend = await createBackendConf(endpoint); - languageClient.sendNotification("qlueLs/addBackend", backend); - languageClient.sendNotification("qlueLs/updateDefaultBackend", { backendName: backend.name }); - } catch (error) { - lastBackendEndpoint = undefined; // allow retry - console.error("Failed to configure qlue-ls backend for", endpoint, error); - } -} - -// Shared code for the demo pages - -/** Default SPARQL endpoint used across the demo pages. */ -export const DEMO_ENDPOINT = "https://sparql.dblp.org/sparql"; - -export type DevTheme = "light" | "dark"; - -/** - * Wire the demo page's light/dark switcher: start from the OS preference, and on toggle set the - * `[data-theme]` attribute (which drives the CSS) and call `onThemeChange` (e.g. editor.setTheme). - */ -export function setupThemeToggle(onThemeChange?: (theme: DevTheme) => void): DevTheme { - let currentTheme: DevTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; - document.documentElement.dataset.theme = currentTheme; - const button = document.getElementById("darkModeToggle"); - const sunSvg = ``; - const moonSvg = ``; - const render = () => { - if (!button) return; - button.innerHTML = currentTheme === "dark" ? sunSvg : moonSvg; - const title = currentTheme === "dark" ? "Switch to light theme" : "Switch to dark theme"; - button.setAttribute("title", title); - button.setAttribute("aria-label", title); - }; - render(); - button?.addEventListener("click", () => { - currentTheme = currentTheme === "dark" ? "light" : "dark"; - document.documentElement.dataset.theme = currentTheme; - render(); - onThemeChange?.(currentTheme); - }); - return currentTheme; -} diff --git a/dev/swls.html b/dev/swls.html index ec72b323..b6e4f79e 100644 --- a/dev/swls.html +++ b/dev/swls.html @@ -22,8 +22,8 @@
diff --git a/dev/utils.ts b/dev/utils.ts new file mode 100644 index 00000000..417f2737 --- /dev/null +++ b/dev/utils.ts @@ -0,0 +1,47 @@ +/// +import QlueLsWorker from "./qluels.worker?worker"; + +/** + * Create a qlue-ls language-server worker and resolve once its WASM is initialized. + * Pass the result to Yasqe/Yasgui via the `languageServerWorker` config option. + */ +export function createQlueLsWorker(): Promise { + return new Promise((resolve) => { + const worker = new QlueLsWorker({ name: "qlue-ls" }); + worker.onmessage = (event) => { + if (event.data?.type === "ready") resolve(worker); + }; + }); +} + +/** Default SPARQL endpoint used across the demo pages. */ +export const DEMO_ENDPOINT = "https://sparql.dblp.org/sparql"; + +export type DevTheme = "light" | "dark"; + +/** + * Wire the demo page's light/dark switcher: start from the OS preference, and on toggle set the + * `[data-theme]` attribute (which drives the CSS) and call `onThemeChange` (e.g. editor.setTheme). + */ +export function setupThemeToggle(onThemeChange?: (theme: DevTheme) => void): DevTheme { + let currentTheme: DevTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + document.documentElement.dataset.theme = currentTheme; + const button = document.getElementById("darkModeToggle"); + const sunSvg = ``; + const moonSvg = ``; + const render = () => { + if (!button) return; + button.innerHTML = currentTheme === "dark" ? sunSvg : moonSvg; + const title = currentTheme === "dark" ? "Switch to light theme" : "Switch to dark theme"; + button.setAttribute("title", title); + button.setAttribute("aria-label", title); + }; + render(); + button?.addEventListener("click", () => { + currentTheme = currentTheme === "dark" ? "light" : "dark"; + document.documentElement.dataset.theme = currentTheme; + render(); + onThemeChange?.(currentTheme); + }); + return currentTheme; +} diff --git a/dev/yasqe.html b/dev/yasqe.html index fbc3aaf8..929d74f3 100644 --- a/dev/yasqe.html +++ b/dev/yasqe.html @@ -20,15 +20,15 @@
+ + diff --git a/dev/index.html b/dev/index.html index 93eec29d..1d2ab1ee 100644 --- a/dev/index.html +++ b/dev/index.html @@ -12,6 +12,7 @@ @@ -22,15 +23,17 @@
+ + + + + + diff --git a/docs/.vitepress/theme/components/YasguiDemo.vue b/docs/.vitepress/theme/components/YasguiDemo.vue index 04a7b845..998fe87b 100644 --- a/docs/.vitepress/theme/components/YasguiDemo.vue +++ b/docs/.vitepress/theme/components/YasguiDemo.vue @@ -6,7 +6,6 @@ let yasgui: any = null; diff --git a/dev/index.html b/dev/index.html index 82fb17f3..476cfaae 100644 --- a/dev/index.html +++ b/dev/index.html @@ -31,19 +31,42 @@ // yasgui/yasqe are LS-agnostic; to switch to another SPARQL language server only ./qluels changes. window.onpageshow = function () { const theme = setupThemeToggle((t) => window.yasgui.yasqe?.setTheme(t)); + + const endpointOf = () => window.yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT; + const onReady = (settings) => (client) => { + qlueLs.configureSettings(client, settings); + qlueLs.configureBackend(client, endpointOf()); + }; + const onEndpointChange = (client, endpoint) => qlueLs.configureBackend(client, endpoint); + const fullSettings = qlueLs.defaultSettings; + const alignedSettings = { + ...qlueLs.defaultSettings, + format: { ...qlueLs.defaultSettings.format, whereNewLine: true, alignPredicates: true }, + }; + window.yasgui = new Yasgui(document.getElementById("yasgui"), { requestConfig: { endpoint: DEMO_ENDPOINT }, yasqe: (parent, conf) => new Yasqe(parent, { ...conf, theme, - languageServerWorker: createQlueLsWorker, - onLanguageClientReady: (languageClient) => { - qlueLs.configureSettings(languageClient); - qlueLs.configureBackend(languageClient, window.yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT); - }, + languageServers: [ + { + label: "Qlue-ls", + description: "SPARQL language server with endpoint-powered completions", + worker: createQlueLsWorker, + onReady: onReady(fullSettings), + onEndpointChange, + }, + { + label: "Qlue-ls (aligned)", + description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", + worker: createQlueLsWorker, + onReady: onReady(alignedSettings), + onEndpointChange, + }, + ], }), - onEndpointChange: (yasgui, endpoint) => qlueLs.configureBackend(yasgui.yasqe?.getLanguageClient(), endpoint), }); }; diff --git a/dev/qluelsCmClient.ts b/dev/qluelsCmClient.ts index e8783bec..6d2b5af8 100644 --- a/dev/qluelsCmClient.ts +++ b/dev/qluelsCmClient.ts @@ -10,10 +10,8 @@ import { EditorView, ViewPlugin, ViewUpdate, Decoration, DecorationSet } from "@ import { RangeSetBuilder, StateField, StateEffect } from "@codemirror/state"; import { setDiagnostics, Diagnostic } from "@codemirror/lint"; import init, { init_language_server, listen } from "qlue-ls"; -// The shared backend builder (which fetches the endpoint's prefix map and applies the default -// completion/hover query templates) and server settings live in the shared utils qlue-ls helper. -import { createBackendConf, defaultSettings } from "@zazuko/yasgui-utils"; -import type { Settings, BackendOptions, DeepPartial } from "@zazuko/yasgui-utils"; +import { qlueLs } from "@zazuko/yasgui-utils"; +import type { DeepPartial } from "@zazuko/yasgui-utils"; const SEVERITY: Record = { 1: "error", 2: "warning", 3: "info", 4: "info" }; @@ -305,32 +303,6 @@ function wasmTransport(): Transport { }; } -/** Merge partial overrides over the shared {@link defaultSettings}, section by section, so any - * omitted field follows the same defaults as the Monaco editor. */ -function mergeSettings(overrides: DeepPartial): Settings { - return { - format: { ...defaultSettings.format, ...overrides.format }, - completion: { ...defaultSettings.completion, ...overrides.completion }, - prefixes: { ...defaultSettings.prefixes, ...overrides.prefixes }, - }; -} - -/** - * Register `endpoint` with qlue-ls and make it the active default. qlue-ls upserts by name, so - * calling this again for the same endpoint updates it in place — use it to keep the server in sync - * when the editor's endpoint changes. `options` mirrors Monaco's `configureBackend` ({@link - * BackendOptions} from `@zazuko/yasgui-utils`). - */ -export async function setQlueLsBackend( - client: LSPClient, - endpoint: string, - options: BackendOptions = {}, -): Promise { - const conf = await createBackendConf(endpoint, { ...options, default: true }); - client.notification("qlueLs/addBackend", conf); - client.notification("qlueLs/updateDefaultBackend", { backendName: conf.name }); -} - /** * Initialise qlue-ls and return a connected LSPClient ready to pass to the CodeMirror Yasqe/Yasgui * as `yasqe.lsp.client`. Register an endpoint with {@link setQlueLsBackend} to enable @@ -340,13 +312,13 @@ export async function setQlueLsBackend( * `settings` overrides are merged over the shared {@link defaultSettings} and pushed via * `qlueLs/changeSettings`. Pass only the fields you want to tune; the rest follow the defaults. */ -export async function createQlueLsClient(settings: DeepPartial = {}): Promise { +export async function createQlueLsClient(settings: DeepPartial = {}): Promise { await init(); const client = new LSPClient({ extensions: [...languageServerExtensions(), pullDiagnostics(), semanticTokens()], }).connect(wasmTransport()); await client.initializing; - client.notification("qlueLs/changeSettings", mergeSettings(settings)); - + qlueLs.configureSettings(client, settings); return client; } +export { qlueLs }; diff --git a/dev/swls.html b/dev/swls.html index 46a17ee6..b884ec9a 100644 --- a/dev/swls.html +++ b/dev/swls.html @@ -23,16 +23,31 @@ diff --git a/docs/.vitepress/theme/components/YasguiCmDemo.vue b/docs/.vitepress/theme/components/YasguiCmDemo.vue index f9241f7f..5fa8e375 100644 --- a/docs/.vitepress/theme/components/YasguiCmDemo.vue +++ b/docs/.vitepress/theme/components/YasguiCmDemo.vue @@ -33,7 +33,7 @@ onMounted(async () => { await import("@zazuko/yasgui/style.css"); await import("@zazuko/yasqe-codemirror/style.css"); // The qlue-ls language server (WASM) and the @codemirror/lsp-client wiring live in the embedder. - const { createQlueLsClient, setQlueLsBackend } = await import("../qluelsCmClient"); + const { createQlueLsClient, qlueLs } = await import("../qluelsCmClient"); const { DEMO_ENDPOINT } = await import("../utils"); syncTheme(isDark.value); @@ -53,25 +53,42 @@ onMounted(async () => { Yasgui.Yasr.registerPlugin("Graph", GraphPlugin as any); Yasgui.Yasr.registerPlugin("Geo", GeoPlugin as any); - let lsp: { client: any } | undefined; - try { - const client = await createQlueLsClient({ completion: { timeoutMs: 2000, resultSizeLimit: 50 } }); - lsp = { client }; - } catch (e) { - console.warn("Could not start qlue-ls; running editors without a language server", e); - } + // qlue-ls compiles to a single-instance WASM module that runs in the main thread here, so both + // entries share ONE client and differ only by the settings their `onReady` pushes + let clientPromise: Promise | undefined; + const getClient = () => (clientPromise ??= createQlueLsClient()); + const endpointOf = () => yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT; + const onReady = (settings: any) => (client: any) => { + qlueLs.configureSettings(client, settings); + qlueLs.configureBackend(client, endpointOf()); + }; + const onEndpointChange = (client: any, endpoint: string) => qlueLs.configureBackend(client, endpoint); yasgui = new Yasgui(container.value!, { requestConfig: { endpoint: DEMO_ENDPOINT }, - // The editor factory: build a CodeMirror Yasqe, wiring in the theme + shared LSP client. + // The editor factory: build a CodeMirror Yasqe, wiring in the theme + the available LSP clients. yasqe: (parent: HTMLElement, conf: any) => - new Yasqe(parent, { ...conf, theme: isDark.value ? "dark" : "light", lsp }), - // Keep qlue-ls pointed at the active tab's endpoint. - onEndpointChange: (_yg: any, endpoint: string) => { - if (lsp?.client) setQlueLsBackend(lsp.client, endpoint); - }, + new Yasqe(parent, { + ...conf, + theme: isDark.value ? "dark" : "light", + languageServers: [ + { + label: "Qlue-ls", + description: "SPARQL language server with endpoint-powered completions", + client: getClient, + onReady: onReady({ completion: { timeoutMs: 2000, resultSizeLimit: 50 } }), + onEndpointChange, + }, + { + label: "Qlue-ls (aligned)", + description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", + client: getClient, + onReady: onReady({ format: { whereNewLine: true, alignPredicates: true } }), + onEndpointChange, + }, + ], + }), }); - (window as any).__ygcm = yasgui; loading.value = false; }); diff --git a/docs/.vitepress/theme/components/YasguiDemo.vue b/docs/.vitepress/theme/components/YasguiDemo.vue index 35e923d4..e55c36fe 100644 --- a/docs/.vitepress/theme/components/YasguiDemo.vue +++ b/docs/.vitepress/theme/components/YasguiDemo.vue @@ -52,27 +52,41 @@ onMounted(async () => { Yasgui.Yasr.registerPlugin("Graph", GraphPlugin as any); Yasgui.Yasr.registerPlugin("Geo", GeoPlugin as any); - // Yasgui and Yasqe are language-server agnostic: they receive a ready LSP worker and expose the - // resulting language client. The qlue-ls plumbing comes from the `qlueLs` helpers (re-exported - // from @zazuko/yasgui); ../utils only builds the consumer-side WASM worker. + const onReady = (settings: any) => (client: any) => { + qlueLs.configureSettings(client, settings); + qlueLs.configureBackend(client, yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT); + }; + const onEndpointChange = (client: any, endpoint: string) => qlueLs.configureBackend(client, endpoint); + const alignedSettings = { + ...qlueLs.defaultSettings, + format: { ...qlueLs.defaultSettings.format, whereNewLine: true, alignPredicates: true }, + }; yasgui = new Yasgui(container.value!, { requestConfig: { endpoint: DEMO_ENDPOINT }, - // The editor factory builds a Monaco Yasqe, wiring in the theme + consumer-provided LSP worker. + // The editor factory builds a Monaco Yasqe, wiring in the theme + the available LSP workers. yasqe: (parent: HTMLElement, conf: any) => new Yasqe(parent, { ...conf, theme: isDark.value ? "dark" : "light", - languageServerWorker: createQlueLsWorker, - onLanguageClientReady: (languageClient: any) => { - qlueLs.configureSettings(languageClient); - qlueLs.configureBackend(languageClient, yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT); - }, + languageServers: [ + { + label: "Qlue-ls", + description: "SPARQL language server with endpoint-powered completions", + worker: createQlueLsWorker, + onReady: onReady(qlueLs.defaultSettings), + onEndpointChange, + }, + { + label: "Qlue-ls (aligned)", + description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", + worker: createQlueLsWorker, + onReady: onReady(alignedSettings), + onEndpointChange, + }, + ], }), yasr: { prefixes: qlueLs.fallbackPrefixMap }, - onEndpointChange: (yg: any, endpoint: string) => - qlueLs.configureBackend(yg.yasqe?.getLanguageClient(), endpoint), }); - (window as any).__yg = yasgui; loading.value = false; }); diff --git a/docs/.vitepress/theme/qluelsCmClient.ts b/docs/.vitepress/theme/qluelsCmClient.ts index f1f0b6e0..f0a8c094 100644 --- a/docs/.vitepress/theme/qluelsCmClient.ts +++ b/docs/.vitepress/theme/qluelsCmClient.ts @@ -10,10 +10,8 @@ import { EditorView, ViewPlugin, ViewUpdate, Decoration, DecorationSet } from "@ import { RangeSetBuilder, StateField, StateEffect } from "@codemirror/state"; import { setDiagnostics, Diagnostic } from "@codemirror/lint"; import init, { init_language_server, listen } from "qlue-ls"; -// The shared backend builder (which fetches the endpoint's prefix map and applies the default -// completion/hover query templates) and server settings live in the shared utils qlue-ls helper. -import { createBackendConf, defaultSettings } from "@zazuko/yasgui-utils"; -import type { Settings, BackendOptions, DeepPartial } from "@zazuko/yasgui-utils"; +import { qlueLs } from "@zazuko/yasgui-utils"; +import type { Settings, DeepPartial } from "@zazuko/yasgui-utils"; const SEVERITY: Record = { 1: "error", 2: "warning", 3: "info", 4: "info" }; @@ -304,39 +302,16 @@ function wasmTransport(): Transport { }; } -/** Merge partial overrides over the shared {@link defaultSettings}, section by section, so any - * omitted field follows the same defaults as the Monaco editor. */ -function mergeSettings(overrides: DeepPartial): Settings { - return { - format: { ...defaultSettings.format, ...overrides.format }, - completion: { ...defaultSettings.completion, ...overrides.completion }, - prefixes: { ...defaultSettings.prefixes, ...overrides.prefixes }, - }; -} - -/** - * Register `endpoint` with qlue-ls and make it the active default. - */ -export async function setQlueLsBackend( - client: LSPClient, - endpoint: string, - options: BackendOptions = {}, -): Promise { - const conf = await createBackendConf(endpoint, { ...options, default: true }); - client.notification("qlueLs/addBackend", conf); - client.notification("qlueLs/updateDefaultBackend", { backendName: conf.name }); -} - /** * Initialise qlue-ls and return a connected LSPClient ready to pass to the CodeMirror Yasqe/Yasgui - * as `yasqe.lsp.client`. + * as a language server's `client` in the `languageServers` config. */ -export async function createQlueLsClient(settings: DeepPartial = {}): Promise { +export async function createQlueLsClient(settings: DeepPartial = {}): Promise { await init(); const client = new LSPClient({ extensions: [...languageServerExtensions(), pullDiagnostics(), semanticTokens()], }).connect(wasmTransport()); await client.initializing; - client.notification("qlueLs/changeSettings", mergeSettings(settings)); + qlueLs.configureSettings(client, settings); return client; } diff --git a/docs/.vitepress/theme/utils.ts b/docs/.vitepress/theme/utils.ts index 694c2c6d..74d07a77 100644 --- a/docs/.vitepress/theme/utils.ts +++ b/docs/.vitepress/theme/utils.ts @@ -3,7 +3,7 @@ import QlueLsWorker from "./qluels.worker?worker"; /** * Create a qlue-ls language-server worker and resolve once its WASM is initialized. - * Pass the result to Yasqe/Yasgui via the `languageServerWorker` config option. + * Pass the result to Yasqe/Yasgui as a language server's `worker` in the `languageServers` config. */ export function createQlueLsWorker(): Promise { return new Promise((resolve) => { diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md index 5aba26ec..47fb0101 100644 --- a/docs/docs/getting-started.md +++ b/docs/docs/getting-started.md @@ -114,24 +114,31 @@ const yasgui = new Yasgui(document.getElementById("yasgui")!, { requestConfig: { endpoint: "https://sparql.dblp.org/sparql" }, // Editor factory: Yasgui is editor-independent, so you build the editor yourself. - // Here the Monaco editor (@zazuko/yasqe), wiring in the language server worker and - // pushing qlue-ls settings once the language client is connected. + // Each server per-entry hooks fire only while it is active: onReady, onEndpointChange yasqe: (parent, conf) => new Yasqe(parent, { ...conf, - languageServerWorker: createQlueLsWorker, - onLanguageClientReady: (languageClient) => qlueLs.configureSettings(languageClient), + languageServers: [ + { + label: "Qlue-ls", + worker: createQlueLsWorker, + onReady: (client) => { + qlueLs.configureSettings(client); + qlueLs.configureBackend(client, yasgui?.getTab()?.getEndpoint()); + }, + onEndpointChange: (client, endpoint) => qlueLs.configureBackend(client, endpoint), + }, + ], }), - - // Fires on load, tab switch, and endpoint edits, defined once, applies to all tabs. - // Register the active endpoint as the default backend so completions resolve against it. - onEndpointChange: (yasgui, endpoint) => - qlueLs.configureBackend(yasgui.yasqe?.getLanguageClient(), endpoint), }); ``` ::: tip CodeMirror instead of Monaco -The factory is also where you choose the editor implementation. To use the CodeMirror 6 editor, import `@zazuko/yasqe-codemirror` instead and pass it an LSP client via `lsp: { client }` (see [Language server](./language-server)). +The factory is also where you choose the editor implementation. To use the CodeMirror 6 editor, import `@zazuko/yasqe-codemirror` instead and give each language server a `client` (an `LSPClient`) rather than a `worker` (see [Language server](./language-server)). +::: + +::: tip Offering several servers +Add more entries to `languageServers` to let users switch at runtime; with two or more, a switcher appears (right-click in Monaco, a dropdown in CodeMirror) and the choice is remembered per endpoint. ::: That is the same setup that powers the [live demo](/). From here: diff --git a/docs/docs/introduction.md b/docs/docs/introduction.md index 5ef9ee29..25c77c50 100644 --- a/docs/docs/introduction.md +++ b/docs/docs/introduction.md @@ -13,7 +13,7 @@ Try it now on the [live demo](/). Then head to [Getting started](./getting-start ## Powered by language servers -Yasgui's smart features, semantic highlighting, diagnostics, code actions, hover, formatting and autocompletion, all come from a **SPARQL language server (LSP)**. Yasgui and Yasqe are language-server agnostic: you provide a ready language server and they wire a language client to it. +Yasgui's smart features, semantic highlighting, diagnostics, code actions, hover, formatting and autocompletion, all come from a **SPARQL language server (LSP)**. Yasgui and Yasqe are language-server agnostic: you provide a ready language server and they wire a language client to it. All server-specific configuration lives in your app, which keeps the libraries small and lets you swap servers later. @@ -25,7 +25,7 @@ Yasgui is editor-independent, you pick the editor implementation when embedding | Editor | Package | When to use | | --- | --- | --- | -| **Monaco** | `@zazuko/yasqe` | The editor that powers VS Code. More features; the default. | +| **Monaco** | `@zazuko/yasqe` | The editor that powers VS Code, more features. | | **CodeMirror 6** | `@zazuko/yasqe-codemirror` | More lightweight. | Both implement the same shared editor interface, so they are interchangeable behind Yasgui's editor factory. diff --git a/docs/docs/language-server.md b/docs/docs/language-server.md index 46604f7b..0fb3c1fa 100644 --- a/docs/docs/language-server.md +++ b/docs/docs/language-server.md @@ -71,11 +71,17 @@ export {}; ## Hooking it up -Pass the worker factory as `languageServerWorker`, then use the `qlueLs` helpers to push settings and -register the active endpoint as the default backend (so completions resolve against it). +Configure one or more servers through the `languageServers` array. Each entry has a `label`, the +`worker` (instance or factory) and two optional **per-server** hooks — only the *active* server's +hooks fire: -- With **Yasgui**, register backends from `onEndpointChange` (fires on load, tab switch and endpoint - edits, once for the whole app): +- `onReady(client, yasqe)` — runs when that server becomes active (on load or when switched to). + Use it to push settings and register the active endpoint as the default backend. +- `onEndpointChange(client, endpoint, yasqe)` — runs when the endpoint changes while that server is + active. Use it to re-register the backend for the new endpoint. + +The first entry is activated on load; with two or more configured, a switcher appears (right-click +the editor in Monaco, a dropdown in CodeMirror) and the user's choice is remembered per endpoint. ```ts [main.ts] import Yasgui from "@zazuko/yasgui"; @@ -83,36 +89,63 @@ register the active endpoint as the default backend (so completions resolve agai import { createQlueLsWorker } from "./qlue-ls"; new Yasgui(el, { - // Yasgui is editor-independent: pass an editor factory and wire the worker into the editor. + // Yasgui is editor-independent: pass an editor factory and list the servers in the editor. yasqe: (parent, conf) => new Yasqe(parent, { ...conf, - languageServerWorker: createQlueLsWorker, - onLanguageClientReady: (languageClient) => qlueLs.configureSettings(languageClient), + languageServers: [ + { + label: "Qlue-ls", + description: "SPARQL language server with endpoint-powered completions", + worker: createQlueLsWorker, + onReady: (client) => { + qlueLs.configureSettings(client); + qlueLs.configureBackend(client, yasgui?.getTab()?.getEndpoint()); + }, + onEndpointChange: (client, endpoint) => qlueLs.configureBackend(client, endpoint), + }, + ], }), - onEndpointChange: (yasgui, endpoint) => - qlueLs.configureBackend(yasgui.yasqe?.getLanguageClient(), endpoint), }); ``` -- With **Yasqe**, do both from `onLanguageClientReady`: +Standalone **Yasqe** is the same — the per-server `onReady` (and `onEndpointChange`, which you can +trigger yourself via `yasqe.notifyEndpointChange(endpoint)`) carry the setup: ```ts [main.ts] import Yasqe, { qlueLs } from "@zazuko/yasqe"; import { createQlueLsWorker } from "./qlue-ls"; new Yasqe(el, { - languageServerWorker: createQlueLsWorker, - onLanguageClientReady: (lc) => { - qlueLs.configureSettings(lc); - qlueLs.configureBackend(lc, "https://sparql.dblp.org/sparql"); - }, + languageServers: [ + { + label: "Qlue-ls", + worker: createQlueLsWorker, + onReady: (lc) => { + qlueLs.configureSettings(lc); + qlueLs.configureBackend(lc, "https://sparql.dblp.org/sparql"); + }, + }, + ], }); ``` -`qlueLs.configureBackend` is safe to call repeatedly (it skips re-registering the same endpoint). -`yasqe.getLanguageClient()` returns the underlying `monaco-languageclient`, so you can also send any -other LSP request or custom notification yourself. +::: tip Per-server vs Yasgui-level +The per-server `onEndpointChange` only fires for the active server, so each server handles endpoints +its own way. Yasgui still has a top-level `onEndpointChange(yasgui, endpoint)` for app-wide, +server-independent work (analytics, UI). Both fire. +::: + +`qlueLs.configureBackend` is safe to call repeatedly (it skips re-registering the same endpoint on +the same client). `yasqe.getLanguageClient()` returns the active `monaco-languageclient`, so you can +also send any other LSP request or custom notification yourself. + +::: tip Offering several servers +List more than one entry to let users switch at runtime (e.g. qlue-ls for QLever endpoints, another +server for large Virtuoso ones). Each entry's `worker` is resolved lazily the first time it is +activated, so unused servers are never started. The reserved `configSchema` / `configCallback` +fields are placeholders for a future generic config UI and are not yet implemented. +::: ## The `qlueLs` helpers @@ -156,39 +189,48 @@ falls back to `fallbackPrefixMap` (a broad set of common vocab prefixes) when no ## CodeMirror editor (`@zazuko/yasqe-codemirror`) The Monaco editor (`@zazuko/yasqe`) is the default, but Yasgui is editor-independent: you can build -the editor factory around the CodeMirror 6 editor instead. `@zazuko/yasqe-codemirror` takes a ready -LSP **client** (rather than a worker) via `lsp: { client }`. You own the qlue-ls wiring (transport, -pull diagnostics, semantic-token highlighting) and pass the resulting `@codemirror/lsp-client` -`LSPClient` in: +the editor factory around the CodeMirror 6 editor instead. Each `languageServers` entry takes a +ready LSP **client** (rather than a worker) as `client` (instance or factory). You own the qlue-ls +wiring (transport, pull diagnostics, semantic-token highlighting) and pass the resulting +`@codemirror/lsp-client` `LSPClient` in: ```ts import Yasgui from "@zazuko/yasgui"; import Yasqe from "@zazuko/yasqe-codemirror"; import { createQlueLsClient, setQlueLsBackend } from "./qlueLsClient"; -const client = await createQlueLsClient(); -const lsp = { client }; - new Yasgui(el, { requestConfig: { endpoint }, - yasqe: (parent, conf) => new Yasqe(parent, { ...conf, lsp }), - onEndpointChange: (_yasgui, endpoint) => setQlueLsBackend(client, endpoint), + yasqe: (parent, conf) => + new Yasqe(parent, { + ...conf, + languageServers: [ + { + label: "Qlue-ls", + client: () => createQlueLsClient(), // resolved lazily on first activation + onReady: (client) => setQlueLsBackend(client, yasgui?.getTab()?.getEndpoint()), + onEndpointChange: (client, endpoint) => setQlueLsBackend(client, endpoint), + }, + ], + }), }); ``` -The completion-query templates (`defaultCompletionQueries`) used by the client live in -`@zazuko/yasgui-utils`. See `dev/codemirror.html` and `dev/qlueLsClient.ts` in the repo for the full -qlue-ls wiring (the `qlueLs` helpers above are Monaco-specific and not used here). +With two or more entries the editor shows a labelled switcher dropdown in its toolbar (left of the +format/share/run buttons). The completion-query templates (`defaultCompletionQueries`) used by the +client live in `@zazuko/yasgui-utils`. See `dev/codemirror.html` and `dev/qlueLsClient.ts` in the +repo for the full qlue-ls wiring (the `qlueLs` helpers above are Monaco-specific and not used here). ## Using a different language server -Yasqe and Yasgui only need a ready LSP `Worker`. The `qlueLs` helpers are a convenience for qlue-ls; -they are not required. To use, for example, +Yasqe and Yasgui only need a ready LSP `Worker` (Monaco) or `LSPClient` (CodeMirror). The `qlueLs` +helpers are a convenience for qlue-ls; they are not required. To use, for example, [swls](https://github.com/SemanticWebLanguageServer/swls) instead: 1. Replace `qlue-ls.worker.ts` / `qlue-ls.ts` with that server's worker and connection. -2. Pass its worker factory as `languageServerWorker`. -3. In `onEndpointChange` / `onLanguageClientReady`, send whatever that server needs to target an - endpoint (its own custom requests) via `getLanguageClient()`. +2. Add it as another `languageServers` entry (its own `worker`/`client`), alongside or instead of + qlue-ls. +3. In that entry's `onReady` / `onEndpointChange`, send whatever that server needs to target an + endpoint (its own custom requests) on the `client` you receive. No changes to the `@zazuko/*` packages are required. diff --git a/docs/docs/yasgui.md b/docs/docs/yasgui.md index c4380c7e..c5397dfd 100644 --- a/docs/docs/yasgui.md +++ b/docs/docs/yasgui.md @@ -16,22 +16,31 @@ const yasgui = new Yasgui(document.getElementById("yasgui")!, { yasqe: (parent, conf) => new Yasqe(parent, { ...conf, - languageServerWorker: createQlueLsWorker, - onLanguageClientReady: (languageClient) => qlueLs.configureSettings(languageClient), + languageServers: [ + { + label: "Qlue-ls", + worker: createQlueLsWorker, + onReady: (client) => { + qlueLs.configureSettings(client); + qlueLs.configureBackend(client, yasgui?.getTab()?.getEndpoint()); + }, + // Per-server, fires only while this server is active. + onEndpointChange: (client, endpoint) => qlueLs.configureBackend(client, endpoint), + }, + ], }), - onEndpointChange: (yasgui, endpoint) => - qlueLs.configureBackend(yasgui.yasqe?.getLanguageClient(), endpoint), }); ``` Yasgui is **editor-independent**: instead of an editor config object, you pass a factory `(parent, conf) => IYasqe` that builds the editor. This is where you choose the editor implementation -(Monaco `@zazuko/yasqe` above, or CodeMirror `@zazuko/yasqe-codemirror`) and wire in its language -server and theme. `qlueLs` is only exported from `@zazuko/yasqe` (the Monaco editor), not from +(Monaco `@zazuko/yasqe` above, or CodeMirror `@zazuko/yasqe-codemirror`) and list its language +servers and theme. `qlueLs` is only exported from `@zazuko/yasqe` (the Monaco editor), not from `@zazuko/yasgui`. -`yasgui.yasqe.getLanguageClient()` returns the underlying `monaco-languageclient` so you can send any -LSP request. +`yasgui.yasqe.getLanguageClient()` returns the active language client so you can send any LSP request. +When two or more `languageServers` are configured, a switcher lets users pick one and Yasgui +remembers the choice **per endpoint**, so each endpoint reopens with its preferred server. ## Configuration diff --git a/docs/docs/yasqe.md b/docs/docs/yasqe.md index aae1f58f..d6b9b872 100644 --- a/docs/docs/yasqe.md +++ b/docs/docs/yasqe.md @@ -12,19 +12,30 @@ const endpoint = "https://sparql.dblp.org/sparql"; const yasqe = new Yasqe(document.getElementById("yasqe")!, { value: "SELECT * WHERE { ?s ?p ?o } LIMIT 10", requestConfig: { endpoint }, - languageServerWorker: createQlueLsWorker, - onLanguageClientReady: (languageClient) => { - qlueLs.configureSettings(languageClient); - qlueLs.configureBackend(languageClient, endpoint); - }, + languageServers: [ + { + label: "Qlue-ls", + worker: createQlueLsWorker, + onReady: (client) => { + qlueLs.configureSettings(client); + qlueLs.configureBackend(client, endpoint); + }, + // Per-server, fires only while this server is active; trigger it with notifyEndpointChange(). + onEndpointChange: (client, endpoint) => qlueLs.configureBackend(client, endpoint), + }, + ], }); yasqe.on("query", (yasqe, req) => console.log("running", req)); yasqe.on("queryResponse", (yasqe, response, duration) => console.log(response, duration)); ``` -Without `languageServerWorker`, Yasqe still works as a syntax-highlighted editor, you just don't get -completion, diagnostics or formatting. +With an empty `languageServers`, Yasqe still works as a syntax-highlighted editor, you just don't get +completion, diagnostics or formatting. List more than one server to let users switch between them at +runtime (right-click the editor); the active server is exposed via `yasqe.getLanguageClient()` and +the choice can be changed programmatically with `yasqe.setLanguageServer(labelOrIndex)`. Each entry's +`onReady` / `onEndpointChange` hooks fire only while that server is active; standalone consumers +trigger the latter with `yasqe.notifyEndpointChange(endpoint)` (Yasgui calls it for you). ::: warning Events are instance-first Yasqe events are emitted **instance-first**, handlers receive `(yasqeInstance, ...payload)`. For @@ -43,5 +54,4 @@ example `queryResponse` is `(yasqe, response, duration)`. | `resizeable` | whether the editor can be resized | | `showQueryButton` | show the run button | | `persistenceId` | localStorage namespace | -| `languageServerWorker` | the LSP worker; omit for highlighting-only | -| `onLanguageClientReady` | `(languageClient) => void`, fired once the LSP client is connected | +| `languageServers` | array of language servers (`{ label, description?, worker, onReady?, onEndpointChange? }`); empty for highlighting-only. The first is activated on load; 2+ adds a switcher. The `onReady`/`onEndpointChange` hooks fire only for the active server | diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index a40a17f6..8a60a963 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -14,6 +14,7 @@ export type { Prefixes, IYasqe, YasqeFactory, + LanguageServerInfo, SparqlFoldingRange, } from "./yasqe"; export { @@ -24,7 +25,10 @@ export { getQueryMode, getSparqlBlockFoldingRanges, } from "./yasqe"; -export * from "./languageServers/qlueLs"; +// export * from "./languageServers/qlueLs"; +// Also expose the qlue-ls helpers as a namespace so editors can re-export `qlueLs` (e.g. the +// `import { qlueLs } from "@zazuko/yasqe"` convenience) without duplicating the logic per editor. +export * as qlueLs from "./languageServers/qlueLs"; export * from "./errorNotification"; export * from "./sparql"; diff --git a/packages/utils/src/languageServers/qlueLs.ts b/packages/utils/src/languageServers/qlueLs.ts index 3a8563b2..2bd4c254 100644 --- a/packages/utils/src/languageServers/qlueLs.ts +++ b/packages/utils/src/languageServers/qlueLs.ts @@ -10,6 +10,7 @@ * * @module qlueLs */ +import type { DeepPartial } from "../types"; /** SPARQL engines qlue-ls can tailor its behaviour to. */ export type SparqlEngine = "QLever" | "GraphDB" | "Virtuoso" | "MillenniumDB" | "Blazegraph" | "Jena"; @@ -410,3 +411,63 @@ export async function createBackendConf(endpoint: string, options: BackendOption default: options.default ?? false, }; } + +/** Merge partial overrides over {@link defaultSettings}, section by section. */ +export function mergeSettings(overrides: DeepPartial = {}): Settings { + return { + format: { ...defaultSettings.format, ...overrides.format }, + completion: { ...defaultSettings.completion, ...overrides.completion }, + prefixes: { ...defaultSettings.prefixes, ...overrides.prefixes }, + }; +} + +/** + * A connected language client able to send JSON-RPC notifications. This is intentionally + * editor-agnostic: it matches both a monaco-languageclient `MonacoLanguageClient` (`sendNotification`) + * and a `@codemirror/lsp-client` `LSPClient` (`notification`), so the same qlue-ls helpers drive + * either editor without per-editor duplication. + */ +export interface QlueLsClient { + sendNotification?: (method: string, params: unknown) => unknown; + notification?: (method: string, params: unknown) => unknown; +} + +/** Send a JSON-RPC notification over whichever client API is available. */ +function notify(client: QlueLsClient, method: string, params: unknown): void { + if (typeof client.sendNotification === "function") void client.sendNotification(method, params); + else if (typeof client.notification === "function") client.notification(method, params); + else throw new Error("Unsupported qlue-ls client: no sendNotification/notification method"); +} + +/** Push qlue-ls server settings via `qlueLs/changeSettings`. Partial overrides merge over the defaults. */ +export function configureSettings(client: QlueLsClient, settings: DeepPartial = {}): void { + notify(client, "qlueLs/changeSettings", mergeSettings(settings)); +} + +// Avoid re-registering the same backend repeatedly when configureBackend is called on every change. +// Keyed per-client so switching between several qlue-ls instances (each its own client) still +// configures each one's backend, instead of a single module-global endpoint short-circuiting them +const lastBackendByClient = new WeakMap(); + +/** + * Register a SPARQL endpoint with the qlue-ls client and make it the default backend (so completions + * resolve against it). Editor-agnostic — works with both the Monaco and CodeMirror language clients. + * Safe to call repeatedly (e.g. from a server's `onReady` or an endpoint-change handler): the same + * endpoint is only registered once per client. + */ +export async function configureBackend( + client: QlueLsClient | undefined, + endpoint: string, + options: BackendOptions = {}, +): Promise { + if (!client || !endpoint || lastBackendByClient.get(client) === endpoint) return; + lastBackendByClient.set(client, endpoint); + try { + const backend = await createBackendConf(endpoint, options); + notify(client, "qlueLs/addBackend", backend); + notify(client, "qlueLs/updateDefaultBackend", { backendName: backend.name }); + } catch (error) { + lastBackendByClient.delete(client); // allow retry + console.error("Failed to configure qlue-ls backend for", endpoint, error); + } +} diff --git a/packages/utils/src/yasqe.ts b/packages/utils/src/yasqe.ts index 6728c5d8..759c3ff2 100644 --- a/packages/utils/src/yasqe.ts +++ b/packages/utils/src/yasqe.ts @@ -217,6 +217,28 @@ export interface IYasqe { /** Switch the editor theme. Optional: not every editor exposes runtime theme switching. */ setTheme?(theme: "light" | "dark"): void | Promise; destroy?(): void; + + /** + * Multiple language servers (optional). When two or more are configured the editor shows a + * switcher; Yasgui programs against these to remember the choice per endpoint. Editors emit a + * `languageServerChange` event with `(instance, { label, description }, index)` when the active + * server changes (whether by the user or programmatically). + */ + getLanguageServers?(): LanguageServerInfo[]; + /** Index of the active language server, or -1 when none is configured/active. */ + getActiveLanguageServer?(): number; + /** Activate a language server by label or index. Resolves once it is started and active. */ + setLanguageServer?(labelOrIndex: string | number): void | Promise; + /** The active language client (editor-specific type), or undefined when no server is active. */ + getLanguageClient?(): any; + /** Fire the active language server's `onEndpointChange` hook (if any) with the new endpoint. */ + notifyEndpointChange?(endpoint: string): void; +} + +/** Editor-agnostic descriptor of a language server, as surfaced by `IYasqe.getLanguageServers`. */ +export interface LanguageServerInfo { + label: string; + description?: string; } /** diff --git a/packages/yasgui/src/PersistentConfig.ts b/packages/yasgui/src/PersistentConfig.ts index 0648ab72..c92b9ca2 100644 --- a/packages/yasgui/src/PersistentConfig.ts +++ b/packages/yasgui/src/PersistentConfig.ts @@ -8,6 +8,8 @@ export interface PersistedJson { active: string | undefined; tabConfig: { [tabId: string]: Tab.PersistedJson }; lastClosedTab: { index: number; tab: Tab.PersistedJson } | undefined; + /** Preferred language server label per endpoint URL, so the right one is restored per endpoint. */ + languageServerByEndpoint: { [endpoint: string]: string }; } function getDefaults(): PersistedJson { return { @@ -16,6 +18,7 @@ function getDefaults(): PersistedJson { active: undefined, tabConfig: {}, lastClosedTab: undefined, + languageServerByEndpoint: {}, }; } @@ -55,6 +58,17 @@ export default class PersistentConfig { public getEndpointHistory() { return this.persistedJson.endpointHistory; } + /** The language server label the user last picked for this endpoint, if any. */ + public getLanguageServerForEndpoint(endpoint: string): string | undefined { + return this.persistedJson.languageServerByEndpoint[endpoint]; + } + /** Remember the language server label chosen for this endpoint and persist it. */ + public setLanguageServerForEndpoint(endpoint: string, label: string) { + if (!endpoint || !label) return; + if (this.persistedJson.languageServerByEndpoint[endpoint] === label) return; + this.persistedJson.languageServerByEndpoint[endpoint] = label; + this.toStorage(); + } public retrieveLastClosedTab() { const tabCopy = this.persistedJson.lastClosedTab; if (tabCopy === undefined) return tabCopy; @@ -92,7 +106,7 @@ export default class PersistentConfig { this.storageId, this.persistedJson, this.yasgui.config.persistencyExpire, - this.handleLocalStorageQuotaFull + this.handleLocalStorageQuotaFull, ); } private fromStorage(): PersistedJson { @@ -103,6 +117,9 @@ export default class PersistentConfig { if (!this.persistedJson.endpointHistory) { this.persistedJson.endpointHistory = []; } + if (!this.persistedJson.languageServerByEndpoint) { + this.persistedJson.languageServerByEndpoint = {}; + } return this.persistedJson; } diff --git a/packages/yasgui/src/index.ts b/packages/yasgui/src/index.ts index 51ac3305..159ff63a 100644 --- a/packages/yasgui/src/index.ts +++ b/packages/yasgui/src/index.ts @@ -111,9 +111,13 @@ export class Yasgui extends EventEmitter { public yasqe: IYasqe | undefined; private yasqeWrapperEl: HTMLDivElement | undefined; // The tab that initiated the in-flight query, so async query events route back to it even if the - // user switches tabs in the meantime. + // user switches tabs in the meantime private queryingTab: Tab | undefined; + // True while we programmatically restore a per-endpoint language-server preference, + // so the resulting languageServerChange event isn't persisted back (redundant write) + private applyingStoredLs = false; public static Tab = Tab; + constructor(parent: HTMLElement, config: PartialConfig = {}) { super(); if (!parent) throw new Error("No parent passed as argument. Dont know where to draw Yasgui"); @@ -209,7 +213,33 @@ export class Yasgui extends EventEmitter { /** Notify the consumer that the active endpoint changed (single Yasgui-level hook for all tabs). */ public emitEndpointChange(endpoint: string) { - if (endpoint) this.config.onEndpointChange?.(this, endpoint); + if (!endpoint) return; + this.config.onEndpointChange?.(this, endpoint); + const switching = this.applyStoredLanguageServer(endpoint); + // When we switch servers, the new server's `onReady` already configures it for this endpoint; + // otherwise fire the active server's per-entry `onEndpointChange` hook. + if (!switching) this.yasqe?.notifyEndpointChange?.(endpoint); + } + + /** + * Apply the stored per-endpoint language-server preference to the shared editor, if any. + * Returns true when it triggered a switch to a *different* server. + */ + private applyStoredLanguageServer(endpoint: string): boolean { + const yasqe = this.yasqe; + if (!yasqe?.setLanguageServer || !yasqe.getLanguageServers) return false; + const label = this.persistentConfig.getLanguageServerForEndpoint(endpoint); + if (!label) return false; + const servers = yasqe.getLanguageServers(); + // Only switch if that server is configured and is not already the active one. + if (!servers.some((s) => s.label === label)) return false; + const activeIdx = yasqe.getActiveLanguageServer?.() ?? -1; + if (activeIdx >= 0 && servers[activeIdx]?.label === label) return false; + this.applyingStoredLs = true; + void Promise.resolve(yasqe.setLanguageServer(label)).finally(() => { + this.applyingStoredLs = false; + }); + return true; } private setupGlobalYasqeListeners() { @@ -236,6 +266,12 @@ export class Yasgui extends EventEmitter { (this.queryingTab || this.getActiveTab())?.handleQueryResponse(yasqe, response, duration); this.queryingTab = undefined; }); + // Remember the user's language-server choice per endpoint + yasqe.on("languageServerChange", (_yasqe: any, def: { label: string }) => { + if (this.applyingStoredLs) return; + const endpoint = this.getActiveTab()?.getEndpoint(); + if (endpoint && def?.label) this.persistentConfig.setLanguageServerForEndpoint(endpoint, def.label); + }); } public getActiveTab(): Tab | undefined { diff --git a/packages/yasqe-codemirror/src/defaults.ts b/packages/yasqe-codemirror/src/defaults.ts index dd02ec9a..87a705a6 100644 --- a/packages/yasqe-codemirror/src/defaults.ts +++ b/packages/yasqe-codemirror/src/defaults.ts @@ -20,6 +20,8 @@ export default function get() { readOnly: false, syntaxErrorCheck: true, extensions: [], + // Language servers are consumer-provided; none by default (Yasqe is then a plain text editor) + languageServers: [], // Follow the OS/browser preference by default so the editor matches the auto-adapting chrome. // Callers can override by passing `theme` explicitly or via Yasqe.setTheme(). theme: diff --git a/packages/yasqe-codemirror/src/index.ts b/packages/yasqe-codemirror/src/index.ts index c42f29f8..dbbac475 100644 --- a/packages/yasqe-codemirror/src/index.ts +++ b/packages/yasqe-codemirror/src/index.ts @@ -1,10 +1,11 @@ /** * Yasqe (CodeMirror 6 edition) · the standalone CodeMirror-based SPARQL query editor. * - * Yasqe is language-server agnostic. The embedder creates and connects an `LSPClient` - * (with `@codemirror/lsp-client`) and passes it via `config.lsp`. All language features - * (highlighting, diagnostics, completion, hover, formatting) come from the language server; - * Yasqe ships no SPARQL grammar of its own. + * Yasqe is language-server agnostic. The embedder creates and connects one or more `LSPClient`s + * (with `@codemirror/lsp-client`) and lists them via `config.languageServers`. All language + * features (highlighting, diagnostics, completion, hover, formatting) come from the active server; + * Yasqe ships no SPARQL grammar of its own. When two or more servers are configured, a switcher + * dropdown lets the user pick between them at runtime ({@link Yasqe.setLanguageServer}). * @module YasqeCodeMirror */ import "./style/yasqe.css"; @@ -171,8 +172,22 @@ export class Yasqe extends EventEmitter implements IYasqe { private readOnlyCompartment = new Compartment(); private extensionsCompartment = new Compartment(); private themeCompartment = new Compartment(); + // Holds the active language server's CM6 extension (lint gutter + LSP plugin); reconfigured on switch. + private lspCompartment = new Compartment(); private static uriCounter = 0; private documentUri?: string; + /** Index of the active language server in `config.languageServers`, or -1 when none is active. */ + public activeLanguageServerIndex = -1; + /** The currently active LSPClient (used by format() and diagnostics). */ + private activeClient?: LSPClient; + /** Resolved LSPClient per server index, so switching back is instant (clients are heavy/WASM). */ + private lsClients = new Map(); + /** Serializes language-server switches so concurrent calls (init + a restored preference) don't race. */ + private lsSwitchQueue: Promise = Promise.resolve(); + /** The language-server switcher button (only drawn when 2+ servers are configured). */ + private lsSelectEl?: HTMLButtonElement; + /** Document-level click handler that closes the open switcher menu (removed on destroy). */ + private lsMenuOutsideClick?: (e: MouseEvent) => void; constructor(parent: HTMLElement, conf: PartialConfig = {}) { super(); @@ -184,17 +199,18 @@ export class Yasqe extends EventEmitter implements IYasqe { this.editorEl.className = "yasqe_editor"; this.rootEl.appendChild(this.editorEl); - // `lsp` (a class instance) and `extensions` (opaque CM6 objects) must not be - // deep-merged by lodash, which would clone away their prototypes. Assign them by reference. + // `languageServers` (carrying LSPClient instances / factory + callback functions) and + // `extensions` (opaque CM6 objects) must not be deep-merged by lodash, which would clone away + // their prototypes / identity. Assign them by reference. // (cast to `any` to avoid the deep type instantiation lodash.merge triggers over DeepPartial) const rawConf = conf as any; - const { lsp, extensions } = rawConf; + const { languageServers, extensions } = rawConf; const mergeableConf = { ...rawConf }; - delete mergeableConf.lsp; + delete mergeableConf.languageServers; delete mergeableConf.extensions; this.config = merge({}, Yasqe.defaults, mergeableConf) as Config; if (extensions) this.config.extensions = extensions as Extension[]; - if (lsp) this.config.lsp = lsp as Config["lsp"]; + if (languageServers) this.config.languageServers = languageServers as Config["languageServers"]; this.storage = new YStorage(Yasqe.storageNamespace); // Restore persisted query @@ -228,6 +244,9 @@ export class Yasqe extends EventEmitter implements IYasqe { this.drawButtons(); + // Activate the first configured language server (the consumer may switch between several). + if (this.config.languageServers?.length) void this.setLanguageServer(0); + if (this.config.consumeShareLink) { this.config.consumeShareLink(this); window.addEventListener("hashchange", this.handleHashChange); @@ -317,13 +336,9 @@ export class Yasqe extends EventEmitter implements IYasqe { ]), ); base.push(search({ top: true })); - if (c.lsp?.client) { - base.push(lintGutter()); - // Add `LSPClient` to the CM6 extensions, it is the single source of language features: - // highlighting, diagnostics, completion, hover, formatting... - base.push(c.lsp.client.plugin(this.getDocumentUri(), c.lsp.languageId ?? "sparql")); - this.setupLanguageServerErrorNotifications(c.lsp.client); - } + // The active language server (lint gutter + LSP plugin) lives in a compartment so it can be + // swapped at runtime via setLanguageServer. Starts empty; the first server is activated below. + base.push(this.lspCompartment.of([])); if (c.lineWrapping) base.push(EditorView.lineWrapping); base.push( EditorView.updateListener.of((u: ViewUpdate) => { @@ -382,19 +397,92 @@ export class Yasqe extends EventEmitter implements IYasqe { } /** - * The LSP document URI for this editor. Derived from `config.lsp.documentUri` - * (string or factory), falling back to an auto-generated unique URI so that - * several editors sharing one client (e.g. Yasgui tabs) get distinct URIs. + * The LSP document URI for this editor. Stable across server switches. Derived from the active + * server's `documentUri` (string or factory), falling back to an auto-generated unique URI so + * that several editors sharing one client (e.g. Yasgui tabs) get distinct URIs. */ - public getDocumentUri(): string { + public getDocumentUri(def?: LanguageServerDef): string { if (this.documentUri) return this.documentUri; - const conf = this.config.lsp?.documentUri; + const conf = def?.documentUri; if (typeof conf === "function") this.documentUri = conf(this); else if (typeof conf === "string") this.documentUri = conf; else this.documentUri = `file:///query${++Yasqe.uriCounter}.rq`; return this.documentUri; } + /* Language servers */ + /** The configured language servers, as `{ label, description }` (the switcher-facing subset). */ + public getLanguageServers(): { label: string; description?: string }[] { + return (this.config.languageServers ?? []).map((s) => ({ label: s.label, description: s.description })); + } + /** Index of the active language server in `config.languageServers`, or -1 when none is active. */ + public getActiveLanguageServer(): number { + return this.activeLanguageServerIndex; + } + /** The active `LSPClient`, or undefined when no language server is active. */ + public getLanguageClient(): LSPClient | undefined { + return this.activeClient; + } + /** + * Notify the active language server that the endpoint changed, firing only its `onEndpointChange` + * (with the active `LSPClient`). Yasgui calls this on endpoint changes; standalone consumers can + * call it themselves. No-op when no server is active or it defines no handler. + */ + public notifyEndpointChange(endpoint: string): void { + const def = this.config.languageServers?.[this.activeLanguageServerIndex]; + if (def?.onEndpointChange && this.activeClient && endpoint) { + def.onEndpointChange(this.activeClient, endpoint, this); + } + } + /** + * Activate a language server by label or index. Resolves (and caches) the target client, runs its + * `onReady`, swaps it into the editor via the LSP compartment, refreshes the switcher button and + * emits `languageServerChange`. The query/document is preserved. + */ + public setLanguageServer(target: string | number): Promise { + // Swallow a prior switch's failure so it doesn't block this one (the chain is reused). + this.lsSwitchQueue = this.lsSwitchQueue.catch(() => {}).then(() => this.activateLanguageServer(target)); + return this.lsSwitchQueue; + } + + private async activateLanguageServer(target: string | number): Promise { + const servers = this.config.languageServers ?? []; + if (!servers.length) return; + const index = typeof target === "number" ? target : servers.findIndex((s) => s.label === target); + if (index < 0 || index >= servers.length) { + console.warn("Unknown language server:", target); + return; + } + if (index === this.activeLanguageServerIndex && this.activeClient) return; + const def = servers[index]; + // Resolve (and cache) the target client. Cached clients make switching back instant. + let client = this.lsClients.get(index); + if (!client) { + client = typeof def.client === "function" ? await def.client() : def.client; + if (!client) { + console.warn("Language server provided no client:", def.label); + return; + } + this.lsClients.set(index, client); + } + this.setupLanguageServerErrorNotifications(client); + // Only (re)attach the LSP plugin when the client instance actually changes. Several entries may + // share one client (differing only by the settings their `onReady` pushes); reattaching the same + // client+document would needlessly re-open the document on the server. + const clientChanged = client !== this.activeClient; + this.activeClient = client; + this.activeLanguageServerIndex = index; + if (clientChanged) { + const uri = this.getDocumentUri(def); + this.cm.dispatch({ + effects: this.lspCompartment.reconfigure([lintGutter(), client.plugin(uri, def.languageId ?? "sparql")]), + }); + } + if (def.onReady) def.onReady(client, this); + this.updateLanguageServerDropdown(); + this.emit("languageServerChange", { label: def.label, description: def.description }, index); + } + /* Events */ /** * Emit an event, always passing this Yasqe instance as the first argument to listeners (the @@ -474,7 +562,7 @@ export class Yasqe extends EventEmitter implements IYasqe { * the returned edits. No-op when no language server is connected (Yasqe ships no formatter). */ public async format(): Promise { - const client = this.config.lsp?.client; + const client = this.activeClient; const plugin = LSPPlugin.get(this.cm); if (!client || !plugin) return; // Make sure the server has the latest document before asking it to format. @@ -552,6 +640,9 @@ export class Yasqe extends EventEmitter implements IYasqe { buttons.className = "yasqe_buttons"; this.rootEl.appendChild(buttons); + // Language-server switcher, leftmost in the button bar (only when 2+ servers are configured). + this.drawLanguageServerDropdown(buttons); + if (this.config.pluginButtons) { const pluginButtons = this.config.pluginButtons(); if (pluginButtons) { @@ -700,6 +791,70 @@ export class Yasqe extends EventEmitter implements IYasqe { } } + /** + * Draw the language-server switcher: a labelled dropdown button (showing the active server's + * label) that, when clicked, opens a menu listing each server with its label and a dimmed + * description. Only drawn when two or more servers are configured. + */ + private drawLanguageServerDropdown(buttons: HTMLElement) { + const servers = this.config.languageServers ?? []; + if (servers.length < 2) return; + const select = document.createElement("button"); + select.className = "yasqe_btn yasqe_lsSelect"; + select.title = "Select language server"; + select.setAttribute("aria-label", "Select language server"); + this.lsSelectEl = select; + buttons.appendChild(select); + + let menu: HTMLDivElement | undefined; + const closeMenu = () => { + menu?.remove(); + menu = undefined; + }; + const openMenu = () => { + menu = document.createElement("div"); + menu.className = "yasqe_lsMenu"; + servers.forEach((s, i) => { + const item = document.createElement("button"); + item.className = "yasqe_lsMenuItem" + (i === this.activeLanguageServerIndex ? " active" : ""); + const label = document.createElement("span"); + label.className = "yasqe_lsMenuLabel"; + label.textContent = s.label; + item.appendChild(label); + if (s.description) { + const desc = document.createElement("span"); + desc.className = "yasqe_lsMenuDesc"; + desc.textContent = s.description; + item.appendChild(desc); + } + item.addEventListener("click", (e) => { + e.stopPropagation(); + closeMenu(); + void this.setLanguageServer(i); + }); + menu!.appendChild(item); + }); + buttons.appendChild(menu); + }; + select.addEventListener("click", (e) => { + e.stopPropagation(); + if (menu) closeMenu(); + else openMenu(); + }); + this.lsMenuOutsideClick = (e: MouseEvent) => { + if (menu && e.target !== select && !menu.contains(e.target as Node)) closeMenu(); + }; + document.body.addEventListener("click", this.lsMenuOutsideClick, true); + this.updateLanguageServerDropdown(); + } + + /** Refresh the switcher button label to reflect the active server. */ + private updateLanguageServerDropdown() { + if (!this.lsSelectEl) return; + const active = (this.config.languageServers ?? [])[this.activeLanguageServerIndex]; + this.lsSelectEl.textContent = active?.label ?? "Language server"; + } + /* Resizer */ private drawResizer() { if (this.resizeWrapper) return; @@ -926,6 +1081,7 @@ export class Yasqe extends EventEmitter implements IYasqe { this.resizeWrapper?.removeEventListener("mousedown", this.initDrag, false); this.resizeWrapper?.removeEventListener("dblclick", this.expandEditor); window.removeEventListener("hashchange", this.handleHashChange); + if (this.lsMenuOutsideClick) document.body.removeEventListener("click", this.lsMenuOutsideClick, true); this.cm.destroy(); this.rootEl.remove(); } @@ -935,10 +1091,10 @@ export class Yasqe extends EventEmitter implements IYasqe { static defaults = getDefaults(); static Autocompleters: { [name: string]: any } = {}; static registerAutocompleter(_value: any, _enable = true): void { - // No-op: autocomplete is now provided by the language server (see `config.lsp`). + // No-op: autocomplete is now provided by the language server (see `config.languageServers`). } static forkAutocompleter(_from: string, _to: { name: string } & any, _enable = true): void { - // No-op: autocomplete is now provided by the language server (see `config.lsp`). + // No-op: autocomplete is now provided by the language server (see `config.languageServers`). } static clearStorage() { const storage = new YStorage(Yasqe.storageNamespace); @@ -977,29 +1133,20 @@ export interface Config { readOnly: boolean; /** Editor theme. Switch at runtime with {@link Yasqe.setTheme}. */ theme: "light" | "dark"; - /** @deprecated No-op. Diagnostics come from the language server (`lsp`); Yasqe ships no built-in syntax checker. */ + /** @deprecated No-op. Diagnostics come from the language server (`languageServers`); Yasqe ships no built-in syntax checker. */ syntaxErrorCheck: boolean; /** Extra CodeMirror 6 extensions (advanced) */ extensions: Extension[]; /** - * Language Server Protocol integration. Yasqe ships no SPARQL grammar of its own — all - * language features (highlighting, diagnostics, completion, hover, formatting) come from - * the server. The embedder creates and connects an `LSPClient` (from `@codemirror/lsp-client`, - * configured with `languageServerExtensions()` plus any server-specific glue such as - * semantic-token highlighting) and passes it here. qlue-ls (or any SPARQL server) lives in - * the embedder, never in Yasqe's dependencies. Without `lsp`, Yasqe is a plain text editor. + * Language Server Protocol integration. Yasqe ships no SPARQL grammar of its own, all language + * features (highlighting, diagnostics, completion, hover, formatting) come from the server. The + * embedder creates and connects each `LSPClient` (from `@codemirror/lsp-client`, configured with + * `languageServerExtensions()` plus any server-specific glue such as semantic-token highlighting) + * and lists them here. The first is activated on load; when two or more are configured a switcher + * appears (a dropdown button in the editor toolbar). qlue-ls (or any SPARQL server) lives in the + * embedder, never in Yasqe's dependencies. When empty, Yasqe is a plain text editor. */ - lsp?: { - /** A connected LSPClient instance. */ - client: LSPClient; - /** - * Document URI for this editor. Provide a function to derive a unique URI per editor - * (e.g. one per Yasgui tab). Defaults to an auto-generated unique URI. - */ - documentUri?: string | ((yasqe: Yasqe) => string); - /** LSP language id sent to the server. Defaults to "sparql". */ - languageId?: string; - }; + languageServers: LanguageServerDef[]; /** Show button to run the query */ showQueryButton: boolean; @@ -1025,6 +1172,44 @@ export interface Config { pluginButtons: (() => HTMLElement[] | HTMLElement) | undefined; prefixCcApi: string; } + +/** + * A language server the consumer makes available to Yasqe (CodeMirror edition). Yasqe stays + * language-server agnostic: the consumer creates/connects the `LSPClient` and supplies any + * server-specific setup. + */ +export interface LanguageServerDef { + /** Short name shown in the switcher (e.g. "Qlue-LS"). */ + label: string; + /** Optional longer description, shown dimmed under the label in the switcher menu. */ + description?: string; + /** A connected `LSPClient`, or a factory returning one (optionally async, e.g. after WASM init). */ + client: LSPClient | (() => LSPClient | Promise); + /** + * Called when this server becomes active (on load or when switched to), with its `LSPClient`. + * Use it for server-specific setup, e.g. registering the SPARQL backend for the current endpoint. + */ + onReady?: (client: LSPClient, yasqe: Yasqe) => void; + /** + * Called when the active endpoint changes, but only for the currently active server, with its + * `LSPClient` and the new endpoint. Use it for server-specific endpoint handling, e.g. + * re-registering the SPARQL backend. Driven by Yasgui's endpoint changes (and any caller of + * {@link Yasqe.notifyEndpointChange}). + */ + onEndpointChange?: (client: LSPClient, endpoint: string, yasqe: Yasqe) => void; + /** + * Document URI for this editor. Provide a function to derive a unique URI per editor (e.g. one + * per Yasgui tab). Defaults to an auto-generated unique URI. The URI is stable across switches. + */ + documentUri?: string | ((yasqe: Yasqe) => string); + /** LSP language id sent to the server. Defaults to "sparql". */ + languageId?: string; + /** Reserved for a generic config UI (JSON-schema describing the server's settings). Not yet implemented. */ + configSchema?: Record; + /** Reserved for a generic config UI (applies the generated JSON config to the server). Not yet implemented. */ + configCallback?: (client: LSPClient, configJson: any) => void; +} + export interface PersistentConfig { query: string; editorHeight: string; diff --git a/packages/yasqe-codemirror/src/style/buttons.css b/packages/yasqe-codemirror/src/style/buttons.css index be276702..6f387861 100644 --- a/packages/yasqe-codemirror/src/style/buttons.css +++ b/packages/yasqe-codemirror/src/style/buttons.css @@ -80,6 +80,70 @@ vertical-align: top; margin-left: 5px; } + +/* Language-server switcher */ +.yasqe .yasqe_buttons .yasqe_lsSelect { + font-size: 12px; + line-height: 1.5; + padding: 2px 22px 2px 8px; + position: relative; + max-width: 220px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.yasqe .yasqe_buttons .yasqe_lsSelect::after { + content: ""; + position: absolute; + right: 8px; + top: 50%; + margin-top: -2px; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid currentColor; +} +.yasqe .yasqe_buttons .yasqe_lsMenu { + position: absolute; + top: 28px; + left: 0; + z-index: 10; + min-width: 220px; + max-width: 360px; + background-color: #fff; + border: 1px solid #ccc; + border-radius: 2px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); + /* padding: 4px 0; */ +} +.yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuItem { + display: block; + width: 100%; + margin: 0; + padding: 6px 12px; + border: none; + border-radius: 0; + background: none; + text-align: left; + cursor: pointer; + white-space: normal; +} +.yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuItem:hover { + background-color: #ebebeb; +} +.yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuItem.active { + background-color: #e6f0fb; +} +.yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuLabel { + display: block; + font-weight: 600; + color: #333; +} +.yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuDesc { + display: block; + font-size: 11px; + color: #888; + margin-top: 2px; +} .yasqe .yasqe_buttons .yasqe_sharePopup { position: absolute; padding: 4px; @@ -200,3 +264,19 @@ background-color: #1e1e1e; color: #ddd; } +[data-theme="dark"] .yasqe .yasqe_buttons .yasqe_lsMenu { + background-color: #2a2a2a; + border-color: #444; +} +[data-theme="dark"] .yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuItem:hover { + background-color: #333; +} +[data-theme="dark"] .yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuItem.active { + background-color: #1f3a52; +} +[data-theme="dark"] .yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuLabel { + color: #ddd; +} +[data-theme="dark"] .yasqe .yasqe_buttons .yasqe_lsMenu .yasqe_lsMenuDesc { + color: #999; +} diff --git a/packages/yasqe/src/defaults.ts b/packages/yasqe/src/defaults.ts index 513b8ef2..fc105c90 100644 --- a/packages/yasqe/src/defaults.ts +++ b/packages/yasqe/src/defaults.ts @@ -57,9 +57,8 @@ SELECT * WHERE { resizeable: true, editorHeight: "300px", queryingDisabled: undefined, - // Language server is consumer-provided; no LSP by default (TextMate highlighting still works) - languageServerWorker: undefined, - onLanguageClientReady: undefined, + // Language servers are consumer-provided; none by default (Monarch highlighting still works) + languageServers: [], }; const requestConfig: PlainRequestConfig = { queryArgument: undefined, //undefined means: get query argument based on query mode diff --git a/packages/yasqe/src/editor/editorConfig.ts b/packages/yasqe/src/editor/editorConfig.ts index de29d393..353ad346 100644 --- a/packages/yasqe/src/editor/editorConfig.ts +++ b/packages/yasqe/src/editor/editorConfig.ts @@ -55,8 +55,6 @@ function registerSparqlFoldingProvider(): void { export interface MonacoEditorResult { apiWrapper: MonacoVscodeApiWrapper; editorApp: EditorApp; - /** The LSP client wrapper, or undefined when no language server worker was provided. */ - languageClient?: LanguageClientWrapper; getContent(): string; setContent(content: string): void; focus(): void; @@ -70,8 +68,63 @@ export interface SparqlThemeOverrides { } /** - * Creates a Monaco editor with SPARQL syntax highlighting. - * @param lsWorker Optional, ready language-server Worker. When given, an LSP client is connected to it. + * Connect a `monaco-languageclient` LanguageClient to a ready language-server `Worker`. Yasqe calls + * this for the active language server (it may switch between several), so it is decoupled from the + * editor setup in {@link startMonacoEditor}. The returned wrapper is already started. + */ +export async function connectLanguageClient(lsWorker: Worker): Promise { + const languageClientConfig: LanguageClientConfig = { + languageId: LANGUAGE_ID, + clientOptions: { + documentSelector: [{ language: LANGUAGE_ID }], + workspaceFolder: { + index: 0, + name: "workspace", + uri: Uri.parse("file:/"), + }, + progressOnInitialization: true, + diagnosticPullOptions: { + onChange: true, + onSave: false, + }, + // The language server returns completion labels as { label, detail } where `detail` is the + // human-readable text. Monaco glues `detail` directly onto the label with no separator, + // so we prefix it with a space here. + middleware: { + provideCompletionItem: async (document, position, context, token, next) => { + const result = await next(document, position, context, token); + if (!result) return result; + const items = Array.isArray(result) ? result : result.items; + for (const item of items) { + const label = item.label; + if (label && typeof label === "object" && label.detail && !label.detail.startsWith(" ")) { + label.detail = " " + label.detail; + } + } + return result; + }, + }, + }, + connection: { + options: { + $type: "WorkerDirect", + worker: lsWorker, + }, + }, + restartOptions: { + retries: 5, + timeout: 1000, + keepWorker: false, + }, + }; + const lcWrapper = new LanguageClientWrapper(languageClientConfig); + await lcWrapper.start(); + return lcWrapper; +} + +/** + * Creates a Monaco editor with SPARQL syntax highlighting. Language-server agnostic: the editor is + * built here, and Yasqe connects the active language client separately via {@link connectLanguageClient}. * @param editorOptions Optional Monaco editor options, deep-merged OVER the built-in defaults. * @param themeOverrides Optional partial light/dark theme objects, deep-merged OVER the built-in themes. */ @@ -79,7 +132,6 @@ export async function startMonacoEditor( container: HTMLElement, initialValue: string, theme: "light" | "dark" = "dark", - lsWorker?: Worker, editorOptions?: Record, themeOverrides?: SparqlThemeOverrides, ): Promise { @@ -123,57 +175,6 @@ export async function startMonacoEditor( const apiWrapper = new MonacoVscodeApiWrapper(vscodeApiConfig); await apiWrapper.start(); - // Connect a language client to the injected worker, if any - let lcWrapper: LanguageClientWrapper | undefined; - if (lsWorker) { - const languageClientConfig: LanguageClientConfig = { - languageId: LANGUAGE_ID, - clientOptions: { - documentSelector: [{ language: LANGUAGE_ID }], - workspaceFolder: { - index: 0, - name: "workspace", - uri: Uri.parse("file:/"), - }, - progressOnInitialization: true, - diagnosticPullOptions: { - onChange: true, - onSave: false, - }, - // The language server returns completion labels as { label, detail } where `detail` is the - // human-readable text. Monaco glues `detail` directly onto the label with no separator, - // so we prefix it with a space here. - middleware: { - provideCompletionItem: async (document, position, context, token, next) => { - const result = await next(document, position, context, token); - if (!result) return result; - const items = Array.isArray(result) ? result : result.items; - for (const item of items) { - const label = item.label; - if (label && typeof label === "object" && label.detail && !label.detail.startsWith(" ")) { - label.detail = " " + label.detail; - } - } - return result; - }, - }, - }, - connection: { - options: { - $type: "WorkerDirect", - worker: lsWorker, - }, - }, - restartOptions: { - retries: 5, - timeout: 1000, - keepWorker: false, - }, - }; - lcWrapper = new LanguageClientWrapper(languageClientConfig); - await lcWrapper.start(); - } - // Built-in default Monaco editor options. Consumers can override/extend any of these via the // `editorOptions` argument (deep-merged on top) const defaultEditorOptions = { @@ -243,7 +244,6 @@ export async function startMonacoEditor( return { apiWrapper, editorApp, - languageClient: lcWrapper, getContent(): string { return editorApp.getEditor()?.getValue() ?? ""; }, diff --git a/packages/yasqe/src/env.d.ts b/packages/yasqe/src/env.d.ts index 0a32b0d6..448c4c41 100644 --- a/packages/yasqe/src/env.d.ts +++ b/packages/yasqe/src/env.d.ts @@ -11,3 +11,11 @@ declare module "*?worker&url" { const workerUrl: string; export default workerUrl; } + +// Internal monaco-vscode-api modules used to render the language server right-click submenu +// (MenuRegistry/MenuId/CommandsRegistry/ContextKeyExpr). Reachable at runtime via the package's +// `./vscode/*` export (aliased to concrete files in vite.config.ts), but that export exposes no +// `types` condition, so TS can't resolve their declarations. We import them as `any`. +declare module "@codingame/monaco-vscode-api/vscode/src/vs/platform/actions/common/actions"; +declare module "@codingame/monaco-vscode-api/vscode/src/vs/platform/commands/common/commands"; +declare module "@codingame/monaco-vscode-api/vscode/src/vs/platform/contextkey/common/contextkey"; diff --git a/packages/yasqe/src/index.ts b/packages/yasqe/src/index.ts index e6261e01..f15ffa59 100644 --- a/packages/yasqe/src/index.ts +++ b/packages/yasqe/src/index.ts @@ -42,7 +42,7 @@ import "./style/buttons.css"; import type { editor } from "monaco-editor"; import { MonacoLanguageClient } from "monaco-languageclient"; export type { SparqlThemeOverrides } from "./editor/editorConfig"; -export * from "./languageServers"; +export { qlueLs } from "@zazuko/yasgui-utils"; export interface Yasqe { on(eventName: "query", handler: (instance: Yasqe, req: Request, abortController?: AbortController) => void): this; @@ -65,6 +65,14 @@ export interface Yasqe { off(eventName: "autocompletionClose", handler: (instance: Yasqe) => void): this; on(eventName: "resize", handler: (instance: Yasqe, newSize: string) => void): this; off(eventName: "resize", handler: (instance: Yasqe, newSize: string) => void): this; + on( + eventName: "languageServerChange", + handler: (instance: Yasqe, def: { label: string; description?: string }, index: number) => void, + ): this; + off( + eventName: "languageServerChange", + handler: (instance: Yasqe, def: { label: string; description?: string }, index: number) => void, + ): this; on(eventName: string, handler: () => void): this; } @@ -81,15 +89,29 @@ export class Yasqe extends EventEmitter { public editor?: editor.IStandaloneCodeEditor; /** Resolves once the Monaco editor has finished initializing (rejects if init fails). */ public ready: Promise; + /** Index of the active language server in `config.languageServers`, or -1 when none is active. */ + public activeLanguageServerIndex = -1; + /** Disposables for the context-menu language-server entries (re-created on every switch). */ + private lsMenuDisposables: { dispose(): void }[] = []; + /** Serializes language server switches so concurrent calls (init + a restored preference) don't race. */ + private lsSwitchQueue: Promise = Promise.resolve(); + /** Monaco's internal menu API, used to render the nested "Language servers" right-click submenu. + * `undefined` = not loaded yet, `null` = unavailable (then we fall back to flat context-menu actions). Loaded once, lazily. + */ + private lsMenuApi: { MenuRegistry: any; MenuId: any; CommandsRegistry: any; ContextKeyExpr: any } | null | undefined; + private lsMenuApiLoading = false; + private lsSubmenuId?: any; + private static menuInstanceCounter = 0; + private readonly menuInstanceId = Yasqe.menuInstanceCounter++; private req?: Request; private abortController?: AbortController; private queryStatus?: "valid" | "error"; private queryBtn?: HTMLButtonElement; private resizeWrapper?: HTMLDivElement; - // Value requested via setValue() before the async editor finished initializing + /** Value requested via setValue() before the async editor finished initializing */ private pendingValue?: string; - // Last height requested via setSize() + /** Last height requested via setSize() */ private currentHeight?: string; /** @@ -100,21 +122,18 @@ export class Yasqe extends EventEmitter { public async initEditor(el: HTMLElement, conf: PartialConfig = {}) { try { const { startMonacoEditor } = await import("./editor/editorConfig"); - // The language server is provided by the consumer (yasqe is LS-agnostic). Resolve the - // optional worker (instance or factory) and hand it to the editor; if none is given, - // the editor still works with TextMate syntax highlighting only. - const lsWorker = await this.resolveLanguageServerWorker(); - if (lsWorker) this.setupLanguageServerErrorNotifications(lsWorker); + // Language servers are provided by the consumer (yasqe is LS-agnostic). The editor is built + // here without a server; the active language client is connected separately (see + // setLanguageServer) so the consumer can configure several and switch between them. With none + // configured the editor still works with Monarch syntax highlighting only. const result = await startMonacoEditor( el, this.config.value, this.config.theme, - lsWorker, this.config.editorOptions, this.config.themes, ); this.editor = result.editorApp.getEditor(); - this.languageClientWrapper = result.languageClient; this.vscodeApi = result.apiWrapper; // Apply any value set via setValue() before the editor finished initializing @@ -180,13 +199,6 @@ export class Yasqe extends EventEmitter { if (this.persistentConfig && this.persistentConfig.query) this.setValue(this.persistentConfig.query); } - // Hand the (consumer-provided) language client to the consumer so it can do any - // server-specific setup (e.g. registering a SPARQL endpoint/backend for completions). - const languageClient = this.getLanguageClient(); - if (languageClient && this.config.onLanguageClientReady) { - this.config.onLanguageClientReady(languageClient, this); - } - if (this.config.consumeShareLink) { this.config.consumeShareLink(this); window.addEventListener("hashchange", this.handleHashChange); @@ -243,22 +255,215 @@ export class Yasqe extends EventEmitter { return getPrefixesFromQuery(this.getValue()); } - /** Resolve the consumer-provided language server worker (a Worker instance or a factory). */ - private async resolveLanguageServerWorker(): Promise { - const provided = this.config.languageServerWorker; - if (!provided) return undefined; - const worker = typeof provided === "function" ? await provided() : provided; - return worker || undefined; - } - /** - * The active monaco-languageclient `LanguageClient`, or undefined if no language server worker - * was provided. Use it to send server-specific requests/notifications (yasqe stays LS-agnostic). + * The active monaco-languageclient `LanguageClient`, or undefined if no language server is + * active. Use it to send server-specific requests/notifications (yasqe stays LS-agnostic). */ public getLanguageClient(): MonacoLanguageClient | undefined { return this.languageClientWrapper?.getLanguageClient?.(); } + /** The configured language servers, as `{ label, description }` (the switcher-facing subset). */ + public getLanguageServers(): { label: string; description?: string }[] { + return (this.config.languageServers ?? []).map((s) => ({ label: s.label, description: s.description })); + } + + /** Index of the active language server in `config.languageServers`, or -1 when none is active. */ + public getActiveLanguageServer(): number { + return this.activeLanguageServerIndex; + } + + /** + * Notify the active language server that the endpoint changed, firing only its `onEndpointChange` + * (with the active `LanguageClient`). Yasgui calls this on endpoint changes; standalone consumers + * can call it themselves. No-op when no server is active or it defines no handler. + */ + public notifyEndpointChange(endpoint: string): void { + const def = this.config.languageServers?.[this.activeLanguageServerIndex]; + const client = this.getLanguageClient(); + if (def?.onEndpointChange && client && endpoint) def.onEndpointChange(client, endpoint, this); + } + + /** + * Activate a language server by label or index. Disposes the current language client (its worker + * is terminated), resolves and connects the target server's worker, runs its `onReady`, refreshes + * the context-menu switcher and emits `languageServerChange`. The query/editor model is preserved. + * Switches are serialized so concurrent calls (e.g. init + a restored preference) run in order. + */ + public setLanguageServer(target: string | number): Promise { + // Swallow a prior switch's failure so it doesn't block this one (the chain is reused). + this.lsSwitchQueue = this.lsSwitchQueue.catch(() => {}).then(() => this.activateLanguageServer(target)); + return this.lsSwitchQueue; + } + + private async activateLanguageServer(target: string | number): Promise { + // Wait for the editor before touching language clients / context-menu actions. + await this.ready.catch(() => {}); + const servers = this.config.languageServers ?? []; + if (!servers.length) return; + const index = typeof target === "number" ? target : servers.findIndex((s) => s.label === target); + if (index < 0 || index >= servers.length) { + console.warn("Unknown language server:", target); + return; + } + if (index === this.activeLanguageServerIndex && this.languageClientWrapper) return; + const def = servers[index]; + // Tear down the current client (restartOptions.keepWorker is false, so its worker is terminated) + if (this.languageClientWrapper) { + try { + await this.languageClientWrapper.dispose(); + } catch (error) { + console.warn("Failed to dispose the previous language client:", error); + } + this.languageClientWrapper = undefined; + } + // Resolve the target server's worker (instance or factory) and connect a language client to it. + const worker = typeof def.worker === "function" ? await def.worker() : def.worker; + if (!worker) { + console.warn("Language server provided no worker:", def.label); + return; + } + this.setupLanguageServerErrorNotifications(worker); + const { connectLanguageClient } = await import("./editor/editorConfig"); + this.languageClientWrapper = await connectLanguageClient(worker); + this.activeLanguageServerIndex = index; + const client = this.getLanguageClient(); + if (client && def.onReady) def.onReady(client, this); + this.updateLanguageServerMenu(); + this.emit("languageServerChange", { label: def.label, description: def.description }, index); + } + + /** + * Build the right-click "Language servers" switcher. Only shown when two or more servers are + * configured. Renders as a nested submenu (a single "Language servers" entry that expands to the + * right, with a native checkmark on the active server) when Monaco's internal menu API is + * reachable; otherwise falls back to a flat list of actions. + */ + private updateLanguageServerMenu() { + for (const d of this.lsMenuDisposables) { + try { + d.dispose(); + } catch { + // ignore + } + } + this.lsMenuDisposables = []; + const servers = this.config.languageServers ?? []; + if (!this.editor || servers.length < 2) return; + if (this.lsMenuApi === undefined) { + // Menu API not resolved yet: render the flat fallback now, then upgrade to the nested submenu + // once the (lazy, one-time) import resolves. Subsequent calls are synchronous. + this.buildFlatLanguageServerActions(servers); + if (!this.lsMenuApiLoading) { + this.lsMenuApiLoading = true; + void this.loadMenuApi().then((api) => { + this.lsMenuApi = api; + this.updateLanguageServerMenu(); + }); + } + return; + } + if (this.lsMenuApi) { + try { + this.buildLanguageServerSubmenu(this.lsMenuApi, servers); + return; + } catch (error) { + console.warn("Language-server submenu unavailable, using a flat menu:", error); + } + } + this.buildFlatLanguageServerActions(servers); + } + + /** Lazily import Monaco's internal menu API (shared singleton); null when it isn't reachable. */ + private async loadMenuApi(): Promise<{ + MenuRegistry: any; + MenuId: any; + CommandsRegistry: any; + ContextKeyExpr: any; + } | null> { + try { + const actions: any = await import("@codingame/monaco-vscode-api/vscode/src/vs/platform/actions/common/actions"); + const commands: any = + await import("@codingame/monaco-vscode-api/vscode/src/vs/platform/commands/common/commands"); + const contextkey: any = + await import("@codingame/monaco-vscode-api/vscode/src/vs/platform/contextkey/common/contextkey"); + if ( + actions?.MenuRegistry && + actions?.MenuId?.EditorContext && + commands?.CommandsRegistry && + contextkey?.ContextKeyExpr?.true + ) { + return { + MenuRegistry: actions.MenuRegistry, + MenuId: actions.MenuId, + CommandsRegistry: commands.CommandsRegistry, + ContextKeyExpr: contextkey.ContextKeyExpr, + }; + } + } catch { + // not reachable; caller falls back to flat actions + } + return null; + } + + /** Native nested submenu in right click menu to choose language server */ + private buildLanguageServerSubmenu( + api: { MenuRegistry: any; MenuId: any; CommandsRegistry: any; ContextKeyExpr: any }, + servers: LanguageServerDef[], + ) { + const { MenuRegistry, MenuId, CommandsRegistry, ContextKeyExpr } = api; + if (!this.lsSubmenuId) this.lsSubmenuId = new MenuId(`yasqeLanguageServers_${this.menuInstanceId}`); + const submenu = this.lsSubmenuId; + // Parent entry under the editor context menu; its title is the category label. + this.lsMenuDisposables.push( + MenuRegistry.appendMenuItem(MenuId.EditorContext, { + submenu, + title: "Language server", + group: "1_language_server", + order: 1, + }), + ); + servers.forEach((s, i) => { + const active = i === this.activeLanguageServerIndex; + const id = `yasqe.languageServer.${this.menuInstanceId}.${i}`; + this.lsMenuDisposables.push( + CommandsRegistry.registerCommand(id, () => { + this.setLanguageServer(i).catch(() => {}); + }), + ); + this.lsMenuDisposables.push( + MenuRegistry.appendMenuItem(submenu, { + command: { + id, + title: s.description ? `${s.label} · ${s.description}` : s.label, + // A constant-true condition makes the active server render with a native checkmark. + toggled: active ? ContextKeyExpr.true() : undefined, + }, + group: "navigation", + order: i, + }), + ); + }); + } + + /** Fallback flat list of editor actions (active marked with "· "), when the submenu API is absent. */ + private buildFlatLanguageServerActions(servers: LanguageServerDef[]) { + servers.forEach((s, i) => { + const active = i === this.activeLanguageServerIndex; + const label = `${active ? "✓ " : ""}${s.label}${s.description ? " · " + s.description : ""}`; + const action = this.editor?.addAction({ + id: `yasqe-language-server-${i}`, + label, + contextMenuGroupId: "1_language_server", + contextMenuOrder: i, + run: () => { + this.setLanguageServer(i).catch(() => {}); + }, + }); + if (action) this.lsMenuDisposables.push(action); + }); + } + /** * Switch the theme of the Monaco editor * @param theme - The theme to switch to ('light' or 'dark') @@ -286,12 +491,23 @@ export class Yasqe extends EventEmitter { this.rootEl.className = "yasqe"; parent.appendChild(this.rootEl); - this.config = merge({}, Yasqe.defaults, conf); + // `languageServers` carry Worker instances / factory + callback functions that lodash.merge + // would deep-clone (mangling their prototypes / identity). Assign them by reference instead. + const rawConf = conf as any; + const languageServers = rawConf.languageServers; + const mergeableConf = { ...rawConf }; + delete mergeableConf.languageServers; + this.config = merge({}, Yasqe.defaults, mergeableConf); + if (languageServers) this.config.languageServers = languageServers as LanguageServerDef[]; // Initialize the editor and then setup everything else. Exposed as `ready` so consumers can // await initialization; swallow here to avoid an unhandled rejection when they don't. this.ready = this.initEditor(this.rootEl); this.ready.catch(() => {}); + + // Activate the first configured language server. Queued now (the activation itself waits for the + // editor to be ready) so a later restored-preference switch is guaranteed to run after this one. + if (this.config.languageServers?.length) void this.setLanguageServer(0); } private handleBeforeUnload = () => { @@ -852,16 +1068,41 @@ export interface Config { */ themes: { light?: Record; dark?: Record }; /** - * The language server to connect, provided by the consumer (yasqe is language-server agnostic). - * Either a ready `Worker`, or a factory returning one (optionally async, e.g. after WASM init). - * When omitted, the editor runs with TextMate syntax highlighting only (no LSP features). + * The language servers the consumer makes available (yasqe is language-server agnostic). The + * first is activated on load; when two or more are configured a switcher appears in the editor's + * right-click context menu. Servers are started lazily (the worker is resolved only when a server + * is first activated). When empty, the editor runs with Monarch syntax highlighting only. + */ + languageServers: LanguageServerDef[]; +} + +/** + * A language server the consumer makes available to Yasqe (Monaco edition). Yasqe stays + * language-server agnostic: the consumer supplies the `Worker` and any server-specific setup. + */ +export interface LanguageServerDef { + /** Short name shown in the switcher (e.g. "Qlue-LS"). */ + label: string; + /** Optional longer description, shown dimmed next to the label. */ + description?: string; + /** A ready LSP `Worker`, or a factory returning one (optionally async, e.g. after WASM init). */ + worker: Worker | (() => Worker | Promise); + /** + * Called when this server becomes active (on load or when switched to), with its `LanguageClient`. + * Use it for server-specific setup, e.g. pushing settings and registering the SPARQL backend. */ - languageServerWorker: Worker | (() => Worker | Promise) | undefined; + onReady?: (languageClient: MonacoLanguageClient, yasqe: Yasqe) => void; /** - * Called once the language client is started, with the `LanguageClient`. Use it for any - * server-specific setup (e.g. registering a SPARQL endpoint for completions). + * Called when the active endpoint changes, but only for the currently active server, with its + * `LanguageClient` and the new endpoint. Use it for server-specific endpoint handling, e.g. + * re-registering the SPARQL backend. Driven by Yasgui's endpoint changes (and any caller of + * {@link Yasqe.notifyEndpointChange}). */ - onLanguageClientReady: ((languageClient: MonacoLanguageClient, yasqe: Yasqe) => void) | undefined; + onEndpointChange?: (languageClient: MonacoLanguageClient, endpoint: string, yasqe: Yasqe) => void; + /** Reserved for a future generic config UI (JSON-schema describing the server's settings). Not yet implemented. */ + configSchema?: Record; + /** Reserved for a future generic config UI (applies the generated JSON config to the server). Not yet implemented. */ + configCallback?: (languageClient: MonacoLanguageClient, configJson: any) => void; } export interface PersistentConfig { diff --git a/packages/yasqe/src/languageServers/index.ts b/packages/yasqe/src/languageServers/index.ts deleted file mode 100644 index 9a6720d5..00000000 --- a/packages/yasqe/src/languageServers/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Helpers to quickly set up SPARQL language servers with Yasqe. - * - * Yasqe is language-server agnostic (it only receives a ready LSP `Worker`); these modules provide - * the server-specific plumbing (settings, types, endpoint/backend registration) so consumers don't - * have to reimplement it. Each supported server is exposed under its own namespace. - * - * @example - * import { qlueLs } from "@zazuko/yasqe/languageServers"; - * new Yasqe(el, { - * languageServerWorker: createQlueLsWorker, // consumer-provided WASM worker - * onLanguageClientReady: (client) => { - * qlueLs.configureSettings(client); - * qlueLs.configureBackend(client, "https://sparql.dblp.org/sparql"); - * }, - * }); - * - * @module languageServers - */ -export * as qlueLs from "./qlue-ls"; diff --git a/packages/yasqe/src/languageServers/qlue-ls.ts b/packages/yasqe/src/languageServers/qlue-ls.ts deleted file mode 100644 index fb77512b..00000000 --- a/packages/yasqe/src/languageServers/qlue-ls.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * qlue-ls helpers (Monaco) · the `monaco-languageclient`-specific glue for the qlue-ls SPARQL - * language server (https://github.com/IoannisNezis/Qlue-ls). - * - * The editor-agnostic core (types, settings, prefix maps, completion query templates, backend - * builder) now lives in `@zazuko/yasgui-utils` and is re-exported here so `@zazuko/yasqe`'s `qlueLs` - * namespace keeps its full surface. Only the functions that talk to a concrete `MonacoLanguageClient` - * (sending `qlueLs/*` notifications) remain here. - * - * Yasqe is language-server agnostic: it only receives a ready LSP `Worker` (via the - * `languageServerWorker` config) and hands back the resulting `MonacoLanguageClient` through - * `onLanguageClientReady`. These helpers drive qlue-ls over that client. - * - * @module languageServers/qlue-ls - */ -import type { MonacoLanguageClient } from "monaco-languageclient"; -import { createBackendConf, defaultSettings } from "@zazuko/yasgui-utils"; -import type { Settings, BackendOptions } from "@zazuko/yasgui-utils"; - -// Re-export the editor-agnostic qlue-ls helpers (now in @zazuko/yasgui-utils) so existing -// `import { qlueLs } from "@zazuko/yasqe"` consumers keep access to them. -export type { - SparqlEngine, - PrefixMap, - CompletionTemplate, - CompletionQueries, - BackendConfiguration, - FormatSettings, - CompletionSettings, - PrefixesSettings, - Settings, - BackendOptions, -} from "@zazuko/yasgui-utils"; -export { - defaultSettings, - fallbackPrefixMap, - defaultCompletionQueries, - fetchPrefixMap, - createBackendConf, -} from "@zazuko/yasgui-utils"; - -/** Push qlue-ls server settings via the `qlueLs/changeSettings` notification. */ -export function configureSettings(languageClient: MonacoLanguageClient, settings: Settings = defaultSettings): void { - void languageClient.sendNotification("qlueLs/changeSettings", settings); -} - -// Avoid re-registering the same backend repeatedly when configureBackend is called on every change. -let lastBackendEndpoint: string | undefined; - -/** - * Register a SPARQL endpoint with the qlue-ls language client and make it the default backend. - * Safe to call repeatedly (e.g. from `onLanguageClientReady` or an endpoint-change handler): the - * same endpoint is only registered once. - */ -export async function configureBackend( - languageClient: MonacoLanguageClient | undefined, - endpoint: string, - options: BackendOptions = {}, -): Promise { - if (!languageClient || !endpoint || endpoint === lastBackendEndpoint) return; - lastBackendEndpoint = endpoint; - try { - const backend = await createBackendConf(endpoint, options); - void languageClient.sendNotification("qlueLs/addBackend", backend); - void languageClient.sendNotification("qlueLs/updateDefaultBackend", { backendName: backend.name }); - } catch (error) { - lastBackendEndpoint = undefined; // allow retry - console.error("Failed to configure qlue-ls backend for", endpoint, error); - } -} diff --git a/packages/yasr/src/darkmode.css b/packages/yasr/src/darkmode.css index 66606ee5..93a27971 100644 --- a/packages/yasr/src/darkmode.css +++ b/packages/yasr/src/darkmode.css @@ -119,7 +119,7 @@ html[data-theme="dark"] { .tableControls input, .tableControls select { color: var(--yr-d-text); - background: var(--yr-d-raised); + background-color: var(--yr-d-raised); border-color: var(--yr-d-border); } } @@ -227,7 +227,7 @@ html[data-theme="dark"] { .tableControls input, .tableControls select { color: var(--yr-d-text); - background: var(--yr-d-raised); + background-color: var(--yr-d-raised); border-color: var(--yr-d-border); } } diff --git a/packages/yasr/src/plugins/table/index.scss b/packages/yasr/src/plugins/table/index.scss index da4d8e0e..9e33b7c6 100644 --- a/packages/yasr/src/plugins/table/index.scss +++ b/packages/yasr/src/plugins/table/index.scss @@ -32,14 +32,14 @@ Delimiters used by datatables padding-right: 5px; flex-wrap: wrap; - .tableFilter { - margin-right: 10px; + .tableFilter, + .tableSizer { height: 100%; - padding: 3px 8px; + padding: 2px 6px; border-radius: 4px; - // Flat look matching the endpoint input (drop the native inset 3D border). border: 2px solid #ccc; box-shadow: none; + background-color: transparent; -webkit-appearance: none; appearance: none; &:focus { @@ -48,10 +48,17 @@ Delimiters used by datatables } } + .tableFilter { + margin-right: 10px; + } + .tableSizer { - height: 100%; - padding: 3px 8px; - border-radius: 4px; + cursor: pointer; + // appearance:none drops the native arrow, so draw a flat chevron and pad for it. + padding-right: 22px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23999' stroke-width='1.5'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 6px center; } .switch { display: flex; diff --git a/vite.config.ts b/vite.config.ts index 80fc8c04..bd39643f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -9,6 +9,21 @@ const isProd = process.env.NODE_ENV === "production"; // When BUILD_PACKAGE is set we build a single package otherwise vite serves the demo pages const libPackage = process.env.BUILD_PACKAGE as "yasgui" | "yasqe" | "yasqe-codemirror" | "yasr" | "utils" | undefined; +// Internal monaco-vscode-api modules used by yasqe to render the language server right-click submenu +// (MenuRegistry/MenuId/CommandsRegistry/ContextKeyExpr). They are reachable via the package's +// `./vscode/*` export, but Rolldown only resolves that reliably in lib-build mode; aliasing the bare +// specifiers to the concrete files makes BOTH the demo and lib builds resolve them, and (resolving to +// the same physical files monaco-languageclient imports) keeps a single shared menu registry. +const monacoInternalBase = "node_modules/@codingame/monaco-vscode-api/vscode/src/vs/platform"; +const monacoInternalAlias = [ + ["actions/common/actions", "actions/common/actions"], + ["commands/common/commands", "commands/common/commands"], + ["contextkey/common/contextkey", "contextkey/common/contextkey"], +].map(([sub]) => ({ + find: new RegExp(`^@codingame/monaco-vscode-api/vscode/src/vs/platform/${sub}$`), + replacement: resolve(__dirname, `${monacoInternalBase}/${sub}.js`), +})); + const alias = [ { find: /^@zazuko\/yasgui$/, replacement: resolve(__dirname, "packages/yasgui/src/index.ts") }, // Order matters: match the more specific yasqe-codemirror before the bare yasqe alias. @@ -16,6 +31,7 @@ const alias = [ { find: /^@zazuko\/yasqe$/, replacement: resolve(__dirname, "packages/yasqe/src/index.ts") }, { find: /^@zazuko\/yasr$/, replacement: resolve(__dirname, "packages/yasr/src/index.ts") }, { find: /^@zazuko\/yasgui-utils$/, replacement: resolve(__dirname, "packages/utils/src/index.ts") }, + ...monacoInternalAlias, ]; // The editor runs monaco-languageclient in `classic` mode so these `extended` service overrides are not used at runtime @@ -59,7 +75,7 @@ export default defineConfig({ rolldownOptions: usesMonaco ? { plugins: [importMetaUrlPlugin] } : undefined, }, worker: { - // Monaco and qlue-ls language-server workers are ES modules and load wasm + // Monaco and qlue-ls language server workers are ES modules and load wasm format: "es", plugins: () => [wasm()], // Emit each worker as 1 self-contained file. From 0973f78f8da4e955a2473cd3721c9bd65ce274d3 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Mon, 15 Jun 2026 19:32:32 +0200 Subject: [PATCH 29/48] test: fix tests --- dev/codemirror.html | 7 ++++--- dev/index.html | 7 ++++--- dev/yasqe.html | 9 +++++++-- docs/.vitepress/theme/components/YasguiCmDemo.vue | 7 ++++--- docs/.vitepress/theme/components/YasguiDemo.vue | 7 ++++--- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/dev/codemirror.html b/dev/codemirror.html index 2354ee3f..ec4536d5 100644 --- a/dev/codemirror.html +++ b/dev/codemirror.html @@ -35,7 +35,8 @@ let clientPromise; const getClient = () => (clientPromise ??= createQlueLsClient()); const endpointOf = () => window.yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT; - const onReady = (settings) => (client) => { + // Apply a server's settings and point it at the current endpoint when it becomes active. + const onReady = (client, settings) => { qlueLs.configureSettings(client, settings); qlueLs.configureBackend(client, endpointOf()); }; @@ -52,14 +53,14 @@ label: "Qlue-ls", description: "SPARQL language server with endpoint-powered completions", client: getClient, - onReady: onReady({ completion: { timeoutMs: 1000, resultSizeLimit: 50 } }), + onReady: (client) => onReady(client, { completion: { timeoutMs: 1000, resultSizeLimit: 50 } }), onEndpointChange, }, { label: "Qlue-ls (aligned)", description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", client: getClient, - onReady: onReady({ format: { whereNewLine: true, alignPredicates: true } }), + onReady: (client) => onReady(client, { format: { whereNewLine: true, alignPredicates: true } }), onEndpointChange, }, ], diff --git a/dev/index.html b/dev/index.html index 476cfaae..902b8bca 100644 --- a/dev/index.html +++ b/dev/index.html @@ -33,7 +33,8 @@ const theme = setupThemeToggle((t) => window.yasgui.yasqe?.setTheme(t)); const endpointOf = () => window.yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT; - const onReady = (settings) => (client) => { + // Apply a server's settings and point it at the current endpoint when it becomes active. + const onReady = (client, settings) => { qlueLs.configureSettings(client, settings); qlueLs.configureBackend(client, endpointOf()); }; @@ -55,14 +56,14 @@ label: "Qlue-ls", description: "SPARQL language server with endpoint-powered completions", worker: createQlueLsWorker, - onReady: onReady(fullSettings), + onReady: (client) => onReady(client, fullSettings), onEndpointChange, }, { label: "Qlue-ls (aligned)", description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", worker: createQlueLsWorker, - onReady: onReady(alignedSettings), + onReady: (client) => onReady(client, alignedSettings), onEndpointChange, }, ], diff --git a/dev/yasqe.html b/dev/yasqe.html index fb97217c..aa6466a3 100644 --- a/dev/yasqe.html +++ b/dev/yasqe.html @@ -28,8 +28,13 @@ const theme = setupThemeToggle((t) => window.yasqe?.setTheme(t)); window.yasqe = new Yasqe(document.getElementById("yasqe"), { theme, - languageServerWorker: createQlueLsWorker, - onLanguageClientReady: (languageClient) => qlueLs.configureBackend(languageClient, DEMO_ENDPOINT), + languageServers: [ + { + label: "Qlue-ls", + worker: createQlueLsWorker, + onReady: (client) => qlueLs.configureBackend(client, DEMO_ENDPOINT), + }, + ], }); window.yasqe.on("queryResponse", function () { console.log("response event", arguments); diff --git a/docs/.vitepress/theme/components/YasguiCmDemo.vue b/docs/.vitepress/theme/components/YasguiCmDemo.vue index 5fa8e375..936e64cf 100644 --- a/docs/.vitepress/theme/components/YasguiCmDemo.vue +++ b/docs/.vitepress/theme/components/YasguiCmDemo.vue @@ -58,7 +58,8 @@ onMounted(async () => { let clientPromise: Promise | undefined; const getClient = () => (clientPromise ??= createQlueLsClient()); const endpointOf = () => yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT; - const onReady = (settings: any) => (client: any) => { + // Apply a server's settings and point it at the current endpoint when it becomes active. + const onReady = (client: any, settings: any) => { qlueLs.configureSettings(client, settings); qlueLs.configureBackend(client, endpointOf()); }; @@ -76,14 +77,14 @@ onMounted(async () => { label: "Qlue-ls", description: "SPARQL language server with endpoint-powered completions", client: getClient, - onReady: onReady({ completion: { timeoutMs: 2000, resultSizeLimit: 50 } }), + onReady: (client: any) => onReady(client, { completion: { timeoutMs: 2000, resultSizeLimit: 50 } }), onEndpointChange, }, { label: "Qlue-ls (aligned)", description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", client: getClient, - onReady: onReady({ format: { whereNewLine: true, alignPredicates: true } }), + onReady: (client: any) => onReady(client, { format: { whereNewLine: true, alignPredicates: true } }), onEndpointChange, }, ], diff --git a/docs/.vitepress/theme/components/YasguiDemo.vue b/docs/.vitepress/theme/components/YasguiDemo.vue index e55c36fe..4c958fba 100644 --- a/docs/.vitepress/theme/components/YasguiDemo.vue +++ b/docs/.vitepress/theme/components/YasguiDemo.vue @@ -52,7 +52,8 @@ onMounted(async () => { Yasgui.Yasr.registerPlugin("Graph", GraphPlugin as any); Yasgui.Yasr.registerPlugin("Geo", GeoPlugin as any); - const onReady = (settings: any) => (client: any) => { + // Apply a server's settings and point it at the current endpoint when it becomes active. + const onReady = (client: any, settings: any) => { qlueLs.configureSettings(client, settings); qlueLs.configureBackend(client, yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT); }; @@ -73,14 +74,14 @@ onMounted(async () => { label: "Qlue-ls", description: "SPARQL language server with endpoint-powered completions", worker: createQlueLsWorker, - onReady: onReady(qlueLs.defaultSettings), + onReady: (client: any) => onReady(client, qlueLs.defaultSettings), onEndpointChange, }, { label: "Qlue-ls (aligned)", description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", worker: createQlueLsWorker, - onReady: onReady(alignedSettings), + onReady: (client: any) => onReady(client, alignedSettings), onEndpointChange, }, ], From acf18e010de524f7345f1ce25bcabc91c7d36de9 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Tue, 16 Jun 2026 08:50:04 +0200 Subject: [PATCH 30/48] fix: fix codemirror language server picker --- docs/.vitepress/theme/components/YasguiCmDemo.vue | 3 ++- docs/.vitepress/theme/qluelsCmClient.ts | 2 +- packages/utils/src/index.ts | 6 ++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/.vitepress/theme/components/YasguiCmDemo.vue b/docs/.vitepress/theme/components/YasguiCmDemo.vue index 936e64cf..331027d4 100644 --- a/docs/.vitepress/theme/components/YasguiCmDemo.vue +++ b/docs/.vitepress/theme/components/YasguiCmDemo.vue @@ -33,7 +33,8 @@ onMounted(async () => { await import("@zazuko/yasgui/style.css"); await import("@zazuko/yasqe-codemirror/style.css"); // The qlue-ls language server (WASM) and the @codemirror/lsp-client wiring live in the embedder. - const { createQlueLsClient, qlueLs } = await import("../qluelsCmClient"); + const { createQlueLsClient } = await import("../qluelsCmClient"); + const { qlueLs } = await import("@zazuko/yasgui-utils"); const { DEMO_ENDPOINT } = await import("../utils"); syncTheme(isDark.value); diff --git a/docs/.vitepress/theme/qluelsCmClient.ts b/docs/.vitepress/theme/qluelsCmClient.ts index f0a8c094..3357c985 100644 --- a/docs/.vitepress/theme/qluelsCmClient.ts +++ b/docs/.vitepress/theme/qluelsCmClient.ts @@ -11,7 +11,7 @@ import { RangeSetBuilder, StateField, StateEffect } from "@codemirror/state"; import { setDiagnostics, Diagnostic } from "@codemirror/lint"; import init, { init_language_server, listen } from "qlue-ls"; import { qlueLs } from "@zazuko/yasgui-utils"; -import type { Settings, DeepPartial } from "@zazuko/yasgui-utils"; +import type { DeepPartial } from "@zazuko/yasgui-utils"; const SEVERITY: Record = { 1: "error", 2: "warning", 3: "info", 4: "info" }; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 8a60a963..40ee0730 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -25,10 +25,8 @@ export { getQueryMode, getSparqlBlockFoldingRanges, } from "./yasqe"; -// export * from "./languageServers/qlueLs"; -// Also expose the qlue-ls helpers as a namespace so editors can re-export `qlueLs` (e.g. the -// `import { qlueLs } from "@zazuko/yasqe"` convenience) without duplicating the logic per editor. -export * as qlueLs from "./languageServers/qlueLs"; +import * as qlueLs from "./languageServers/qlueLs"; +export { qlueLs }; export * from "./errorNotification"; export * from "./sparql"; From 512cb39153164f3aa7238199fabd8209eb669515 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Fri, 19 Jun 2026 09:12:56 +0200 Subject: [PATCH 31/48] fix: fix qlueLs import --- packages/utils/src/index.ts | 6 ++++-- packages/utils/src/languageServers/qlueLs.ts | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 40ee0730..b49fc391 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -25,8 +25,10 @@ export { getQueryMode, getSparqlBlockFoldingRanges, } from "./yasqe"; -import * as qlueLs from "./languageServers/qlueLs"; -export { qlueLs }; +// Rolldown (vite 8) lazy-wraps the qlueLs module and, when it is only reachable through a +// namespace object's getters, dead-eliminates the module init, leaving `defaultSettings` undefined +import * as qlueLsNs from "./languageServers/qlueLs"; +export const qlueLs = { ...qlueLsNs }; export * from "./errorNotification"; export * from "./sparql"; diff --git a/packages/utils/src/languageServers/qlueLs.ts b/packages/utils/src/languageServers/qlueLs.ts index 2bd4c254..04275df2 100644 --- a/packages/utils/src/languageServers/qlueLs.ts +++ b/packages/utils/src/languageServers/qlueLs.ts @@ -434,8 +434,13 @@ export interface QlueLsClient { /** Send a JSON-RPC notification over whichever client API is available. */ function notify(client: QlueLsClient, method: string, params: unknown): void { - if (typeof client.sendNotification === "function") void client.sendNotification(method, params); - else if (typeof client.notification === "function") client.notification(method, params); + if (typeof client.sendNotification === "function") { + // Due to having 2 qlue-ls clients (Monaco and cm) + // monaco-languageclient's sendNotification returns a promise that REJECTS with "Client is not + // running" when the client has been disposed. configureBackend awaits a network prefix fetch, so a + // language-server switch can tear the client down mid-flight; swallow that benign rejection + void Promise.resolve(client.sendNotification(method, params)).catch(() => {}); + } else if (typeof client.notification === "function") client.notification(method, params); else throw new Error("Unsupported qlue-ls client: no sendNotification/notification method"); } From 0d82cbb10c5179db6b39cca2238fe57857636b0d Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Fri, 19 Jun 2026 09:25:25 +0200 Subject: [PATCH 32/48] fi: improve init of language servers when multiple --- packages/yasqe-codemirror/src/index.ts | 21 ++++++++++++++------- packages/yasqe/src/index.ts | 21 ++++++++++++++------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/packages/yasqe-codemirror/src/index.ts b/packages/yasqe-codemirror/src/index.ts index dbbac475..3533fa86 100644 --- a/packages/yasqe-codemirror/src/index.ts +++ b/packages/yasqe-codemirror/src/index.ts @@ -184,6 +184,10 @@ export class Yasqe extends EventEmitter implements IYasqe { private lsClients = new Map(); /** Serializes language-server switches so concurrent calls (init + a restored preference) don't race. */ private lsSwitchQueue: Promise = Promise.resolve(); + /** Index of the most recently requested language server. A queued activation whose index no longer + * matches this has been superseded (e.g. the constructor's default 0 followed by a restored + * preference); it bails before any client setup so we never start a server just to dispose it. */ + private requestedLanguageServerIndex = -1; /** The language-server switcher button (only drawn when 2+ servers are configured). */ private lsSelectEl?: HTMLButtonElement; /** Document-level click handler that closes the open switcher menu (removed on destroy). */ @@ -440,19 +444,22 @@ export class Yasqe extends EventEmitter implements IYasqe { * emits `languageServerChange`. The query/document is preserved. */ public setLanguageServer(target: string | number): Promise { + const servers = this.config.languageServers ?? []; + const index = typeof target === "number" ? target : servers.findIndex((s) => s.label === target); + if (index < 0 || index >= servers.length) { + console.warn("Unknown language server:", target); + return Promise.resolve(); + } + this.requestedLanguageServerIndex = index; // Swallow a prior switch's failure so it doesn't block this one (the chain is reused). - this.lsSwitchQueue = this.lsSwitchQueue.catch(() => {}).then(() => this.activateLanguageServer(target)); + this.lsSwitchQueue = this.lsSwitchQueue.catch(() => {}).then(() => this.activateLanguageServer(index)); return this.lsSwitchQueue; } - private async activateLanguageServer(target: string | number): Promise { + private async activateLanguageServer(index: number): Promise { const servers = this.config.languageServers ?? []; if (!servers.length) return; - const index = typeof target === "number" ? target : servers.findIndex((s) => s.label === target); - if (index < 0 || index >= servers.length) { - console.warn("Unknown language server:", target); - return; - } + if (index !== this.requestedLanguageServerIndex) return; if (index === this.activeLanguageServerIndex && this.activeClient) return; const def = servers[index]; // Resolve (and cache) the target client. Cached clients make switching back instant. diff --git a/packages/yasqe/src/index.ts b/packages/yasqe/src/index.ts index f15ffa59..83cf1e67 100644 --- a/packages/yasqe/src/index.ts +++ b/packages/yasqe/src/index.ts @@ -95,6 +95,10 @@ export class Yasqe extends EventEmitter { private lsMenuDisposables: { dispose(): void }[] = []; /** Serializes language server switches so concurrent calls (init + a restored preference) don't race. */ private lsSwitchQueue: Promise = Promise.resolve(); + /** Index of the most recently requested language server. A queued activation whose index no longer + * matches this has been superseded (e.g. the constructor's default 0 followed by a restored + * preference); it bails before any worker/client setup so we never start a server just to dispose it. */ + private requestedLanguageServerIndex = -1; /** Monaco's internal menu API, used to render the nested "Language servers" right-click submenu. * `undefined` = not loaded yet, `null` = unavailable (then we fall back to flat context-menu actions). Loaded once, lazily. */ @@ -291,21 +295,24 @@ export class Yasqe extends EventEmitter { * Switches are serialized so concurrent calls (e.g. init + a restored preference) run in order. */ public setLanguageServer(target: string | number): Promise { + const servers = this.config.languageServers ?? []; + const index = typeof target === "number" ? target : servers.findIndex((s) => s.label === target); + if (index < 0 || index >= servers.length) { + console.warn("Unknown language server:", target); + return Promise.resolve(); + } + this.requestedLanguageServerIndex = index; // Swallow a prior switch's failure so it doesn't block this one (the chain is reused). - this.lsSwitchQueue = this.lsSwitchQueue.catch(() => {}).then(() => this.activateLanguageServer(target)); + this.lsSwitchQueue = this.lsSwitchQueue.catch(() => {}).then(() => this.activateLanguageServer(index)); return this.lsSwitchQueue; } - private async activateLanguageServer(target: string | number): Promise { + private async activateLanguageServer(index: number): Promise { // Wait for the editor before touching language clients / context-menu actions. await this.ready.catch(() => {}); const servers = this.config.languageServers ?? []; if (!servers.length) return; - const index = typeof target === "number" ? target : servers.findIndex((s) => s.label === target); - if (index < 0 || index >= servers.length) { - console.warn("Unknown language server:", target); - return; - } + if (index !== this.requestedLanguageServerIndex) return; if (index === this.activeLanguageServerIndex && this.languageClientWrapper) return; const def = servers[index]; // Tear down the current client (restartOptions.keepWorker is false, so its worker is terminated) From 4ffb229cde1709b6631833bf063e5fc673315922 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Fri, 19 Jun 2026 11:37:52 +0200 Subject: [PATCH 33/48] feat: add swls and traqula language servers Co-authored-by: ajuvercr <29926645+ajuvercr@users.noreply.github.com> --- dev/index.html | 28 ++- dev/swls.worker.ts | 121 +++++++++++++ dev/traqula.worker.ts | 171 ++++++++++++++++++ dev/utils.ts | 29 +++ docs/.vitepress/config.mts | 8 +- .../theme/components/YasguiDemo.vue | 27 ++- docs/.vitepress/theme/swls.worker.ts | 121 +++++++++++++ docs/.vitepress/theme/traqula.worker.ts | 171 ++++++++++++++++++ docs/.vitepress/theme/utils.ts | 29 +++ package-lock.json | 105 ++++++++++- package.json | 5 +- packages/utils/src/languageServers/qlueLs.ts | 10 +- packages/yasqe/src/index.ts | 23 +++ 13 files changed, 804 insertions(+), 44 deletions(-) create mode 100644 dev/swls.worker.ts create mode 100644 dev/traqula.worker.ts create mode 100644 docs/.vitepress/theme/swls.worker.ts create mode 100644 docs/.vitepress/theme/traqula.worker.ts diff --git a/dev/index.html b/dev/index.html index 902b8bca..773831e0 100644 --- a/dev/index.html +++ b/dev/index.html @@ -27,23 +27,18 @@ // Monaco-based @zazuko/yasqe with the qlue-ls language server (provided by the embedder). import Yasgui from "@zazuko/yasgui"; import Yasqe, { qlueLs } from "@zazuko/yasqe"; - import { setupThemeToggle, createQlueLsWorker, DEMO_ENDPOINT } from "./utils"; + import { setupThemeToggle, createQlueLsWorker, createSwlsWorker, createTraqulaWorker, DEMO_ENDPOINT } from "./utils"; // yasgui/yasqe are LS-agnostic; to switch to another SPARQL language server only ./qluels changes. window.onpageshow = function () { const theme = setupThemeToggle((t) => window.yasgui.yasqe?.setTheme(t)); const endpointOf = () => window.yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT; - // Apply a server's settings and point it at the current endpoint when it becomes active. - const onReady = (client, settings) => { - qlueLs.configureSettings(client, settings); + // Apply qlue-ls settings and point it at the current endpoint when it becomes active. + const onReady = (client) => { + qlueLs.configureSettings(client, qlueLs.defaultSettings); qlueLs.configureBackend(client, endpointOf()); }; const onEndpointChange = (client, endpoint) => qlueLs.configureBackend(client, endpoint); - const fullSettings = qlueLs.defaultSettings; - const alignedSettings = { - ...qlueLs.defaultSettings, - format: { ...qlueLs.defaultSettings.format, whereNewLine: true, alignPredicates: true }, - }; window.yasgui = new Yasgui(document.getElementById("yasgui"), { requestConfig: { endpoint: DEMO_ENDPOINT }, @@ -56,15 +51,18 @@ label: "Qlue-ls", description: "SPARQL language server with endpoint-powered completions", worker: createQlueLsWorker, - onReady: (client) => onReady(client, fullSettings), + onReady, onEndpointChange, }, { - label: "Qlue-ls (aligned)", - description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", - worker: createQlueLsWorker, - onReady: (client) => onReady(client, alignedSettings), - onEndpointChange, + label: "swls", + description: "Semantic web language server", + worker: createSwlsWorker, + }, + { + label: "Traqula", + description: "JS SPARQL 1.2 parser (diagnostics only)", + worker: createTraqulaWorker, }, ], }), diff --git a/dev/swls.worker.ts b/dev/swls.worker.ts new file mode 100644 index 00000000..8cfdbc1b --- /dev/null +++ b/dev/swls.worker.ts @@ -0,0 +1,121 @@ +/** + * swls SPARQL language server running as a Web Worker (WASM). + * + * Consumer-side config: yasqe is language-server agnostic and just receives this worker. + * Unlike qlue-ls, swls speaks length-prefixed LSP frames (`Content-Length` headers), so this + * worker frames outgoing messages and deframes incoming bytes back into JSON-RPC objects. + * + * The WASM is loaded once up front and the worker only signals "ready" afterwards. The host waits + * for that signal before connecting a language client, so the initialize/initialized/didOpen burst + * can't arrive before the server exists (a lazy per-message import races that burst and corrupts + * message ordering, which left semantic-token highlighting unapplied). + */ +class LspMessageSplitter { + private buffer: Uint8Array = new Uint8Array(0); + private readonly asciiDecoder = new TextDecoder("ascii"); + private readonly utf8Decoder = new TextDecoder("utf-8"); + + /** + * Push raw bytes into the splitter. + * Returns zero or more complete LSP message payloads (JSON text). + */ + push(chunk: Uint8Array): string[] { + this.buffer = concat(this.buffer, chunk); + + const messages: string[] = []; + + while (true) { + const headerEnd = indexOfDoubleCRLF(this.buffer); + if (headerEnd === -1) break; + + const headerBytes = this.buffer.subarray(0, headerEnd); + const headerText = this.asciiDecoder.decode(headerBytes); + + const match = /Content-Length:\s*(\d+)/i.exec(headerText); + if (!match) { + throw new Error("Invalid LSP header: missing Content-Length"); + } + + const contentLength = Number(match[1]); + const messageStart = headerEnd + 4; + const messageEnd = messageStart + contentLength; + + if (this.buffer.length < messageEnd) break; + + const messageBytes = this.buffer.subarray(messageStart, messageEnd); + const messageText = this.utf8Decoder.decode(messageBytes); + + messages.push(messageText); + + // Consume processed bytes + this.buffer = this.buffer.subarray(messageEnd); + } + + return messages; + } +} + +/* ----------------- helpers ----------------- */ + +function concat(a: Uint8Array, b: Uint8Array): Uint8Array { + const out = new Uint8Array(a.length + b.length); + out.set(a, 0); + out.set(b, a.length); + return out; +} + +function indexOfDoubleCRLF(buf: Uint8Array): number { + for (let i = 0; i + 3 < buf.length; i++) { + if ( + buf[i] === 13 && // \r + buf[i + 1] === 10 && // \n + buf[i + 2] === 13 && + buf[i + 3] === 10 + ) { + return i; + } + } + return -1; +} + +const encoder = new TextEncoder(); +const deframer = new LspMessageSplitter(); + +/** + * swls logs through this callback as verbose structured (JSON) lines. Surface only WARN/ERROR (and + * anything that isn't a recognizable structured log) so the console isn't flooded with INFO traces. + */ +function logFromSwls(...args: unknown[]) { + const line = args[0]; + if (typeof line === "string") { + try { + const level = JSON.parse(line).level; + if (level === "INFO" || level === "DEBUG" || level === "TRACE") return; + } catch { + // not a structured log line; fall through and print it + } + } + // eslint-disable-next-line no-console + console.log(...args); +} + +(async () => { + const mod = await import("swls-wasm"); + const lsp = new mod.WasmLsp((bytes: Uint8Array) => { + // Language Server -> Language Client: deframe and forward each complete message as JSON. + for (const msg of deframer.push(bytes)) self.postMessage(JSON.parse(msg)); + }, logFromSwls); + + // Language Client -> Language Server: frame as a length-prefixed LSP message. + self.onmessage = (event) => { + const payload = typeof event.data === "string" ? event.data : JSON.stringify(event.data); + // Content-Length is a byte count; the server reads UTF-8 bytes, so measure bytes, not chars. + const framed = `Content-Length: ${encoder.encode(payload).length}\r\n\r\n${payload}`; + lsp.send(framed); + }; + + // Signal to the host that the WASM server is initialized and ready to connect. + self.postMessage({ type: "ready" }); +})(); + +export {}; diff --git a/dev/traqula.worker.ts b/dev/traqula.worker.ts new file mode 100644 index 00000000..724286c7 --- /dev/null +++ b/dev/traqula.worker.ts @@ -0,0 +1,171 @@ +/** + * Traqula SPARQL 1.2 language server running as a Web Worker. + * + * Consumer-side config: yasqe is language-server agnostic and just receives this worker. + * This is a pure-JS parser (no WASM): it runs the @traqula SPARQL 1.2 parser on every document + * change and reports syntax errors as LSP diagnostics. It provides no completions. + */ +import { defaultLexerErrorProvider, defaultParserErrorProvider } from "@traqula/chevrotain"; +import { Parser } from "@traqula/parser-sparql-1-2"; + +interface LspPosition { + line: number; + character: number; +} + +interface LspRange { + start: LspPosition; + end: LspPosition; +} + +interface LspDiagnostic { + range: LspRange; + severity: 1 | 2 | 3 | 4; + message: string; +} + +const lexerErrors: { length: number; line?: number; column?: number; message: string }[] = []; +const parserErrors: { token: any; message: string }[] = []; + +const parser = new Parser({ + lexerConfig: { + positionTracking: "full", + errorMessageProvider: Object.assign({}, defaultLexerErrorProvider, { + buildUnexpectedCharactersMessage( + fullText: string, + startOffset: number, + length: number, + line?: number, + column?: number, + mode?: string, + ): string { + const message = defaultLexerErrorProvider.buildUnexpectedCharactersMessage( + fullText, + startOffset, + length, + line, + column, + mode, + ); + lexerErrors.push({ length, line, column, message }); + return message; + }, + }), + }, + parserConfig: { + errorMessageProvider: Object.assign({}, defaultParserErrorProvider, { + buildMismatchTokenMessage(options: any): string { + const message = defaultParserErrorProvider.buildMismatchTokenMessage(options); + parserErrors.push({ token: options.actual, message }); + return message; + }, + buildNotAllInputParsedMessage(options: any): string { + const message = defaultParserErrorProvider.buildNotAllInputParsedMessage(options); + parserErrors.push({ token: options.firstRedundant, message }); + return message; + }, + buildNoViableAltMessage(options: any): string { + const message = defaultParserErrorProvider.buildNoViableAltMessage(options); + const token = options.actual?.[0] ?? options.previous; + parserErrors.push({ token, message }); + return message; + }, + buildEarlyExitMessage(options: any): string { + const message = defaultParserErrorProvider.buildEarlyExitMessage(options); + const token = options.actual?.[0] ?? options.previous; + parserErrors.push({ token, message }); + return message; + }, + }), + }, +}); + +function tokenToRange(token: any): LspRange { + const startLine = Math.max(0, (token?.startLine ?? 1) - 1); + const startChar = Math.max(0, (token?.startColumn ?? 1) - 1); + const endLine = Math.max(0, (token?.endLine ?? token?.startLine ?? 1) - 1); + // chevrotain endColumn is 1-indexed inclusive; convert to 0-indexed exclusive + const endChar = token?.endColumn ?? startChar + 1; + return { + start: { line: startLine, character: startChar }, + end: { line: endLine, character: endChar }, + }; +} + +function runParser(text: string): LspDiagnostic[] { + lexerErrors.length = 0; + parserErrors.length = 0; + + try { + parser.parse(text); + } catch { + // errors already captured by the providers above + } + + const diagnostics: LspDiagnostic[] = []; + + for (const err of lexerErrors) { + const line = Math.max(0, (err.line ?? 1) - 1); + const character = Math.max(0, (err.column ?? 1) - 1); + diagnostics.push({ + range: { + start: { line, character }, + end: { line, character: character + err.length }, + }, + severity: 1, + message: err.message, + }); + } + + for (const err of parserErrors) { + diagnostics.push({ + range: tokenToRange(err.token), + severity: 1, + message: err.message, + }); + } + + return diagnostics; +} + +onmessage = function handleIncomingMessage(event: MessageEvent) { + const msg = typeof event.data === "string" ? JSON.parse(event.data) : event.data; + const { id, method, params } = msg; + + if (method === "initialize") { + postMessage({ + jsonrpc: "2.0", + id, + result: { + capabilities: { + textDocumentSync: 1, // Full: client always sends complete document text + }, + }, + }); + } else if (method === "textDocument/didOpen") { + const { textDocument } = params; + const diagnostics = runParser(textDocument.text); + postMessage({ + jsonrpc: "2.0", + method: "textDocument/publishDiagnostics", + params: { + uri: textDocument.uri, + diagnostics, + }, + }); + } else if (method === "textDocument/didChange") { + const { textDocument, contentChanges } = params; + const text: string = contentChanges[contentChanges.length - 1].text; + const diagnostics = runParser(text); + postMessage({ + jsonrpc: "2.0", + method: "textDocument/publishDiagnostics", + params: { + uri: textDocument.uri, + diagnostics, + }, + }); + } +}; + +postMessage("ready"); diff --git a/dev/utils.ts b/dev/utils.ts index 417f2737..73f7e745 100644 --- a/dev/utils.ts +++ b/dev/utils.ts @@ -1,5 +1,7 @@ /// import QlueLsWorker from "./qluels.worker?worker"; +import SwlsWorker from "./swls.worker?worker"; +import TraqulaWorker from "./traqula.worker?worker"; /** * Create a qlue-ls language-server worker and resolve once its WASM is initialized. @@ -14,6 +16,33 @@ export function createQlueLsWorker(): Promise { }); } +/** + * Create an swls language-server worker and resolve once its WASM is loaded and it signals + * readiness. Waiting avoids racing the language client's initialize/didOpen burst against the + * WASM import (which broke message ordering and left semantic-token highlighting unapplied). + */ +export function createSwlsWorker(): Promise { + return new Promise((resolve) => { + const worker = new SwlsWorker({ name: "swls" }); + worker.onmessage = (event) => { + if (event.data?.type === "ready") resolve(worker); + }; + }); +} + +/** + * Create a Traqula language-server worker (pure-JS SPARQL 1.2 parser) and resolve once it signals + * readiness with a "ready" message. + */ +export function createTraqulaWorker(): Promise { + return new Promise((resolve) => { + const worker = new TraqulaWorker({ name: "traqula-ls" }); + worker.onmessage = (event) => { + if (event.data === "ready") resolve(worker); + }; + }); +} + /** Default SPARQL endpoint used across the demo pages. */ export const DEMO_ENDPOINT = "https://sparql.dblp.org/sparql"; diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index b2953344..feb0c741 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -80,6 +80,9 @@ export default defineConfig({ vite: { // The demo imports the @zazuko/* packages' pre-built // Run `npm run build:lib` before building/serving the docs so those bundles exist + // esnext is required because the qlue-ls / swls wasm glue emits top-level await; VitePress's + // default es2020 target rejects it (and the worker bundle inherits this target). + build: { target: "esnext" }, css: { preprocessorOptions: { scss: { api: "modern-compiler" }, @@ -110,8 +113,9 @@ export default defineConfig({ }, optimizeDeps: { esbuildOptions: { plugins: [importMetaUrlPlugin as any] }, - // The pre-built editor bundles ship their own internal chunks/assets. - exclude: ["@zazuko/yasgui", "@zazuko/yasqe", "@zazuko/yasqe-codemirror", "@zazuko/yasr", "qlue-ls"], + // The pre-built editor bundles ship their own internal chunks/assets; swls-wasm imports its + // .wasm directly (handled by vite-plugin-wasm, not esbuild dep pre-bundling). + exclude: ["@zazuko/yasgui", "@zazuko/yasqe", "@zazuko/yasqe-codemirror", "@zazuko/yasr", "qlue-ls", "swls-wasm"], }, ssr: { // The demo is client-only, so the editor deps must not enter the server bundle diff --git a/docs/.vitepress/theme/components/YasguiDemo.vue b/docs/.vitepress/theme/components/YasguiDemo.vue index 4c958fba..b2a221ef 100644 --- a/docs/.vitepress/theme/components/YasguiDemo.vue +++ b/docs/.vitepress/theme/components/YasguiDemo.vue @@ -33,7 +33,7 @@ onMounted(async () => { await import("@zazuko/yasgui/style.css"); await import("@zazuko/yasqe/style.css"); // Only the WASM worker + demo endpoint are consumer-side; the qlue-ls plumbing lives in `qlueLs`. - const { createQlueLsWorker, DEMO_ENDPOINT } = await import("../utils"); + const { createQlueLsWorker, createSwlsWorker, createTraqulaWorker, DEMO_ENDPOINT } = await import("../utils"); syncTheme(isDark.value); @@ -52,16 +52,12 @@ onMounted(async () => { Yasgui.Yasr.registerPlugin("Graph", GraphPlugin as any); Yasgui.Yasr.registerPlugin("Geo", GeoPlugin as any); - // Apply a server's settings and point it at the current endpoint when it becomes active. - const onReady = (client: any, settings: any) => { - qlueLs.configureSettings(client, settings); + // Apply qlue-ls settings and point it at the current endpoint when it becomes active. + const onReady = (client: any) => { + qlueLs.configureSettings(client, qlueLs.defaultSettings); qlueLs.configureBackend(client, yasgui?.getTab()?.getEndpoint() ?? DEMO_ENDPOINT); }; const onEndpointChange = (client: any, endpoint: string) => qlueLs.configureBackend(client, endpoint); - const alignedSettings = { - ...qlueLs.defaultSettings, - format: { ...qlueLs.defaultSettings.format, whereNewLine: true, alignPredicates: true }, - }; yasgui = new Yasgui(container.value!, { requestConfig: { endpoint: DEMO_ENDPOINT }, // The editor factory builds a Monaco Yasqe, wiring in the theme + the available LSP workers. @@ -74,15 +70,18 @@ onMounted(async () => { label: "Qlue-ls", description: "SPARQL language server with endpoint-powered completions", worker: createQlueLsWorker, - onReady: (client: any) => onReady(client, qlueLs.defaultSettings), + onReady, onEndpointChange, }, { - label: "Qlue-ls (aligned)", - description: "Same engine, alternative formatting (WHERE on its own line, aligned predicates)", - worker: createQlueLsWorker, - onReady: (client: any) => onReady(client, alignedSettings), - onEndpointChange, + label: "swls", + description: "Semantic web language server", + worker: createSwlsWorker, + }, + { + label: "Traqula", + description: "JS SPARQL 1.2 parser (diagnostics only)", + worker: createTraqulaWorker, }, ], }), diff --git a/docs/.vitepress/theme/swls.worker.ts b/docs/.vitepress/theme/swls.worker.ts new file mode 100644 index 00000000..ac264cee --- /dev/null +++ b/docs/.vitepress/theme/swls.worker.ts @@ -0,0 +1,121 @@ +/* eslint-disable no-console */ +/** + * swls SPARQL language server running as a Web Worker (WASM). + * + * Consumer-side config: yasqe is language-server agnostic and just receives this worker. + * Unlike qlue-ls, swls speaks length-prefixed LSP frames (`Content-Length` headers), so this + * worker frames outgoing messages and deframes incoming bytes back into JSON-RPC objects. + * + * The WASM is loaded once up front and the worker only signals "ready" afterwards. The host waits + * for that signal before connecting a language client, so the initialize/initialized/didOpen burst + * can't arrive before the server exists (a lazy per-message import races that burst and corrupts + * message ordering, which left semantic-token highlighting unapplied). + */ +class LspMessageSplitter { + private buffer: Uint8Array = new Uint8Array(0); + private readonly asciiDecoder = new TextDecoder("ascii"); + private readonly utf8Decoder = new TextDecoder("utf-8"); + + /** + * Push raw bytes into the splitter. + * Returns zero or more complete LSP message payloads (JSON text). + */ + push(chunk: Uint8Array): string[] { + this.buffer = concat(this.buffer, chunk); + + const messages: string[] = []; + + while (true) { + const headerEnd = indexOfDoubleCRLF(this.buffer); + if (headerEnd === -1) break; + + const headerBytes = this.buffer.subarray(0, headerEnd); + const headerText = this.asciiDecoder.decode(headerBytes); + + const match = /Content-Length:\s*(\d+)/i.exec(headerText); + if (!match) { + throw new Error("Invalid LSP header: missing Content-Length"); + } + + const contentLength = Number(match[1]); + const messageStart = headerEnd + 4; + const messageEnd = messageStart + contentLength; + + if (this.buffer.length < messageEnd) break; + + const messageBytes = this.buffer.subarray(messageStart, messageEnd); + const messageText = this.utf8Decoder.decode(messageBytes); + + messages.push(messageText); + + // Consume processed bytes + this.buffer = this.buffer.subarray(messageEnd); + } + + return messages; + } +} + +/* ----------------- helpers ----------------- */ + +function concat(a: Uint8Array, b: Uint8Array): Uint8Array { + const out = new Uint8Array(a.length + b.length); + out.set(a, 0); + out.set(b, a.length); + return out; +} + +function indexOfDoubleCRLF(buf: Uint8Array): number { + for (let i = 0; i + 3 < buf.length; i++) { + if ( + buf[i] === 13 && // \r + buf[i + 1] === 10 && // \n + buf[i + 2] === 13 && + buf[i + 3] === 10 + ) { + return i; + } + } + return -1; +} + +const encoder = new TextEncoder(); +const deframer = new LspMessageSplitter(); + +/** + * swls logs through this callback as verbose structured (JSON) lines. Surface only WARN/ERROR (and + * anything that isn't a recognizable structured log) so the console isn't flooded with INFO traces. + */ +function logFromSwls(...args: unknown[]) { + const line = args[0]; + if (typeof line === "string") { + try { + const level = JSON.parse(line).level; + if (level === "INFO" || level === "DEBUG" || level === "TRACE") return; + } catch { + // not a structured log line; fall through and print it + } + } + console.log(...args); +} + +(async () => { + const mod = await import("swls-wasm"); + const lsp = new mod.WasmLsp((bytes: Uint8Array) => { + // Language Server -> Language Client: deframe and forward each complete message as JSON. + for (const msg of deframer.push(bytes)) self.postMessage(JSON.parse(msg)); + }, logFromSwls); + + // Language Client -> Language Server: frame as a length-prefixed LSP message. + self.onmessage = (event) => { + const payload = typeof event.data === "string" ? event.data : JSON.stringify(event.data); + // Content-Length is a byte count; the server reads UTF-8 bytes, so measure bytes, not chars. + const framed = `Content-Length: ${encoder.encode(payload).length}\r\n\r\n${payload}`; + lsp.send(framed); + }; + + // Signal to the host that the WASM server is initialized and ready to connect. + self.postMessage({ type: "ready" }); +})(); + +export {}; diff --git a/docs/.vitepress/theme/traqula.worker.ts b/docs/.vitepress/theme/traqula.worker.ts new file mode 100644 index 00000000..724286c7 --- /dev/null +++ b/docs/.vitepress/theme/traqula.worker.ts @@ -0,0 +1,171 @@ +/** + * Traqula SPARQL 1.2 language server running as a Web Worker. + * + * Consumer-side config: yasqe is language-server agnostic and just receives this worker. + * This is a pure-JS parser (no WASM): it runs the @traqula SPARQL 1.2 parser on every document + * change and reports syntax errors as LSP diagnostics. It provides no completions. + */ +import { defaultLexerErrorProvider, defaultParserErrorProvider } from "@traqula/chevrotain"; +import { Parser } from "@traqula/parser-sparql-1-2"; + +interface LspPosition { + line: number; + character: number; +} + +interface LspRange { + start: LspPosition; + end: LspPosition; +} + +interface LspDiagnostic { + range: LspRange; + severity: 1 | 2 | 3 | 4; + message: string; +} + +const lexerErrors: { length: number; line?: number; column?: number; message: string }[] = []; +const parserErrors: { token: any; message: string }[] = []; + +const parser = new Parser({ + lexerConfig: { + positionTracking: "full", + errorMessageProvider: Object.assign({}, defaultLexerErrorProvider, { + buildUnexpectedCharactersMessage( + fullText: string, + startOffset: number, + length: number, + line?: number, + column?: number, + mode?: string, + ): string { + const message = defaultLexerErrorProvider.buildUnexpectedCharactersMessage( + fullText, + startOffset, + length, + line, + column, + mode, + ); + lexerErrors.push({ length, line, column, message }); + return message; + }, + }), + }, + parserConfig: { + errorMessageProvider: Object.assign({}, defaultParserErrorProvider, { + buildMismatchTokenMessage(options: any): string { + const message = defaultParserErrorProvider.buildMismatchTokenMessage(options); + parserErrors.push({ token: options.actual, message }); + return message; + }, + buildNotAllInputParsedMessage(options: any): string { + const message = defaultParserErrorProvider.buildNotAllInputParsedMessage(options); + parserErrors.push({ token: options.firstRedundant, message }); + return message; + }, + buildNoViableAltMessage(options: any): string { + const message = defaultParserErrorProvider.buildNoViableAltMessage(options); + const token = options.actual?.[0] ?? options.previous; + parserErrors.push({ token, message }); + return message; + }, + buildEarlyExitMessage(options: any): string { + const message = defaultParserErrorProvider.buildEarlyExitMessage(options); + const token = options.actual?.[0] ?? options.previous; + parserErrors.push({ token, message }); + return message; + }, + }), + }, +}); + +function tokenToRange(token: any): LspRange { + const startLine = Math.max(0, (token?.startLine ?? 1) - 1); + const startChar = Math.max(0, (token?.startColumn ?? 1) - 1); + const endLine = Math.max(0, (token?.endLine ?? token?.startLine ?? 1) - 1); + // chevrotain endColumn is 1-indexed inclusive; convert to 0-indexed exclusive + const endChar = token?.endColumn ?? startChar + 1; + return { + start: { line: startLine, character: startChar }, + end: { line: endLine, character: endChar }, + }; +} + +function runParser(text: string): LspDiagnostic[] { + lexerErrors.length = 0; + parserErrors.length = 0; + + try { + parser.parse(text); + } catch { + // errors already captured by the providers above + } + + const diagnostics: LspDiagnostic[] = []; + + for (const err of lexerErrors) { + const line = Math.max(0, (err.line ?? 1) - 1); + const character = Math.max(0, (err.column ?? 1) - 1); + diagnostics.push({ + range: { + start: { line, character }, + end: { line, character: character + err.length }, + }, + severity: 1, + message: err.message, + }); + } + + for (const err of parserErrors) { + diagnostics.push({ + range: tokenToRange(err.token), + severity: 1, + message: err.message, + }); + } + + return diagnostics; +} + +onmessage = function handleIncomingMessage(event: MessageEvent) { + const msg = typeof event.data === "string" ? JSON.parse(event.data) : event.data; + const { id, method, params } = msg; + + if (method === "initialize") { + postMessage({ + jsonrpc: "2.0", + id, + result: { + capabilities: { + textDocumentSync: 1, // Full: client always sends complete document text + }, + }, + }); + } else if (method === "textDocument/didOpen") { + const { textDocument } = params; + const diagnostics = runParser(textDocument.text); + postMessage({ + jsonrpc: "2.0", + method: "textDocument/publishDiagnostics", + params: { + uri: textDocument.uri, + diagnostics, + }, + }); + } else if (method === "textDocument/didChange") { + const { textDocument, contentChanges } = params; + const text: string = contentChanges[contentChanges.length - 1].text; + const diagnostics = runParser(text); + postMessage({ + jsonrpc: "2.0", + method: "textDocument/publishDiagnostics", + params: { + uri: textDocument.uri, + diagnostics, + }, + }); + } +}; + +postMessage("ready"); diff --git a/docs/.vitepress/theme/utils.ts b/docs/.vitepress/theme/utils.ts index 74d07a77..b4a47fba 100644 --- a/docs/.vitepress/theme/utils.ts +++ b/docs/.vitepress/theme/utils.ts @@ -1,5 +1,7 @@ /// import QlueLsWorker from "./qluels.worker?worker"; +import SwlsWorker from "./swls.worker?worker"; +import TraqulaWorker from "./traqula.worker?worker"; /** * Create a qlue-ls language-server worker and resolve once its WASM is initialized. @@ -14,6 +16,33 @@ export function createQlueLsWorker(): Promise { }); } +/** + * Create an swls language-server worker and resolve once its WASM is loaded and it signals + * readiness. Waiting avoids racing the language client's initialize/didOpen burst against the + * WASM import (which broke message ordering and left semantic-token highlighting unapplied). + */ +export function createSwlsWorker(): Promise { + return new Promise((resolve) => { + const worker = new SwlsWorker({ name: "swls" }); + worker.onmessage = (event) => { + if (event.data?.type === "ready") resolve(worker); + }; + }); +} + +/** + * Create a Traqula language-server worker (pure-JS SPARQL 1.2 parser) and resolve once it signals + * readiness with a "ready" message. + */ +export function createTraqulaWorker(): Promise { + return new Promise((resolve) => { + const worker = new TraqulaWorker({ name: "traqula-ls" }); + worker.onmessage = (event) => { + if (event.data === "ready") resolve(worker); + }; + }); +} + /** Default SPARQL endpoint used across the demo pages. */ export const DEMO_ENDPOINT = "https://sparql.dblp.org/sparql"; diff --git a/package-lock.json b/package-lock.json index 62432a25..c40015a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,10 @@ "packages/*" ], "dependencies": { - "qlue-ls": "^2.8.0" + "@traqula/chevrotain": "^1.1.0", + "@traqula/parser-sparql-1-2": "^1.1.4", + "qlue-ls": "^2.8.0", + "swls-wasm": "^0.3.1" }, "devDependencies": { "@changesets/cli": "^2.29.7", @@ -2969,7 +2972,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-2.0.1.tgz", "integrity": "sha512-uyAzpugX7KekAXAHq26m3JlUIZJOC0uSBhpnefGV5i15bevDyyejoB7I+9MKeUrzXD8OOUI3+4FeV1wwQr5ihA==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -3724,6 +3726,82 @@ "integrity": "sha512-p1aEcKC/WHpVBuFyRhXq/ie+mgO4QqCNEsdVIPUBgmNqmxV4dVfqYEpk///9vvKyranUUvrlVu4e2tdzAaXKIg==", "license": "Apache-2.0" }, + "node_modules/@traqula/chevrotain": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@traqula/chevrotain/-/chevrotain-1.1.0.tgz", + "integrity": "sha512-VpcoQKb2ZnsOQPlPO2mE9hfR0Aidp7drwTCtaXfpdRHGkXeQ5Nv4rnxmxwUoEDRzu6b/3z4n8oTd+sYhe4fAaA==", + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@traqula/core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@traqula/core/-/core-1.1.4.tgz", + "integrity": "sha512-oSY1Ig2CDKoVflW87oaLvQuPYYbkknwLjIkGE1TsMpYYcbxUiVfN5KCqP2qhRIZPdLTjWUd07HxYhZ7K07ERDw==", + "license": "MIT", + "dependencies": { + "@traqula/chevrotain": "^1.1.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@traqula/parser-sparql-1-1": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@traqula/parser-sparql-1-1/-/parser-sparql-1-1-1.1.4.tgz", + "integrity": "sha512-SRjc2aOFUiiSGHA0NhT6ESyUQmiRW972boPTW8lq+KDMSREv0gtAAP39P3KJniCzOgz5lBeQ9wjWF5MnxSkpiA==", + "license": "MIT", + "dependencies": { + "@traqula/core": "^1.1.4", + "@traqula/rules-sparql-1-1": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@traqula/parser-sparql-1-2": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@traqula/parser-sparql-1-2/-/parser-sparql-1-2-1.1.4.tgz", + "integrity": "sha512-Nlvk/wetqhFXP6s+C6Dw39TSNN/5ozp3JBSeJ+PDKbTaaf/GjSBzTukshX2j0oliui834GhWKArJJxJHerdgWg==", + "license": "MIT", + "dependencies": { + "@traqula/core": "^1.1.4", + "@traqula/parser-sparql-1-1": "^1.1.4", + "@traqula/rules-sparql-1-1": "^1.1.4", + "@traqula/rules-sparql-1-2": "^1.1.4", + "rdf-data-factory": "^2.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@traqula/rules-sparql-1-1": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@traqula/rules-sparql-1-1/-/rules-sparql-1-1-1.1.4.tgz", + "integrity": "sha512-E/IbmR7PM50yLDHlWP19amsAf2X0+KBb8lYI8lRQH92/rhVCP7QYEhSZksJ75+w6jm/l+OQvmQKolCZaMF7JHA==", + "license": "MIT", + "dependencies": { + "@traqula/chevrotain": "^1.1.0", + "@traqula/core": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@traqula/rules-sparql-1-2": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@traqula/rules-sparql-1-2/-/rules-sparql-1-2-1.1.4.tgz", + "integrity": "sha512-ooEETfE/HGOf2ufpOhwOvbZFbCCMLQ4tYTESGRvfcF+4l/mythXNeCbVXCA1CWv4JiEDmAX+myVZMtGvBijpAA==", + "license": "MIT", + "dependencies": { + "@traqula/core": "^1.1.4", + "@traqula/rules-sparql-1-1": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", @@ -3932,7 +4010,6 @@ "version": "24.10.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz", "integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -3953,7 +4030,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, "license": "MIT" }, "node_modules/@types/papaparse": { @@ -8448,6 +8524,19 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rdf-data-factory": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rdf-data-factory/-/rdf-data-factory-2.0.2.tgz", + "integrity": "sha512-WzPoYHwQYWvIP9k+7IBLY1b4nIDitzAK4mA37WumAF/Cjvu/KOtYJH9IPZnUTWNSd5K2+pq4vrcE9WZC4sRHhg==", + "license": "MIT", + "dependencies": { + "@rdfjs/types": "^2.0.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/rubensworks/" + } + }, "node_modules/read-yaml-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", @@ -9241,6 +9330,11 @@ "node": ">=8" } }, + "node_modules/swls-wasm": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/swls-wasm/-/swls-wasm-0.3.1.tgz", + "integrity": "sha512-HDYg3euyEYShZINGscoodKqyK4eyTxJ6PviCCv055Rtfe6+8l4iojPuZMriGcB2hs26RFsKTUSD6t1691VtnQA==" + }, "node_modules/tabbable": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", @@ -11021,9 +11115,12 @@ "license": "MIT", "dependencies": { "dompurify": "^3.2.4", + "lodash-es": "^4.18.1", + "query-string": "^6.10.1", "store": "^2.0.12" }, "devDependencies": { + "@types/lodash-es": "^4.17.3", "@types/node": "^22.5.4" } }, diff --git a/package.json b/package.json index c0b3be4c..557e49a7 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,9 @@ "printWidth": 120 }, "dependencies": { - "qlue-ls": "^2.8.0" + "@traqula/chevrotain": "^1.1.0", + "@traqula/parser-sparql-1-2": "^1.1.4", + "qlue-ls": "^2.8.0", + "swls-wasm": "^0.3.1" } } diff --git a/packages/utils/src/languageServers/qlueLs.ts b/packages/utils/src/languageServers/qlueLs.ts index 04275df2..6507bcbd 100644 --- a/packages/utils/src/languageServers/qlueLs.ts +++ b/packages/utils/src/languageServers/qlueLs.ts @@ -434,14 +434,8 @@ export interface QlueLsClient { /** Send a JSON-RPC notification over whichever client API is available. */ function notify(client: QlueLsClient, method: string, params: unknown): void { - if (typeof client.sendNotification === "function") { - // Due to having 2 qlue-ls clients (Monaco and cm) - // monaco-languageclient's sendNotification returns a promise that REJECTS with "Client is not - // running" when the client has been disposed. configureBackend awaits a network prefix fetch, so a - // language-server switch can tear the client down mid-flight; swallow that benign rejection - void Promise.resolve(client.sendNotification(method, params)).catch(() => {}); - } else if (typeof client.notification === "function") client.notification(method, params); - else throw new Error("Unsupported qlue-ls client: no sendNotification/notification method"); + if (typeof client.sendNotification === "function") void client.sendNotification(method, params); + else if (typeof client.notification === "function") client.notification(method, params); } /** Push qlue-ls server settings via `qlueLs/changeSettings`. Partial overrides merge over the defaults. */ diff --git a/packages/yasqe/src/index.ts b/packages/yasqe/src/index.ts index 83cf1e67..3b334bc5 100644 --- a/packages/yasqe/src/index.ts +++ b/packages/yasqe/src/index.ts @@ -336,10 +336,33 @@ export class Yasqe extends EventEmitter { this.activeLanguageServerIndex = index; const client = this.getLanguageClient(); if (client && def.onReady) def.onReady(client, this); + await this.refreshSemanticTokens(); this.updateLanguageServerMenu(); this.emit("languageServerChange", { label: def.label, description: def.description }, index); } + /** + * Force Monaco to discard the previous language server's semantic tokens and re-pull from the now + * active client. Disposing a language client does not clear the tokens it already painted, so + * after a switch the old server's colors linger (and a server without semantic tokens never + * clears them). Bouncing the model language to `plaintext` and back resets the model's + * tokenization, which re-opens the document on the new client and re-requests its tokens (or + * leaves the Monarch fallback when the new server provides none). + */ + private async refreshSemanticTokens(): Promise { + const model = this.editor?.getModel(); + if (!model) return; + const languageId = model.getLanguageId(); + if (languageId === "plaintext") return; + try { + const monaco = await import("monaco-editor"); + monaco.editor.setModelLanguage(model, "plaintext"); + monaco.editor.setModelLanguage(model, languageId); + } catch (error) { + console.warn("Failed to refresh semantic tokens after language-server switch:", error); + } + } + /** * Build the right-click "Language servers" switcher. Only shown when two or more servers are * configured. Renders as a nested submenu (a single "Language servers" entry that expands to the From d58ae8001c6e9bae74608e44ff21861eaa60d4a3 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Sun, 21 Jun 2026 09:27:32 +0200 Subject: [PATCH 34/48] fix: fix qlue-ls diagnostics --- dev/qluelsCmClient.ts | 4 +- docs/.vitepress/theme/qluelsCmClient.ts | 4 +- docs/.vitepress/theme/swls.worker.ts | 10 ----- docs/.vitepress/theme/traqula.worker.ts | 6 --- package-lock.json | 2 +- packages/utils/src/index.ts | 15 +++++++ packages/utils/src/languageServers/qlueLs.ts | 4 +- packages/yasqe/src/editor/sparqlMonarch.ts | 4 +- packages/yasqe/src/index.ts | 43 ++++++++++---------- 9 files changed, 45 insertions(+), 47 deletions(-) diff --git a/dev/qluelsCmClient.ts b/dev/qluelsCmClient.ts index 6d2b5af8..ef5b6eee 100644 --- a/dev/qluelsCmClient.ts +++ b/dev/qluelsCmClient.ts @@ -11,7 +11,7 @@ import { RangeSetBuilder, StateField, StateEffect } from "@codemirror/state"; import { setDiagnostics, Diagnostic } from "@codemirror/lint"; import init, { init_language_server, listen } from "qlue-ls"; import { qlueLs } from "@zazuko/yasgui-utils"; -import type { DeepPartial } from "@zazuko/yasgui-utils"; +import type { DeepPartial, Settings } from "@zazuko/yasgui-utils"; const SEVERITY: Record = { 1: "error", 2: "warning", 3: "info", 4: "info" }; @@ -312,7 +312,7 @@ function wasmTransport(): Transport { * `settings` overrides are merged over the shared {@link defaultSettings} and pushed via * `qlueLs/changeSettings`. Pass only the fields you want to tune; the rest follow the defaults. */ -export async function createQlueLsClient(settings: DeepPartial = {}): Promise { +export async function createQlueLsClient(settings: DeepPartial = {}): Promise { await init(); const client = new LSPClient({ extensions: [...languageServerExtensions(), pullDiagnostics(), semanticTokens()], diff --git a/docs/.vitepress/theme/qluelsCmClient.ts b/docs/.vitepress/theme/qluelsCmClient.ts index 3357c985..a8f8d6d7 100644 --- a/docs/.vitepress/theme/qluelsCmClient.ts +++ b/docs/.vitepress/theme/qluelsCmClient.ts @@ -11,7 +11,7 @@ import { RangeSetBuilder, StateField, StateEffect } from "@codemirror/state"; import { setDiagnostics, Diagnostic } from "@codemirror/lint"; import init, { init_language_server, listen } from "qlue-ls"; import { qlueLs } from "@zazuko/yasgui-utils"; -import type { DeepPartial } from "@zazuko/yasgui-utils"; +import type { DeepPartial, Settings } from "@zazuko/yasgui-utils"; const SEVERITY: Record = { 1: "error", 2: "warning", 3: "info", 4: "info" }; @@ -306,7 +306,7 @@ function wasmTransport(): Transport { * Initialise qlue-ls and return a connected LSPClient ready to pass to the CodeMirror Yasqe/Yasgui * as a language server's `client` in the `languageServers` config. */ -export async function createQlueLsClient(settings: DeepPartial = {}): Promise { +export async function createQlueLsClient(settings: DeepPartial = {}): Promise { await init(); const client = new LSPClient({ extensions: [...languageServerExtensions(), pullDiagnostics(), semanticTokens()], diff --git a/docs/.vitepress/theme/swls.worker.ts b/docs/.vitepress/theme/swls.worker.ts index ac264cee..996c079b 100644 --- a/docs/.vitepress/theme/swls.worker.ts +++ b/docs/.vitepress/theme/swls.worker.ts @@ -22,36 +22,26 @@ class LspMessageSplitter { */ push(chunk: Uint8Array): string[] { this.buffer = concat(this.buffer, chunk); - const messages: string[] = []; - while (true) { const headerEnd = indexOfDoubleCRLF(this.buffer); if (headerEnd === -1) break; - const headerBytes = this.buffer.subarray(0, headerEnd); const headerText = this.asciiDecoder.decode(headerBytes); - const match = /Content-Length:\s*(\d+)/i.exec(headerText); if (!match) { throw new Error("Invalid LSP header: missing Content-Length"); } - const contentLength = Number(match[1]); const messageStart = headerEnd + 4; const messageEnd = messageStart + contentLength; - if (this.buffer.length < messageEnd) break; - const messageBytes = this.buffer.subarray(messageStart, messageEnd); const messageText = this.utf8Decoder.decode(messageBytes); - messages.push(messageText); - // Consume processed bytes this.buffer = this.buffer.subarray(messageEnd); } - return messages; } } diff --git a/docs/.vitepress/theme/traqula.worker.ts b/docs/.vitepress/theme/traqula.worker.ts index 724286c7..3c525a86 100644 --- a/docs/.vitepress/theme/traqula.worker.ts +++ b/docs/.vitepress/theme/traqula.worker.ts @@ -95,15 +95,12 @@ function tokenToRange(token: any): LspRange { function runParser(text: string): LspDiagnostic[] { lexerErrors.length = 0; parserErrors.length = 0; - try { parser.parse(text); } catch { // errors already captured by the providers above } - const diagnostics: LspDiagnostic[] = []; - for (const err of lexerErrors) { const line = Math.max(0, (err.line ?? 1) - 1); const character = Math.max(0, (err.column ?? 1) - 1); @@ -116,7 +113,6 @@ function runParser(text: string): LspDiagnostic[] { message: err.message, }); } - for (const err of parserErrors) { diagnostics.push({ range: tokenToRange(err.token), @@ -124,14 +120,12 @@ function runParser(text: string): LspDiagnostic[] { message: err.message, }); } - return diagnostics; } onmessage = function handleIncomingMessage(event: MessageEvent) { const msg = typeof event.data === "string" ? JSON.parse(event.data) : event.data; const { id, method, params } = msg; - if (method === "initialize") { postMessage({ jsonrpc: "2.0", diff --git a/package-lock.json b/package-lock.json index c40015a9..7acc42d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@traqula/chevrotain": "^1.1.0", "@traqula/parser-sparql-1-2": "^1.1.4", - "qlue-ls": "^2.8.0", + "qlue-ls": "2.8.0", "swls-wasm": "^0.3.1" }, "devDependencies": { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index b49fc391..91ddb5ff 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -29,6 +29,21 @@ export { // namespace object's getters, dead-eliminates the module init, leaving `defaultSettings` undefined import * as qlueLsNs from "./languageServers/qlueLs"; export const qlueLs = { ...qlueLsNs }; +// `qlueLs` above is a runtime value (the spread is required so Rolldown keeps the module init), so +// it cannot be used in type position (`qlueLs.Settings`), re-export types separately from the module +export type { + Settings, + FormatSettings, + CompletionSettings, + PrefixesSettings, + PrefixMap, + BackendConfiguration, + BackendOptions, + CompletionQueries, + CompletionTemplate, + SparqlEngine, + QlueLsClient, +} from "./languageServers/qlueLs"; export * from "./errorNotification"; export * from "./sparql"; diff --git a/packages/utils/src/languageServers/qlueLs.ts b/packages/utils/src/languageServers/qlueLs.ts index 6507bcbd..1803bed6 100644 --- a/packages/utils/src/languageServers/qlueLs.ts +++ b/packages/utils/src/languageServers/qlueLs.ts @@ -436,6 +436,7 @@ export interface QlueLsClient { function notify(client: QlueLsClient, method: string, params: unknown): void { if (typeof client.sendNotification === "function") void client.sendNotification(method, params); else if (typeof client.notification === "function") client.notification(method, params); + else throw new Error("Unsupported qlue-ls client: no sendNotification/notification method"); } /** Push qlue-ls server settings via `qlueLs/changeSettings`. Partial overrides merge over the defaults. */ @@ -444,8 +445,7 @@ export function configureSettings(client: QlueLsClient, settings: DeepPartial(); /** diff --git a/packages/yasqe/src/editor/sparqlMonarch.ts b/packages/yasqe/src/editor/sparqlMonarch.ts index b24a6fda..ec46a869 100644 --- a/packages/yasqe/src/editor/sparqlMonarch.ts +++ b/packages/yasqe/src/editor/sparqlMonarch.ts @@ -137,8 +137,8 @@ export const sparqlMonarchLanguage: monaco.languages.IMonarchLanguage = { [/[?$]\w+/, "variable"], // IRIs: <...> [/<[^<>"{}|^`\s]*>/, "operator"], - // Prefixed-name prefix part (incl. trailing colon): foaf: / rdf: - [/[A-Za-z_][\w.\-]*:/, "namespace"], + // Prefixed name / CURIE + [/[A-Za-z_][\w.\-]*:[A-Za-z0-9_-]*/, "namespace"], // Numbers [/\d+(\.\d+([eE][\-+]?\d+)?)?/, "number"], // Strings (double / single quoted, with escapes) diff --git a/packages/yasqe/src/index.ts b/packages/yasqe/src/index.ts index 3b334bc5..0d9a9476 100644 --- a/packages/yasqe/src/index.ts +++ b/packages/yasqe/src/index.ts @@ -336,32 +336,31 @@ export class Yasqe extends EventEmitter { this.activeLanguageServerIndex = index; const client = this.getLanguageClient(); if (client && def.onReady) def.onReady(client, this); - await this.refreshSemanticTokens(); this.updateLanguageServerMenu(); this.emit("languageServerChange", { label: def.label, description: def.description }, index); } - /** - * Force Monaco to discard the previous language server's semantic tokens and re-pull from the now - * active client. Disposing a language client does not clear the tokens it already painted, so - * after a switch the old server's colors linger (and a server without semantic tokens never - * clears them). Bouncing the model language to `plaintext` and back resets the model's - * tokenization, which re-opens the document on the new client and re-requests its tokens (or - * leaves the Monarch fallback when the new server provides none). - */ - private async refreshSemanticTokens(): Promise { - const model = this.editor?.getModel(); - if (!model) return; - const languageId = model.getLanguageId(); - if (languageId === "plaintext") return; - try { - const monaco = await import("monaco-editor"); - monaco.editor.setModelLanguage(model, "plaintext"); - monaco.editor.setModelLanguage(model, languageId); - } catch (error) { - console.warn("Failed to refresh semantic tokens after language-server switch:", error); - } - } + // /** + // * Force Monaco to discard the previous language server's semantic tokens and re-pull from the now + // * active client. Disposing a language client does not clear the tokens it already painted, so + // * after a switch the old server's colors linger (and a server without semantic tokens never + // * clears them). Bouncing the model language to `plaintext` and back resets the model's + // * tokenization, which re-opens the document on the new client and re-requests its tokens (or + // * leaves the Monarch fallback when the new server provides none). + // */ + // private async refreshSemanticTokens(): Promise { + // const model = this.editor?.getModel(); + // if (!model) return; + // const languageId = model.getLanguageId(); + // if (languageId === "plaintext") return; + // try { + // const monaco = await import("monaco-editor"); + // monaco.editor.setModelLanguage(model, "plaintext"); + // monaco.editor.setModelLanguage(model, languageId); + // } catch (error) { + // console.warn("Failed to refresh semantic tokens after language-server switch:", error); + // } + // } /** * Build the right-click "Language servers" switcher. Only shown when two or more servers are From fe0ea55329101446af8075d1fbd922595dd48159 Mon Sep 17 00:00:00 2001 From: Vincent Emonet Date: Sun, 21 Jun 2026 09:54:44 +0200 Subject: [PATCH 35/48] feat: add generic settings panel generated from settings JSON schema, with a callback to enable applying those settings to different language servers --- dev/index.html | 2 + .../theme/components/YasguiDemo.vue | 2 + packages/utils/src/languageServers/qlueLs.ts | 145 +++++++++++- packages/yasqe/src/editor/settingsPanel.ts | 218 ++++++++++++++++++ packages/yasqe/src/index.ts | 103 ++++++++- packages/yasqe/src/style/yasqe.css | 113 +++++++++ 6 files changed, 576 insertions(+), 7 deletions(-) create mode 100644 packages/yasqe/src/editor/settingsPanel.ts diff --git a/dev/index.html b/dev/index.html index 773831e0..c0c974ec 100644 --- a/dev/index.html +++ b/dev/index.html @@ -53,6 +53,8 @@ worker: createQlueLsWorker, onReady, onEndpointChange, + configSchema: qlueLs.settingsSchema, + configCallback: (client, settings) => qlueLs.configureSettings(client, settings), }, { label: "swls", diff --git a/docs/.vitepress/theme/components/YasguiDemo.vue b/docs/.vitepress/theme/components/YasguiDemo.vue index b2a221ef..ccb34e52 100644 --- a/docs/.vitepress/theme/components/YasguiDemo.vue +++ b/docs/.vitepress/theme/components/YasguiDemo.vue @@ -72,6 +72,8 @@ onMounted(async () => { worker: createQlueLsWorker, onReady, onEndpointChange, + configSchema: qlueLs.settingsSchema, + configCallback: (client: any, settings: any) => qlueLs.configureSettings(client, settings), }, { label: "swls", diff --git a/packages/utils/src/languageServers/qlueLs.ts b/packages/utils/src/languageServers/qlueLs.ts index 1803bed6..8fafdb00 100644 --- a/packages/utils/src/languageServers/qlueLs.ts +++ b/packages/utils/src/languageServers/qlueLs.ts @@ -59,7 +59,7 @@ export interface FormatSettings { filterSameLine: boolean; lineLength: number; contractTriples: boolean; - keepEmptyLines: boolean; + autoLineBreak: boolean; } /** qlue-ls completion settings (the `completion` section of `qlueLs/changeSettings`). */ @@ -98,10 +98,10 @@ export const defaultSettings: Settings = { filterSameLine: true, lineLength: 120, contractTriples: true, - keepEmptyLines: false, + autoLineBreak: false, }, completion: { - timeoutMs: 2000, + timeoutMs: 3000, resultSizeLimit: 50, subjectCompletionTriggerLength: 3, objectCompletionSuffix: true, @@ -470,3 +470,142 @@ export async function configureBackend( console.error("Failed to configure qlue-ls backend for", endpoint, error); } } + +/** + * A small JSON-schema subset describing qlue-ls's most useful settings, ready to pass to a Yasqe + * language server's `configSchema`. Keys are dotted paths into {@link Settings} (e.g. `format.tabSize`); + * Yasqe de-flattens them into a nested object before handing it to the `configCallback`, so a callback + * of `(client, settings) => qlueLs.configureSettings(client, settings)` applies them directly. + * Defaults are sourced from {@link defaultSettings} so the panel and the server agree. + */ +export const settingsSchema = { + title: "Qlue-ls settings", + properties: { + "prefixes.addMissing": { + type: "boolean", + title: "Auto-add missing prefixes", + group: "Prefixes", + default: defaultSettings.prefixes.addMissing, + }, + "prefixes.removeUnused": { + type: "boolean", + title: "Remove unused prefixes", + group: "Prefixes", + default: defaultSettings.prefixes.removeUnused, + }, + "format.capitalizeKeywords": { + type: "boolean", + title: "Capitalize keywords", + group: "Formatting", + default: defaultSettings.format.capitalizeKeywords, + }, + "format.alignPredicates": { + type: "boolean", + title: "Align predicates", + group: "Formatting", + default: defaultSettings.format.alignPredicates, + }, + "format.alignPrefixes": { + type: "boolean", + title: "Align prefixes", + group: "Formatting", + default: defaultSettings.format.alignPrefixes, + }, + "format.separatePrologue": { + type: "boolean", + title: "Separate prologue", + group: "Formatting", + default: defaultSettings.format.separatePrologue, + }, + "format.insertSpaces": { + type: "boolean", + title: "Indent with spaces", + group: "Formatting", + default: defaultSettings.format.insertSpaces, + }, + "format.tabSize": { + type: "number", + title: "Indent size", + group: "Formatting", + minimum: 1, + maximum: 8, + default: defaultSettings.format.tabSize, + }, + "format.whereNewLine": { + type: "boolean", + title: "WHERE on new line", + group: "Formatting", + default: defaultSettings.format.whereNewLine, + }, + "format.filterSameLine": { + type: "boolean", + title: "FILTER on same line", + group: "Formatting", + default: defaultSettings.format.filterSameLine, + }, + "format.lineLength": { + type: "number", + title: "Max line length", + group: "Formatting", + minimum: 40, + maximum: 400, + default: defaultSettings.format.lineLength, + }, + "format.contractTriples": { + type: "boolean", + title: "Contract triples", + group: "Formatting", + default: defaultSettings.format.contractTriples, + }, + "format.autoLineBreak": { + type: "boolean", + title: "Auto line break", + group: "Formatting", + default: defaultSettings.format.autoLineBreak, + }, + "completion.timeoutMs": { + type: "number", + title: "Completion timeout (ms)", + group: "Completion", + minimum: 100, + maximum: 30000, + default: defaultSettings.completion.timeoutMs, + }, + "completion.resultSizeLimit": { + type: "number", + title: "Max completion results", + group: "Completion", + minimum: 1, + maximum: 1000, + default: defaultSettings.completion.resultSizeLimit, + }, + "completion.subjectCompletionTriggerLength": { + type: "number", + title: "Subject completion trigger length", + group: "Completion", + minimum: 0, + maximum: 20, + default: defaultSettings.completion.subjectCompletionTriggerLength, + }, + "completion.objectCompletionSuffix": { + type: "boolean", + title: "Object completion suffix", + group: "Completion", + default: defaultSettings.completion.objectCompletionSuffix, + }, + "completion.variableCompletionLimit": { + type: "number", + title: "Variable completion limit", + group: "Completion", + minimum: 0, + maximum: 100, + default: defaultSettings.completion.variableCompletionLimit, + }, + "completion.sameSubjectSemicolon": { + type: "boolean", + title: "Same subject semicolon", + group: "Completion", + default: defaultSettings.completion.sameSubjectSemicolon, + }, + }, +} as const; diff --git a/packages/yasqe/src/editor/settingsPanel.ts b/packages/yasqe/src/editor/settingsPanel.ts new file mode 100644 index 00000000..501732c4 --- /dev/null +++ b/packages/yasqe/src/editor/settingsPanel.ts @@ -0,0 +1,218 @@ +/** + * Generic, schema-driven settings panel for a language server (classic DOM, no Monaco UI). + * + * A language server can describe its tunable settings with a {@link LanguageServerSettingsSchema} + * (a small JSON-schema subset) via `LanguageServerDef.configSchema`. Yasqe renders this modal from + * that schema and, on Apply, hands the collected values back to `LanguageServerDef.configCallback`, + * which pushes them to the running server. Yasqe itself stays language-server agnostic: it never + * knows what the settings mean, only how to render and collect them. + */ + +/** A single configurable field. The property key may be dotted (e.g. `format.tabSize`). */ +export interface SettingFieldSchema { + /** Widget/value type: checkbox, number input, or text/select. */ + type: "boolean" | "number" | "string"; + /** Human label shown next to the field (defaults to the property key). */ + title?: string; + /** Optional helper text shown under the field. */ + description?: string; + /** Default value, used when no value has been applied yet and by the Reset button. */ + default?: boolean | number | string; + /** When set, a `string` field renders as a `