From 4cbe12cf48ff6ebd636430abf6701031e28a2930 Mon Sep 17 00:00:00 2001 From: e-filchenko-bosh Date: Mon, 20 Apr 2026 12:47:29 +0300 Subject: [PATCH 1/3] init commit --- .gitignore | 1 + extension/.gitignore | 6 + extension/.prettierrc | 9 + extension/.vscode-test.mjs | 9 + extension/.vscode/extensions.json | 8 + extension/.vscode/launch.json | 21 + extension/.vscode/settings.json | 11 + extension/.vscode/tasks.json | 20 + extension/.vscodeignore | 11 + extension/eslint.config.mjs | 27 + extension/language-configuration.json | 24 + extension/package-lock.json | 3720 +++++++++++++++++ extension/package.json | 64 + extension/samples/invalid.ttl | 5 + .../org.eclipse.esmf.test/1.0.0/Aspect.ttl | 23 + extension/samples/valid.ttl | 10 + extension/src/aspectValidation.ts | 200 + extension/src/extension.ts | 108 + .../test/aspectValidationController.test.ts | 118 + extension/src/test/validationTestHarness.ts | 124 + extension/tsconfig.json | 13 + extension/vsc-extension-quickstart.md | 17 + lsp-server/.development/esmf-checkstyle.xml | 341 ++ .../.development/esmf-eclipse-codestyle.xml | 346 ++ .../.development/esmf-intellij-codestyle.xml | 137 + .../esmf-intellij-inspections.xml | 1669 ++++++++ lsp-server/.gitattributes | 12 + lsp-server/.gitignore | 4 + lsp-server/pom.xml | 119 + .../main/java/com/example/turtlelsp/App.java | 33 + .../turtlelsp/TurtleLanguageServer.java | 80 + .../diagnostics/AspectDiagnosticMapper.java | 49 + .../aspect/model/AspectValidationError.java | 7 + .../model/AspectValidationErrorType.java | 8 + .../aspect/model/AspectValidationResult.java | 11 + .../aspect/model/AspectViolationInfo.java | 12 + .../request/ValidateDocumentParams.java | 7 + .../service/AspectModelValidationService.java | 14 + .../service/AspectValidationCoordinator.java | 89 + .../DefaultAspectModelValidationService.java | 129 + .../common/uri/DocumentUriResolver.java | 18 + .../lsp/text/AspectDiagnosticsWorkflow.java | 58 + .../text/DocumentAspectValidationService.java | 110 + .../lsp/text/DocumentDiagnosticsService.java | 46 + .../lsp/text/DocumentDiagnosticsStore.java | 37 + .../turtlelsp/lsp/text/DocumentStore.java | 24 + .../lsp/text/TextDocumentClientNotifier.java | 43 + .../text/TurtleSyntaxValidationService.java | 56 + .../lsp/text/TurtleTextDocumentService.java | 130 + .../lsp/workspace/TurtleWorkspaceService.java | 20 + .../TurtlePrefixDefinitionService.java | 95 + lsp-server/src/main/resources/log4j2.xml | 28 + .../turtlelsp/TurtleDefinitionTest.java | 63 + ...faultAspectModelValidationServiceTest.java | 83 + .../text/AspectDiagnosticsWorkflowTest.java | 118 + .../text/DocumentDiagnosticsServiceTest.java | 99 + .../text/DocumentDiagnosticsStoreTest.java | 43 + .../text/TextDocumentClientNotifierTest.java | 56 + .../TurtlePrefixDefinitionServiceTest.java | 57 + 59 files changed, 8800 insertions(+) create mode 100644 .gitignore create mode 100644 extension/.gitignore create mode 100644 extension/.prettierrc create mode 100644 extension/.vscode-test.mjs create mode 100644 extension/.vscode/extensions.json create mode 100644 extension/.vscode/launch.json create mode 100644 extension/.vscode/settings.json create mode 100644 extension/.vscode/tasks.json create mode 100644 extension/.vscodeignore create mode 100644 extension/eslint.config.mjs create mode 100644 extension/language-configuration.json create mode 100644 extension/package-lock.json create mode 100644 extension/package.json create mode 100644 extension/samples/invalid.ttl create mode 100644 extension/samples/org.eclipse.esmf.test/1.0.0/Aspect.ttl create mode 100644 extension/samples/valid.ttl create mode 100644 extension/src/aspectValidation.ts create mode 100644 extension/src/extension.ts create mode 100644 extension/src/test/aspectValidationController.test.ts create mode 100644 extension/src/test/validationTestHarness.ts create mode 100644 extension/tsconfig.json create mode 100644 extension/vsc-extension-quickstart.md create mode 100644 lsp-server/.development/esmf-checkstyle.xml create mode 100644 lsp-server/.development/esmf-eclipse-codestyle.xml create mode 100644 lsp-server/.development/esmf-intellij-codestyle.xml create mode 100644 lsp-server/.development/esmf-intellij-inspections.xml create mode 100644 lsp-server/.gitattributes create mode 100644 lsp-server/.gitignore create mode 100644 lsp-server/pom.xml create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/App.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/TurtleLanguageServer.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/diagnostics/AspectDiagnosticMapper.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationError.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationErrorType.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationResult.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectViolationInfo.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/request/ValidateDocumentParams.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/service/AspectModelValidationService.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/service/AspectValidationCoordinator.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/aspect/service/DefaultAspectModelValidationService.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/common/uri/DocumentUriResolver.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/text/AspectDiagnosticsWorkflow.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentAspectValidationService.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsService.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsStore.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentStore.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TextDocumentClientNotifier.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TurtleSyntaxValidationService.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TurtleTextDocumentService.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/lsp/workspace/TurtleWorkspaceService.java create mode 100644 lsp-server/src/main/java/com/example/turtlelsp/turtle/navigation/TurtlePrefixDefinitionService.java create mode 100644 lsp-server/src/main/resources/log4j2.xml create mode 100644 lsp-server/src/test/java/com/example/turtlelsp/TurtleDefinitionTest.java create mode 100644 lsp-server/src/test/java/com/example/turtlelsp/aspect/service/DefaultAspectModelValidationServiceTest.java create mode 100644 lsp-server/src/test/java/com/example/turtlelsp/lsp/text/AspectDiagnosticsWorkflowTest.java create mode 100644 lsp-server/src/test/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsServiceTest.java create mode 100644 lsp-server/src/test/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsStoreTest.java create mode 100644 lsp-server/src/test/java/com/example/turtlelsp/lsp/text/TextDocumentClientNotifierTest.java create mode 100644 lsp-server/src/test/java/com/example/turtlelsp/turtle/navigation/TurtlePrefixDefinitionServiceTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/extension/.gitignore b/extension/.gitignore new file mode 100644 index 0000000..17df51b --- /dev/null +++ b/extension/.gitignore @@ -0,0 +1,6 @@ +out +dist +node_modules +.vscode-test/ +*.vsix +logs diff --git a/extension/.prettierrc b/extension/.prettierrc new file mode 100644 index 0000000..d4e3781 --- /dev/null +++ b/extension/.prettierrc @@ -0,0 +1,9 @@ +{ + "printWidth": 140, + "singleQuote": true, + "useTabs": false, + "tabWidth": 4, + "semi": true, + "bracketSpacing": false, + "arrowParens": "avoid" +} diff --git a/extension/.vscode-test.mjs b/extension/.vscode-test.mjs new file mode 100644 index 0000000..d320b89 --- /dev/null +++ b/extension/.vscode-test.mjs @@ -0,0 +1,9 @@ +import { defineConfig } from '@vscode/test-cli'; + +export default defineConfig({ + files: 'out/test/**/*.test.js', + launchArgs: [ + '--user-data-dir=/tmp/extension-vscode-test-user-data', + '--extensions-dir=/tmp/extension-vscode-test-extensions' + ], +}); diff --git a/extension/.vscode/extensions.json b/extension/.vscode/extensions.json new file mode 100644 index 0000000..186459d --- /dev/null +++ b/extension/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "dbaeumer.vscode-eslint", + "ms-vscode.extension-test-runner" + ] +} diff --git a/extension/.vscode/launch.json b/extension/.vscode/launch.json new file mode 100644 index 0000000..8880465 --- /dev/null +++ b/extension/.vscode/launch.json @@ -0,0 +1,21 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "preLaunchTask": "${defaultBuildTask}" + } + ] +} diff --git a/extension/.vscode/settings.json b/extension/.vscode/settings.json new file mode 100644 index 0000000..afdab66 --- /dev/null +++ b/extension/.vscode/settings.json @@ -0,0 +1,11 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false // set this to true to hide the "out" folder with the compiled JS files + }, + "search.exclude": { + "out": true // set this to false to include "out" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off" +} diff --git a/extension/.vscode/tasks.json b/extension/.vscode/tasks.json new file mode 100644 index 0000000..3b17e53 --- /dev/null +++ b/extension/.vscode/tasks.json @@ -0,0 +1,20 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/extension/.vscodeignore b/extension/.vscodeignore new file mode 100644 index 0000000..7d3e5c7 --- /dev/null +++ b/extension/.vscodeignore @@ -0,0 +1,11 @@ +.vscode/** +.vscode-test/** +src/** +.gitignore +.yarnrc +vsc-extension-quickstart.md +**/tsconfig.json +**/eslint.config.mjs +**/*.map +**/*.ts +**/.vscode-test.* diff --git a/extension/eslint.config.mjs b/extension/eslint.config.mjs new file mode 100644 index 0000000..7c51b0c --- /dev/null +++ b/extension/eslint.config.mjs @@ -0,0 +1,27 @@ +import typescriptEslint from "typescript-eslint"; + +export default [{ + files: ["**/*.ts"], +}, { + plugins: { + "@typescript-eslint": typescriptEslint.plugin, + }, + + languageOptions: { + parser: typescriptEslint.parser, + ecmaVersion: 2022, + sourceType: "module", + }, + + rules: { + "@typescript-eslint/naming-convention": ["warn", { + selector: "import", + format: ["camelCase", "PascalCase"], + }], + + curly: "warn", + eqeqeq: "warn", + "no-throw-literal": "warn", + semi: "warn", + }, +}]; \ No newline at end of file diff --git a/extension/language-configuration.json b/extension/language-configuration.json new file mode 100644 index 0000000..cad0785 --- /dev/null +++ b/extension/language-configuration.json @@ -0,0 +1,24 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + ["(", ")"], + ["[", "]"], + ["{", "}"] + ], + "autoClosingPairs": [ + { "open": "(", "close": ")" }, + { "open": "[", "close": "]" }, + { "open": "{", "close": "}" }, + { "open": "\"", "close": "\"" }, + { "open": "<", "close": ">" } + ], + "surroundingPairs": [ + ["(", ")"], + ["[", "]"], + ["{", "}"], + ["\"", "\""], + ["<", ">"] + ] +} diff --git a/extension/package-lock.json b/extension/package-lock.json new file mode 100644 index 0000000..0f869ff --- /dev/null +++ b/extension/package-lock.json @@ -0,0 +1,3720 @@ +{ + "name": "extension", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "extension", + "version": "0.0.1", + "dependencies": { + "vscode-languageclient": "^9.0.1" + }, + "devDependencies": { + "@types/jest": "^30.0.0", + "@types/mocha": "^10.0.10", + "@types/node": "22.x", + "@types/vscode": "^1.110.0", + "@vscode/test-cli": "^0.0.12", + "@vscode/test-electron": "^2.5.2", + "eslint": "^9.39.3", + "typescript": "^5.9.3", + "typescript-eslint": "^8.56.1" + }, + "engines": { + "vscode": "^1.110.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/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": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", + "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", + "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", + "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "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" + }, + "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/node": { + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vscode": { + "version": "1.110.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.110.0.tgz", + "integrity": "sha512-AGuxUEpU4F4mfuQjxPPaQVyuOMhs+VT/xRok1jiHVBubHK7lBRvCuOMZG0LKUwxncrPorJ5qq/uil3IdZBd5lA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "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.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "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", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "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/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "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" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "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" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "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" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "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" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.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" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "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" + } + }, + "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": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vscode/test-cli": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.12.tgz", + "integrity": "sha512-iYN0fDg29+a2Xelle/Y56Xvv7Nc8Thzq4VwpzAF/SIE6918rDicqfsQxV6w1ttr2+SOm+10laGuY9FG2ptEKsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mocha": "^10.0.10", + "c8": "^10.1.3", + "chokidar": "^3.6.0", + "enhanced-resolve": "^5.18.3", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^11.7.4", + "supports-color": "^10.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, + "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/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": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "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" + } + }, + "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/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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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/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/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/c8": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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/cliui/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/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": ">=8" + } + }, + "node_modules/cliui/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/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", + "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" + } + }, + "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", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "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": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "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/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "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", + "engines": { + "node": ">=6" + } + }, + "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": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@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", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.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", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/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/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/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/expect": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", + "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.3.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.3.0", + "jest-message-util": "30.3.0", + "jest-mock": "30.3.0", + "jest-util": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.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==", + "dev": true, + "license": "MIT" + }, + "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/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/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/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/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/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/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.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "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==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "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": "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/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "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/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/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "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/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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": "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/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-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "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/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/jest-diff": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", + "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.3.0", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", + "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.3.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3", + "pretty-format": "30.3.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/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/jest-mock": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", + "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-util": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", + "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/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/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "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/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/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "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/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/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "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/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "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/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": "ISC" + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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/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": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/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": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "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/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/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/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "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": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/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/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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": ">=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==", + "dev": true, + "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": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", + "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "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", + "engines": { + "node": ">=6" + } + }, + "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": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "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", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "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": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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": ">=8" + } + }, + "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": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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/string-width-cjs/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/string-width-cjs/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/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/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/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": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/test-exclude": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/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/test-exclude/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/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/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "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": "MIT", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "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", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "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": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.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" + } + }, + "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/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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "license": "MIT", + "dependencies": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "engines": { + "vscode": "^1.82.0" + } + }, + "node_modules/vscode-languageclient/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "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", + "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" + } + }, + "node_modules/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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/wrap-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/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/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/yargs/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/yargs/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": ">=8" + } + }, + "node_modules/yargs/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/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/extension/package.json b/extension/package.json new file mode 100644 index 0000000..e254f2e --- /dev/null +++ b/extension/package.json @@ -0,0 +1,64 @@ +{ + "name": "extension", + "displayName": "Turtle LSP", + "description": "VS Code extension for Turtle LSP", + "version": "0.0.1", + "engines": { + "vscode": "^1.110.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "onLanguage:turtle", + "onCommand:turtleLsp.validateAspectModelNow" + ], + "main": "./out/extension.js", + "contributes": { + "commands": [ + { + "command": "turtleLsp.validateAspectModelNow", + "title": "Validate Aspect Model Now", + "category": "Turtle LSP" + } + ], + "languages": [ + { + "id": "turtle", + "aliases": [ + "Turtle", + "turtle", + "RDF Turtle" + ], + "extensions": [ + ".ttl" + ], + "configuration": "./language-configuration.json" + } + ] + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "build-watch": "tsc -p tsconfig.json --watch", + "prettier": "prettier --config .prettierrc --write './src/**/*{.ts,.js,.json}'", + "test": "jest --reporters default jest-stare", + "test:prettier": "prettier --config .prettierrc --list-different './src/**/*{.ts,.js,.json}'", + "test:coverage": "jest --coverage --reporters default jest-stare", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix" + }, + "devDependencies": { + "@types/jest": "^30.0.0", + "@types/mocha": "^10.0.10", + "@types/node": "22.x", + "@types/vscode": "^1.110.0", + "@vscode/test-cli": "^0.0.12", + "@vscode/test-electron": "^2.5.2", + "eslint": "^9.39.3", + "typescript": "^5.9.3", + "typescript-eslint": "^8.56.1" + }, + "dependencies": { + "vscode-languageclient": "^9.0.1" + } +} diff --git a/extension/samples/invalid.ttl b/extension/samples/invalid.ttl new file mode 100644 index 0000000..d5a3189 --- /dev/null +++ b/extension/samples/invalid.ttl @@ -0,0 +1,5 @@ +# Invalid Turtle — missing dot after prefix declaration and bad triple +@prefix ex: . + +ex:Alice foaf:name "Alice" . # missing . terminator +ex:Bob ex:knows ex:Alice . # foaf:name used without prefix diff --git a/extension/samples/org.eclipse.esmf.test/1.0.0/Aspect.ttl b/extension/samples/org.eclipse.esmf.test/1.0.0/Aspect.ttl new file mode 100644 index 0000000..c5c24da --- /dev/null +++ b/extension/samples/org.eclipse.esmf.test/1.0.0/Aspect.ttl @@ -0,0 +1,23 @@ +# Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH +# +# See the AUTHORS file(s) distributed with this work for additional +# information regarding authorship. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 + +@prefix : . +@prefix samm: . +@prefix samm-c: . +@prefix xsd: . +@prefix unit: . + +:Aspect a samm:Aspect ; + samm:preferredName "Test Aspect"@en ; + samm:description "This is a test ription"@en ; + samm:description "This is a taaest ription"@de ; + samm:properties ( ) ; + samm:operations ( ) . \ No newline at end of file diff --git a/extension/samples/valid.ttl b/extension/samples/valid.ttl new file mode 100644 index 0000000..b2fb87b --- /dev/null +++ b/extension/samples/valid.ttl @@ -0,0 +1,10 @@ +# Valid Turtle example +@prefix ex: . +@prefix foaf: . + +ex:Alice a foaf:Person ; + foaf:name "Alice" ; + foaf:knows ex:Bob . + +ex:Bob a foaf:Person ; + foaf:name "Bob" . diff --git a/extension/src/aspectValidation.ts b/extension/src/aspectValidation.ts new file mode 100644 index 0000000..8b05fec --- /dev/null +++ b/extension/src/aspectValidation.ts @@ -0,0 +1,200 @@ +import * as vscode from 'vscode'; + +export const VALIDATE_DOCUMENT_REQUEST = 'turtle/aspectValidation/validateDocument'; +export const VALIDATE_DOCUMENT_COMMAND = 'turtleLsp.validateAspectModelNow'; +const STATUS_MESSAGE_TIMEOUT_MS = 5000; + +export type AspectValidationTrigger = 'manual' | 'save'; + +export interface AspectValidationError { + type?: string; + message?: string; +} + +export interface AspectValidationResult { + valid?: boolean; + report?: string; + violations?: Array; + error?: AspectValidationError | null; +} + +export interface RequestClient { + sendRequest(method: string, params?: unknown): Thenable; +} + +export interface ValidationWindow { + showInformationMessage(message: string): Thenable; + showWarningMessage(message: string): Thenable; + showErrorMessage(message: string): Thenable; + withProgress( + options: vscode.ProgressOptions, + task: (progress: vscode.Progress<{message?: string; increment?: number}>, token: vscode.CancellationToken) => Thenable, + ): Thenable; + setStatusBarMessage(text: string, hideAfterTimeout: number): vscode.Disposable; +} + +export interface ValidationWorkspace { + onDidSaveTextDocument(listener: (document: vscode.TextDocument) => void): vscode.Disposable; +} + +export interface ValidationOutputChannel { + appendLine(value: string): void; +} + +export class AspectValidationController { + private readonly inFlightKeys = new Set(); + + constructor( + private readonly client: RequestClient, + private readonly window: ValidationWindow, + private readonly workspace: ValidationWorkspace, + private readonly outputChannel: ValidationOutputChannel, + ) {} + + register(context: vscode.ExtensionContext): void { + context.subscriptions.push( + vscode.commands.registerCommand(VALIDATE_DOCUMENT_COMMAND, async () => { + const editor = vscode.window.activeTextEditor; + await this.validateDocument(editor?.document, 'manual'); + }), + this.workspace.onDidSaveTextDocument(async document => { + await this.validateDocument(document, 'save'); + }), + ); + } + + async validateDocument( + document: Pick | undefined, + trigger: AspectValidationTrigger, + ): Promise { + if (!document || document.languageId !== 'turtle') { + if (trigger === 'manual') { + await this.window.showWarningMessage('Open a Turtle file before running aspect validation.'); + } + return undefined; + } + + const request = () => + this.client.sendRequest(VALIDATE_DOCUMENT_REQUEST, { + uri: document.uri.toString(), + reason: trigger, + }); + + return this.runValidation(`document:${document.uri.toString()}`, 'Aspect model validation', trigger, request); + } + + private async runValidation( + key: string, + title: string, + trigger: AspectValidationTrigger, + request: () => Thenable, + ): Promise { + if (this.inFlightKeys.has(key)) { + this.outputChannel.appendLine(`[aspectValidation] ${title} already running for ${key}`); + return undefined; + } + + this.inFlightKeys.add(key); + try { + const result = await this.runWithProgress(title, trigger, request); + + await this.showSummary(result, trigger); + return result; + } catch (error) { + await this.handleFailure(error, trigger); + return undefined; + } finally { + this.inFlightKeys.delete(key); + } + } + + private runWithProgress( + title: string, + trigger: AspectValidationTrigger, + request: () => Thenable, + ): Promise { + if (trigger === 'save') { + const disposable = this.window.setStatusBarMessage(`${title} in progress...`, STATUS_MESSAGE_TIMEOUT_MS); + return Promise.resolve(request()).finally(() => disposable.dispose()); + } + + return Promise.resolve( + this.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title, + cancellable: false, + }, + async () => request(), + ), + ); + } + + private async showSummary(result: AspectValidationResult, trigger: AspectValidationTrigger): Promise { + const summary = this.formatSummary(result); + this.outputChannel.appendLine(`[aspectValidation] ${summary}`); + + if (trigger === 'save') { + this.window.setStatusBarMessage(summary, STATUS_MESSAGE_TIMEOUT_MS); + return; + } + + if (result.error) { + await this.window.showErrorMessage(summary); + return; + } + + if (result.valid === false) { + await this.window.showWarningMessage(summary); + return; + } + + await this.window.showInformationMessage(summary); + } + + private async handleFailure(error: unknown, trigger: AspectValidationTrigger): Promise { + const summary = this.toFailureMessage(error); + this.outputChannel.appendLine(`[aspectValidation] ${summary}`); + + if (trigger === 'save') { + this.window.setStatusBarMessage(summary, STATUS_MESSAGE_TIMEOUT_MS); + return; + } + + await this.window.showErrorMessage(summary); + } + + private formatSummary(result: AspectValidationResult): string { + if (result.error?.message) { + return `Aspect validation failed: ${result.error.message}`; + } + + const violationCount = result.violations?.length ?? 0; + const baseMessage = + result.valid || violationCount === 0 + ? 'Aspect validation completed without issues.' + : `Aspect validation found ${violationCount} issue${violationCount === 1 ? '' : 's'}.`; + + if (!result.report) { + return baseMessage; + } + + const firstLine = result.report + .split(/\r?\n/) + .map(line => line.trim()) + .find(line => line.length > 0); + + return firstLine ? `${baseMessage} ${firstLine}` : baseMessage; + } + + private toFailureMessage(error: unknown): string { + if (error instanceof Error) { + if (error.message.includes('Method not found')) { + return 'Aspect validation request is not supported by the current server build.'; + } + return `Aspect validation request failed: ${error.message}`; + } + + return 'Aspect validation request failed.'; + } +} diff --git a/extension/src/extension.ts b/extension/src/extension.ts new file mode 100644 index 0000000..48b79f6 --- /dev/null +++ b/extension/src/extension.ts @@ -0,0 +1,108 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import {Executable, LanguageClient, LanguageClientOptions, ServerOptions} from 'vscode-languageclient/node'; +import {AspectValidationController} from './aspectValidation'; + +let client: LanguageClient | undefined; + +export async function activate(context: vscode.ExtensionContext): Promise { + const serverProjectPath = path.join(context.extensionPath, '..', 'lsp-server'); + const jarPath = path.join(serverProjectPath, 'target', 'lsp-server.jar'); + const outputChannel = vscode.window.createOutputChannel('Turtle LSP'); + + context.subscriptions.push(outputChannel); + + const executable = resolveServerExecutable(serverProjectPath, jarPath); + if (!executable) { + const message = `Turtle language server launch target not found in ${serverProjectPath}`; + outputChannel.appendLine(message); + void vscode.window.showErrorMessage(`${message}. Run mvn package in the server project before using the extension.`); + return; + } + + outputChannel.appendLine(`[startup] Launching Turtle language server via: java ${(executable.args ?? []).join(' ')}`); + + const serverOptions: ServerOptions = { + run: executable, + debug: executable, + }; + + const clientOptions: LanguageClientOptions = { + documentSelector: [ + {scheme: 'file', language: 'turtle'}, + {scheme: 'untitled', language: 'turtle'}, + ], + outputChannel, + synchronize: { + configurationSection: 'turtleLsp', + fileEvents: vscode.workspace.createFileSystemWatcher('**/*.ttl'), + }, + }; + + client = new LanguageClient('turtleLanguageServer', 'Turtle Language Server', serverOptions, clientOptions); + context.subscriptions.push(client); + await client.start(); + + const aspectValidationController = new AspectValidationController(client, vscode.window, vscode.workspace, outputChannel); + aspectValidationController.register(context); +} + +function resolveServerExecutable(serverProjectPath: string, jarPath: string): Executable | undefined { + const runtimeClasspath = resolveMavenRuntimeClasspath(serverProjectPath); + + if (runtimeClasspath) { + return { + command: 'java', + args: ['-cp', runtimeClasspath, 'com.example.turtlelsp.App'], + options: { + cwd: serverProjectPath, + }, + }; + } + + if (fs.existsSync(jarPath)) { + return { + command: 'java', + args: ['-jar', jarPath], + options: { + cwd: serverProjectPath, + }, + }; + } + + return undefined; +} + +function resolveMavenRuntimeClasspath(serverProjectPath: string): string | undefined { + const reportsDirectory = path.join(serverProjectPath, 'target', 'surefire-reports'); + if (!fs.existsSync(reportsDirectory)) { + return undefined; + } + + const reportFile = fs.readdirSync(reportsDirectory).find(fileName => fileName.startsWith('TEST-') && fileName.endsWith('.xml')); + if (!reportFile) { + return undefined; + } + + const reportContents = fs.readFileSync(path.join(reportsDirectory, reportFile), 'utf8'); + const match = reportContents.match(//); + if (!match) { + return undefined; + } + + const entries = match[1] + .split(path.delimiter) + .filter(Boolean) + .filter((entry, index) => !(index === 0 && entry.endsWith(path.join('target', 'test-classes')))) + .filter(entry => fs.existsSync(entry)); + + return entries.length > 0 ? entries.join(path.delimiter) : undefined; +} + +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } +} diff --git a/extension/src/test/aspectValidationController.test.ts b/extension/src/test/aspectValidationController.test.ts new file mode 100644 index 0000000..6eae8d0 --- /dev/null +++ b/extension/src/test/aspectValidationController.test.ts @@ -0,0 +1,118 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import {AspectValidationController, VALIDATE_DOCUMENT_REQUEST} from '../aspectValidation'; +import {createValidationControllerHarness, createValidationDocument} from './validationTestHarness'; + +describe('AspectValidationController', () => { + test('sends manual validation request with notification progress and warning summary', async () => { + const harness = createValidationControllerHarness({ + response: {valid: false, violations: [{code: 'E001'}], report: 'First detail line'}, + }); + + await harness.controller.validateDocument(createValidationDocument('/tmp/Aspect.ttl'), 'manual'); + + assert.deepStrictEqual(harness.sentRequests, [ + { + method: VALIDATE_DOCUMENT_REQUEST, + params: {uri: 'file:///tmp/Aspect.ttl', reason: 'manual'}, + }, + ]); + assert.deepStrictEqual(harness.window.progressTitles, ['Aspect model validation']); + assert.deepStrictEqual(harness.window.statusMessages, []); + assert.deepStrictEqual(harness.window.warningMessages, ['Aspect validation found 1 issue. First detail line']); + }); + + test('register wires save validation through the workspace listener and status bar', async () => { + const harness = createValidationControllerHarness({ + response: {valid: true, report: 'All checks completed successfully.'}, + }); + + const context = {subscriptions: [] as vscode.Disposable[]} as unknown as vscode.ExtensionContext; + await withStubbedRegisterCommand(() => { + harness.controller.register(context); + }); + + await harness.workspace.fireSave(createValidationDocument('/tmp/Aspect.ttl')); + + assert.deepStrictEqual(harness.sentRequests, [ + { + method: VALIDATE_DOCUMENT_REQUEST, + params: {uri: 'file:///tmp/Aspect.ttl', reason: 'save'}, + }, + ]); + assert.deepStrictEqual(harness.window.progressTitles, []); + assert.deepStrictEqual(harness.window.statusMessages, [ + 'Aspect model validation in progress...', + 'Aspect validation completed without issues. All checks completed successfully.', + ]); + }); + + test('shows an info summary for successful manual validation', async () => { + const harness = createValidationControllerHarness({ + response: {valid: true, report: 'Everything passed.'}, + }); + + await harness.controller.validateDocument(createValidationDocument('/tmp/Aspect.ttl'), 'manual'); + + assert.deepStrictEqual(harness.window.infoMessages, ['Aspect validation completed without issues. Everything passed.']); + assert.deepStrictEqual(harness.window.warningMessages, []); + assert.deepStrictEqual(harness.window.errorMessages, []); + }); + + test('shows an error message for server-side validation errors during manual runs', async () => { + const harness = createValidationControllerHarness({ + response: {error: {message: 'Validator crashed'}}, + }); + + await harness.controller.validateDocument(createValidationDocument('/tmp/Aspect.ttl'), 'manual'); + + assert.deepStrictEqual(harness.window.errorMessages, ['Aspect validation failed: Validator crashed']); + assert.deepStrictEqual(harness.window.infoMessages, []); + assert.deepStrictEqual(harness.window.warningMessages, []); + }); + + test('reports failed save validations via the status bar instead of dialogs', async () => { + const harness = createValidationControllerHarness({ + error: new Error('Method not found'), + }); + + await harness.controller.validateDocument(createValidationDocument('/tmp/Aspect.ttl'), 'save'); + + assert.deepStrictEqual(harness.window.statusMessages, [ + 'Aspect model validation in progress...', + 'Aspect validation request is not supported by the current server build.', + ]); + assert.deepStrictEqual(harness.window.errorMessages, []); + }); + + test('warns instead of sending a manual request when the active document is not Turtle', async () => { + const harness = createValidationControllerHarness(); + + await harness.controller.validateDocument( + { + languageId: 'plaintext', + uri: vscode.Uri.file('/tmp/readme.txt'), + }, + 'manual', + ); + + assert.deepStrictEqual(harness.sentRequests, []); + assert.deepStrictEqual(harness.window.warningMessages, ['Open a Turtle file before running aspect validation.']); + }); +}); + +async function withStubbedRegisterCommand(run: () => void | Promise): Promise { + const originalRegisterCommand = vscode.commands.registerCommand; + + Object.assign(vscode.commands, { + registerCommand: () => new vscode.Disposable(() => undefined), + }); + + try { + await run(); + } finally { + Object.assign(vscode.commands, { + registerCommand: originalRegisterCommand, + }); + } +} diff --git a/extension/src/test/validationTestHarness.ts b/extension/src/test/validationTestHarness.ts new file mode 100644 index 0000000..5a75a99 --- /dev/null +++ b/extension/src/test/validationTestHarness.ts @@ -0,0 +1,124 @@ +import * as vscode from 'vscode'; +import { + AspectValidationController, + AspectValidationResult, + RequestClient, + ValidationOutputChannel, + ValidationWindow, + ValidationWorkspace, +} from '../aspectValidation'; + +type ValidationHarnessOptions = { + response?: AspectValidationResult; + error?: Error; +}; + +type RecordedRequest = { + method: string; + params: unknown; +}; + +type FakeWindow = ValidationWindow & { + errorMessages: string[]; + infoMessages: string[]; + progressTitles: string[]; + statusMessages: string[]; + warningMessages: string[]; +}; + +type FakeWorkspace = ValidationWorkspace & { + fireSave(document: Pick): Promise; +}; + +type FakeOutputChannel = ValidationOutputChannel & { + lines: string[]; +}; + +export function createValidationControllerHarness(options: ValidationHarnessOptions = {}) { + const sentRequests: RecordedRequest[] = []; + const window = createFakeWindow(); + const workspace = createFakeWorkspace(); + const outputChannel = createFakeOutputChannel(); + const client: RequestClient = { + sendRequest: async (method: string, params?: unknown) => { + sentRequests.push({method, params}); + + if (options.error) { + throw options.error; + } + + return (options.response ?? {valid: true}) as R; + }, + }; + + return { + controller: new AspectValidationController(client, window, workspace, outputChannel), + outputChannel, + sentRequests, + window, + workspace, + }; +} + +export function createValidationDocument(filePath: string): Pick { + return { + languageId: 'turtle', + uri: vscode.Uri.file(filePath), + }; +} + +function createFakeWorkspace(): FakeWorkspace { + let saveListener: ((document: vscode.TextDocument) => void | Promise) | undefined; + + return { + onDidSaveTextDocument: (listener: (document: vscode.TextDocument) => void | Promise) => { + saveListener = listener; + return new vscode.Disposable(() => undefined); + }, + fireSave: async (document: Pick) => { + await saveListener?.(document as vscode.TextDocument); + }, + }; +} + +function createFakeWindow(): FakeWindow { + return { + errorMessages: [], + infoMessages: [], + progressTitles: [], + statusMessages: [], + warningMessages: [], + showInformationMessage(message: string) { + this.infoMessages.push(message); + return Promise.resolve(undefined); + }, + showWarningMessage(message: string) { + this.warningMessages.push(message); + return Promise.resolve(undefined); + }, + showErrorMessage(message: string) { + this.errorMessages.push(message); + return Promise.resolve(undefined); + }, + withProgress( + options: vscode.ProgressOptions, + task: (progress: vscode.Progress<{message?: string; increment?: number}>, token: vscode.CancellationToken) => Thenable, + ) { + this.progressTitles.push(options.title ?? ''); + return Promise.resolve(task({report: () => undefined}, {} as vscode.CancellationToken)); + }, + setStatusBarMessage(text: string) { + this.statusMessages.push(text); + return new vscode.Disposable(() => undefined); + }, + }; +} + +function createFakeOutputChannel(): FakeOutputChannel { + return { + lines: [], + appendLine(value: string) { + this.lines.push(value); + }, + }; +} diff --git a/extension/tsconfig.json b/extension/tsconfig.json new file mode 100644 index 0000000..9559ed1 --- /dev/null +++ b/extension/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "Node16", + "target": "ES2022", + "outDir": "out", + "lib": [ + "ES2022" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true + } +} diff --git a/extension/vsc-extension-quickstart.md b/extension/vsc-extension-quickstart.md new file mode 100644 index 0000000..d727ac7 --- /dev/null +++ b/extension/vsc-extension-quickstart.md @@ -0,0 +1,17 @@ +# Welcome to your VS Code Extension + +## What's in the folder + +* This folder contains all of the files necessary for your extension. +* `package.json` - this is the manifest file in which you declare your extension and command. + * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. +* `src/extension.ts` - this is the main file where you will provide the implementation of your command. + * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. + * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. + +## Get up and running straight away + +* Press `F5` to open a new window with your extension loaded. +* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. +* Set breakpoints in your code inside `src/extension.ts` to debug your extension. +* Find output from your extension in the debug console. diff --git a/lsp-server/.development/esmf-checkstyle.xml b/lsp-server/.development/esmf-checkstyle.xml new file mode 100644 index 0000000..37be7d4 --- /dev/null +++ b/lsp-server/.development/esmf-checkstyle.xml @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lsp-server/.development/esmf-eclipse-codestyle.xml b/lsp-server/.development/esmf-eclipse-codestyle.xml new file mode 100644 index 0000000..f812391 --- /dev/null +++ b/lsp-server/.development/esmf-eclipse-codestyle.xml @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lsp-server/.development/esmf-intellij-codestyle.xml b/lsp-server/.development/esmf-intellij-codestyle.xml new file mode 100644 index 0000000..dc231a6 --- /dev/null +++ b/lsp-server/.development/esmf-intellij-codestyle.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lsp-server/.development/esmf-intellij-inspections.xml b/lsp-server/.development/esmf-intellij-inspections.xml new file mode 100644 index 0000000..6aee320 --- /dev/null +++ b/lsp-server/.development/esmf-intellij-inspections.xml @@ -0,0 +1,1669 @@ + + + + diff --git a/lsp-server/.gitattributes b/lsp-server/.gitattributes new file mode 100644 index 0000000..f91f646 --- /dev/null +++ b/lsp-server/.gitattributes @@ -0,0 +1,12 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + +# Binary files should be left untouched +*.jar binary + diff --git a/lsp-server/.gitignore b/lsp-server/.gitignore new file mode 100644 index 0000000..8b75bac --- /dev/null +++ b/lsp-server/.gitignore @@ -0,0 +1,4 @@ +.idea +target +logs +lsp-server.iml diff --git a/lsp-server/pom.xml b/lsp-server/pom.xml new file mode 100644 index 0000000..d66c9d9 --- /dev/null +++ b/lsp-server/pom.xml @@ -0,0 +1,119 @@ + + 4.0.0 + + com.example + lsp-server + 1.0-SNAPSHOT + jar + + + UTF-8 + 25 + com.example.turtlelsp.App + 6.0.1 + 3.27.6 + 0.23.1 + 5.6.0 + 2.0.17 + 2.25.3 + + + + + org.eclipse.lsp4j + org.eclipse.lsp4j + ${lsp4j.version} + + + org.apache.jena + jena-arq + ${jena.version} + + + org.eclipse.esmf + esmf-aspect-model-starter + 2.14.2 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j2-impl + ${log4j.version} + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.mockito + mockito-core + 5.20.0 + test + + + + + lsp-server + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + ${maven.compiler.release} + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.4 + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.1 + + + package + + shade + + + false + + + + ${main.class} + + + + + + + + + diff --git a/lsp-server/src/main/java/com/example/turtlelsp/App.java b/lsp-server/src/main/java/com/example/turtlelsp/App.java new file mode 100644 index 0000000..9cbedb6 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/App.java @@ -0,0 +1,33 @@ +package com.example.turtlelsp; + +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.services.LanguageClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public static void main(String[] args) { + LOGGER.info("Starting lsp-server"); + TurtleLanguageServer server = new TurtleLanguageServer(); + + try { + Launcher launcher = Launcher.createLauncher( + server, + LanguageClient.class, + System.in, + System.out + ); + + server.connect(launcher.getRemoteProxy()); + launcher.startListening().get(); + LOGGER.info("Language server listener stopped"); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + LOGGER.error("Language server listener was interrupted", ex); + } catch (Exception ex) { + LOGGER.error("Language server terminated with an error", ex); + } + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/TurtleLanguageServer.java b/lsp-server/src/main/java/com/example/turtlelsp/TurtleLanguageServer.java new file mode 100644 index 0000000..b98fc7e --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/TurtleLanguageServer.java @@ -0,0 +1,80 @@ +package com.example.turtlelsp; + +import java.util.concurrent.CompletableFuture; + +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import com.example.turtlelsp.aspect.request.ValidateDocumentParams; +import com.example.turtlelsp.aspect.service.AspectModelValidationService; +import com.example.turtlelsp.lsp.text.TurtleTextDocumentService; +import com.example.turtlelsp.lsp.workspace.TurtleWorkspaceService; +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.InitializeResult; +import org.eclipse.lsp4j.SaveOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.TextDocumentSyncKind; +import org.eclipse.lsp4j.TextDocumentSyncOptions; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.lsp4j.services.LanguageClientAware; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.eclipse.lsp4j.services.WorkspaceService; + +public class TurtleLanguageServer implements LanguageServer, LanguageClientAware { + private final TurtleTextDocumentService textDocumentService; + private final TurtleWorkspaceService workspaceService; + + public TurtleLanguageServer() { + this( new TurtleTextDocumentService() ); + } + + TurtleLanguageServer( TurtleTextDocumentService textDocumentService ) { + this.textDocumentService = textDocumentService; + this.workspaceService = new TurtleWorkspaceService( textDocumentService ); + } + + @Override + public CompletableFuture initialize( InitializeParams params ) { + ServerCapabilities capabilities = new ServerCapabilities(); + TextDocumentSyncOptions syncOptions = new TextDocumentSyncOptions(); + syncOptions.setOpenClose( true ); + syncOptions.setChange( TextDocumentSyncKind.Full ); + syncOptions.setSave( new SaveOptions( true ) ); + capabilities.setTextDocumentSync( syncOptions ); + capabilities.setDefinitionProvider( true ); + + return CompletableFuture.completedFuture( new InitializeResult( capabilities ) ); + } + + @Override + public CompletableFuture shutdown() { + textDocumentService.shutdown(); + return CompletableFuture.completedFuture( null ); + } + + @Override + public void exit() { + throw new UnsupportedOperationException(); + } + + @Override + public TextDocumentService getTextDocumentService() { + return textDocumentService; + } + + @Override + public WorkspaceService getWorkspaceService() { + return workspaceService; + } + + @Override + public void connect( LanguageClient client ) { + textDocumentService.connect( client ); + } + + @JsonRequest("turtle/aspectValidation/validateDocument") + public CompletableFuture validateDocument( ValidateDocumentParams params ) { + String uri = params != null ? params.uri() : null; + return CompletableFuture.completedFuture( textDocumentService.validateDocument( uri ) ); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/diagnostics/AspectDiagnosticMapper.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/diagnostics/AspectDiagnosticMapper.java new file mode 100644 index 0000000..f944f45 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/diagnostics/AspectDiagnosticMapper.java @@ -0,0 +1,49 @@ +package com.example.turtlelsp.aspect.diagnostics; + +import java.net.URI; +import java.util.List; + +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import com.example.turtlelsp.aspect.model.AspectViolationInfo; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.jsonrpc.messages.Either; + +public final class AspectDiagnosticMapper { + public static final String SOURCE = "lsp-server.aspect"; + + public List toDiagnostics(String documentUri, AspectValidationResult result) { + return result.violations().stream() + .filter(violation -> appliesToDocument(documentUri, violation)) + .map(this::toDiagnostic) + .toList(); + } + + private boolean appliesToDocument(String documentUri, AspectViolationInfo violation) { + if (violation.sourceLocation() == null) { + return true; + } + + return URI.create(documentUri).equals(violation.sourceLocation()); + } + + private Diagnostic toDiagnostic(AspectViolationInfo violation) { + Diagnostic diagnostic = new Diagnostic(); + diagnostic.setSource(SOURCE); + diagnostic.setSeverity(DiagnosticSeverity.Error); + diagnostic.setMessage(violation.message()); + diagnostic.setCode(Either.forLeft(violation.code())); + diagnostic.setRange(toRange(violation)); + return diagnostic; + } + + private Range toRange(AspectViolationInfo violation) { + long line = violation.line() != null ? violation.line() : 1L; + long column = violation.column() != null ? violation.column() : 1L; + int safeLine = (int) Math.max(0, line - 1); + int safeColumn = (int) Math.max(0, column - 1); + return new Range(new Position(safeLine, safeColumn), new Position(safeLine, safeColumn + 1)); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationError.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationError.java new file mode 100644 index 0000000..de75991 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationError.java @@ -0,0 +1,7 @@ +package com.example.turtlelsp.aspect.model; + +public record AspectValidationError( + AspectValidationErrorType type, + String message +) { +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationErrorType.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationErrorType.java new file mode 100644 index 0000000..e24c7e2 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationErrorType.java @@ -0,0 +1,8 @@ +package com.example.turtlelsp.aspect.model; + +public enum AspectValidationErrorType { + LOAD, + PARSE, + RESOLVE, + PROCESSING +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationResult.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationResult.java new file mode 100644 index 0000000..794570b --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectValidationResult.java @@ -0,0 +1,11 @@ +package com.example.turtlelsp.aspect.model; + +import java.util.List; + +public record AspectValidationResult( + boolean valid, + String report, + List violations, + AspectValidationError error +) { +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectViolationInfo.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectViolationInfo.java new file mode 100644 index 0000000..d2730d2 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/model/AspectViolationInfo.java @@ -0,0 +1,12 @@ +package com.example.turtlelsp.aspect.model; + +import java.net.URI; + +public record AspectViolationInfo( + String code, + String message, + URI sourceLocation, + Long line, + Long column +) { +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/request/ValidateDocumentParams.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/request/ValidateDocumentParams.java new file mode 100644 index 0000000..7141a3e --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/request/ValidateDocumentParams.java @@ -0,0 +1,7 @@ +package com.example.turtlelsp.aspect.request; + +public record ValidateDocumentParams( + String uri, + String reason +) { +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/AspectModelValidationService.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/AspectModelValidationService.java new file mode 100644 index 0000000..1f8ee1e --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/AspectModelValidationService.java @@ -0,0 +1,14 @@ +package com.example.turtlelsp.aspect.service; + +import java.io.File; +import java.nio.file.Path; + +import com.example.turtlelsp.aspect.model.AspectValidationResult; + +public interface AspectModelValidationService { + AspectValidationResult validate(Path path); + + default AspectValidationResult validate(File file) { + return validate(file.toPath()); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/AspectValidationCoordinator.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/AspectValidationCoordinator.java new file mode 100644 index 0000000..92543c3 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/AspectValidationCoordinator.java @@ -0,0 +1,89 @@ +package com.example.turtlelsp.aspect.service; + +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiConsumer; + +import com.example.turtlelsp.aspect.model.AspectValidationError; +import com.example.turtlelsp.aspect.model.AspectValidationErrorType; +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AspectValidationCoordinator implements AutoCloseable { + private static final Logger LOGGER = LoggerFactory.getLogger(AspectValidationCoordinator.class); + + private final AspectModelValidationService validationService; + private final ExecutorService executorService; + private final Map> inFlight = new ConcurrentHashMap<>(); + private final Map generations = new ConcurrentHashMap<>(); + + public AspectValidationCoordinator(AspectModelValidationService validationService) { + this(validationService, Executors.newSingleThreadExecutor(Thread.ofPlatform().name("aspect-validation-", 0).factory())); + } + + AspectValidationCoordinator(AspectModelValidationService validationService, ExecutorService executorService) { + this.validationService = validationService; + this.executorService = executorService; + } + + public long nextGeneration(String uri) { + return generations.computeIfAbsent(uri, ignored -> new AtomicLong()).incrementAndGet(); + } + + public long currentGeneration(String uri) { + AtomicLong generation = generations.get(uri); + return generation != null ? generation.get() : 0L; + } + + public void cancel(String uri) { + CompletableFuture previous = inFlight.remove(uri); + if (previous != null) { + LOGGER.debug("[cancel] cancelling previous aspect validation for {}", uri); + previous.cancel(true); + } + } + + public void submit(String uri, Path path, long generation, BiConsumer callback) { + cancel(uri); + CompletableFuture future = CompletableFuture.supplyAsync( + () -> validationService.validate(path), + executorService + ); + inFlight.put(uri, future); + future.whenComplete((result, throwable) -> { + inFlight.remove(uri, future); + if (throwable instanceof CancellationException || future.isCancelled()) { + LOGGER.debug("[cancel] aspect validation cancelled for {}", uri); + return; + } + if (throwable != null) { + LOGGER.error("[publish diagnostics] aspect validation failed for {}", uri, throwable); + callback.accept(generation, new AspectValidationResult( + false, + throwable.getMessage(), + java.util.List.of(), + new AspectValidationError( AspectValidationErrorType.PROCESSING, throwable.getMessage()) + )); + return; + } + callback.accept(generation, result); + }); + } + + public AspectValidationResult validateSync(Path path) { + return validationService.validate(path); + } + + @Override + public void close() { + inFlight.values().forEach(future -> future.cancel(true)); + executorService.shutdownNow(); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/DefaultAspectModelValidationService.java b/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/DefaultAspectModelValidationService.java new file mode 100644 index 0000000..a705597 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/aspect/service/DefaultAspectModelValidationService.java @@ -0,0 +1,129 @@ +package com.example.turtlelsp.aspect.service; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; + +import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader; +import org.eclipse.esmf.aspectmodel.shacl.violation.Violation; +import org.eclipse.esmf.aspectmodel.validation.InvalidLexicalValueViolation; +import org.eclipse.esmf.aspectmodel.validation.InvalidSyntaxViolation; +import org.eclipse.esmf.aspectmodel.validation.ProcessingViolation; +import org.eclipse.esmf.aspectmodel.validation.services.AspectModelValidator; +import org.eclipse.esmf.aspectmodel.validation.services.DetailedViolationFormatter; +import org.eclipse.esmf.metamodel.AspectModel; + +import com.example.turtlelsp.aspect.model.AspectValidationError; +import com.example.turtlelsp.aspect.model.AspectValidationErrorType; +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import com.example.turtlelsp.aspect.model.AspectViolationInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultAspectModelValidationService implements AspectModelValidationService { + private static final Logger LOAD_LOGGER = LoggerFactory.getLogger("com.example.turtlelsp.validation.aspect.load"); + private static final Logger RESOLVE_LOGGER = LoggerFactory.getLogger("com.example.turtlelsp.validation.aspect.resolve"); + private static final Logger VALIDATE_LOGGER = LoggerFactory.getLogger("com.example.turtlelsp.validation.aspect.validate"); + + private final AspectModelLoader loader; + private final AspectModelValidator validator; + + public DefaultAspectModelValidationService() { + this(new AspectModelLoader(), new AspectModelValidator()); + } + + DefaultAspectModelValidationService(AspectModelLoader loader, AspectModelValidator validator) { + this.loader = loader; + this.validator = validator; + } + + @Override + public AspectValidationResult validate(Path path) { + if (path == null) { + return failedResult( AspectValidationErrorType.LOAD, "Path must not be null"); + } + + if (!Files.exists(path)) { + return failedResult(AspectValidationErrorType.LOAD, "Aspect model file does not exist: " + path); + } + + if (!Files.isRegularFile(path) || !Files.isReadable(path)) { + return failedResult(AspectValidationErrorType.LOAD, "Aspect model file is not readable: " + path); + } + + try { + LOAD_LOGGER.debug("[load] loading aspect model from {}", path); + List violations = validator.validateModel(() -> loadAspectModel(path)); + VALIDATE_LOGGER.debug("[validate] validation finished for {} with {} violation(s)", path, violations.size()); + String report = new DetailedViolationFormatter().apply(violations); + AspectValidationError error = classifyError(violations); + return new AspectValidationResult(violations.isEmpty(), report, violations.stream().map(this::toViolationInfo).toList(), error); + } catch (Exception exception) { + VALIDATE_LOGGER.error("[validate] unexpected runtime failure for {}", path, exception); + return failedResult(AspectValidationErrorType.PROCESSING, exception.getMessage()); + } + } + + private AspectModel loadAspectModel(Path path) { + RESOLVE_LOGGER.debug("[resolve imports] resolving imports for {}", path); + return loader.load(path.toFile()); + } + + private AspectValidationResult failedResult(AspectValidationErrorType type, String message) { + return new AspectValidationResult(false, message, List.of(), new AspectValidationError(type, message)); + } + + private AspectValidationError classifyError(List violations) { + Optional firstFailure = violations.stream() + .filter(violation -> violation instanceof InvalidSyntaxViolation || violation instanceof InvalidLexicalValueViolation || violation instanceof ProcessingViolation) + .findFirst(); + + if (firstFailure.isEmpty()) { + return null; + } + + Violation violation = firstFailure.get(); + if (violation instanceof InvalidSyntaxViolation syntaxViolation) { + return new AspectValidationError(AspectValidationErrorType.PARSE, syntaxViolation.message()); + } + if (violation instanceof InvalidLexicalValueViolation lexicalValueViolation) { + return new AspectValidationError(AspectValidationErrorType.PARSE, lexicalValueViolation.message()); + } + + String message = violation.message(); + AspectValidationErrorType type = message != null && message.toLowerCase().contains("resolve") + ? AspectValidationErrorType.RESOLVE + : AspectValidationErrorType.PROCESSING; + return new AspectValidationError(type, message); + } + + private AspectViolationInfo toViolationInfo(Violation violation) { + if (violation instanceof InvalidSyntaxViolation syntaxViolation) { + return new AspectViolationInfo( + syntaxViolation.errorCode(), + syntaxViolation.message(), + syntaxViolation.sourceLocation().orElse(null), + syntaxViolation.line(), + syntaxViolation.column() + ); + } + if (violation instanceof InvalidLexicalValueViolation lexicalValueViolation) { + return new AspectViolationInfo( + lexicalValueViolation.errorCode(), + lexicalValueViolation.message(), + lexicalValueViolation.sourceLocation().orElse(null), + (long) lexicalValueViolation.line(), + (long) lexicalValueViolation.column() + ); + } + + return new AspectViolationInfo( + violation.errorCode(), + violation.message(), + violation.sourceLocation().orElse(null), + null, + null + ); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/common/uri/DocumentUriResolver.java b/lsp-server/src/main/java/com/example/turtlelsp/common/uri/DocumentUriResolver.java new file mode 100644 index 0000000..cb9d750 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/common/uri/DocumentUriResolver.java @@ -0,0 +1,18 @@ +package com.example.turtlelsp.common.uri; + +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + +public final class DocumentUriResolver { + private DocumentUriResolver() { + } + + public static Path toPath(String uri) { + if (uri == null || !uri.startsWith("file:")) { + return null; + } + + return Paths.get(URI.create(uri)); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/AspectDiagnosticsWorkflow.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/AspectDiagnosticsWorkflow.java new file mode 100644 index 0000000..8f0895d --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/AspectDiagnosticsWorkflow.java @@ -0,0 +1,58 @@ +package com.example.turtlelsp.lsp.text; + +import java.nio.file.Path; + +import com.example.turtlelsp.aspect.service.AspectValidationCoordinator; +import com.example.turtlelsp.common.uri.DocumentUriResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AspectDiagnosticsWorkflow { + private static final Logger LOGGER = LoggerFactory.getLogger( AspectDiagnosticsWorkflow.class ); + + private final AspectValidationCoordinator aspectValidationCoordinator; + private final DocumentDiagnosticsService diagnosticsService; + private final TextDocumentClientNotifier clientNotifier; + + public AspectDiagnosticsWorkflow( + AspectValidationCoordinator aspectValidationCoordinator, + DocumentDiagnosticsService diagnosticsService, + TextDocumentClientNotifier clientNotifier ) { + this.aspectValidationCoordinator = aspectValidationCoordinator; + this.diagnosticsService = diagnosticsService; + this.clientNotifier = clientNotifier; + } + + public void onDocumentChanged( String uri ) { + aspectValidationCoordinator.cancel( uri ); + diagnosticsService.clearAspect( uri ); + } + + public void onDocumentClosed( String uri ) { + aspectValidationCoordinator.cancel( uri ); + diagnosticsService.clearAll( uri ); + } + + public void onDocumentSaved( String uri ) { + Path path = DocumentUriResolver.toPath( uri ); + if ( path == null ) { + LOGGER.info( "[scheduleAspectValidation] unsupported non-file uri={}, skipping aspect validation", uri ); + diagnosticsService.clearAspect( uri ); + clientNotifier.publishCombinedDiagnostics( uri ); + return; + } + + long generation = aspectValidationCoordinator.nextGeneration( uri ); + aspectValidationCoordinator.submit( uri, path, generation, ( completedGeneration, result ) -> { + long currentGeneration = aspectValidationCoordinator.currentGeneration( uri ); + if ( completedGeneration != currentGeneration ) { + LOGGER.debug( "[publish diagnostics] ignoring stale aspect diagnostics for uri={}, generation={}, current={}", uri, + completedGeneration, currentGeneration ); + return; + } + + diagnosticsService.updateAspect( uri, result ); + clientNotifier.publishCombinedDiagnostics( uri ); + } ); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentAspectValidationService.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentAspectValidationService.java new file mode 100644 index 0000000..5bf7f45 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentAspectValidationService.java @@ -0,0 +1,110 @@ +package com.example.turtlelsp.lsp.text; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Objects; + +import com.example.turtlelsp.aspect.model.AspectValidationError; +import com.example.turtlelsp.aspect.model.AspectValidationErrorType; +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import com.example.turtlelsp.aspect.model.AspectViolationInfo; +import com.example.turtlelsp.aspect.service.AspectValidationCoordinator; +import com.example.turtlelsp.common.uri.DocumentUriResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DocumentAspectValidationService { + private static final Logger LOGGER = LoggerFactory.getLogger( DocumentAspectValidationService.class ); + + private final AspectValidationCoordinator aspectValidationCoordinator; + + public DocumentAspectValidationService( AspectValidationCoordinator aspectValidationCoordinator ) { + this.aspectValidationCoordinator = aspectValidationCoordinator; + } + + public AspectValidationResult validateDocument( String uri, String content ) { + if ( content == null ) { + return failedValidation( AspectValidationErrorType.LOAD, "Document is not available in memory: " + uri ); + } + + Path path = DocumentUriResolver.toPath( uri ); + if ( path == null ) { + return failedValidation( AspectValidationErrorType.LOAD, "Aspect validation supports only file URIs: " + uri ); + } + + return validateOpenDocument( uri, path, content ); + } + + private AspectValidationResult validateOpenDocument( String uri, Path originalPath, String content ) { + Path parent = originalPath.getParent(); + if ( parent == null ) { + return failedValidation( AspectValidationErrorType.LOAD, "Document path has no parent directory: " + originalPath ); + } + + String originalFileName = originalPath.getFileName() != null ? originalPath.getFileName().toString() : "aspect"; + String tempPrefix = originalFileName.replaceAll( "[^A-Za-z0-9._-]", "_" ) + "-"; + if ( tempPrefix.length() < 3 ) { + tempPrefix = "ttl-"; + } + + Path tempFile = null; + try { + tempFile = Files.createTempFile( parent, tempPrefix, ".ttl" ); + Files.writeString( tempFile, content, StandardOpenOption.TRUNCATE_EXISTING ); + AspectValidationResult result = aspectValidationCoordinator.validateSync( tempFile ); + return remapValidationResult( result, tempFile, originalPath, uri ); + } catch ( IOException exception ) { + LOGGER.error( "[validateDocument] failed to prepare in-memory validation for {}", uri, exception ); + return failedValidation( AspectValidationErrorType.PROCESSING, exception.getMessage() ); + } finally { + if ( tempFile != null ) { + try { + Files.deleteIfExists( tempFile ); + } catch ( IOException exception ) { + LOGGER.warn( "[validateDocument] failed to delete temp file {}", tempFile, exception ); + } + } + } + } + + private AspectValidationResult remapValidationResult( AspectValidationResult result, Path tempFile, Path originalPath, String originalUri ) { + URI tempUri = tempFile.toUri(); + List remappedViolations = result.violations().stream() + .map( violation -> remapViolation( violation, tempUri, originalUri ) ) + .toList(); + String remappedReport = remapReport( result.report(), tempFile, originalPath, originalUri ); + return new AspectValidationResult( result.valid(), remappedReport, remappedViolations, result.error() ); + } + + private AspectViolationInfo remapViolation( AspectViolationInfo violation, URI tempUri, String originalUri ) { + if ( !Objects.equals( violation.sourceLocation(), tempUri ) ) { + return violation; + } + + return new AspectViolationInfo( + violation.code(), + violation.message(), + URI.create( originalUri ), + violation.line(), + violation.column() + ); + } + + private String remapReport( String report, Path tempFile, Path originalPath, String originalUri ) { + if ( report == null || report.isBlank() ) { + return report; + } + + return report + .replace( tempFile.toUri().toString(), originalUri ) + .replace( tempFile.toAbsolutePath().toString(), originalPath.toAbsolutePath().toString() ); + } + + private AspectValidationResult failedValidation( AspectValidationErrorType type, String message ) { + return new AspectValidationResult( false, message, List.of(), new AspectValidationError( type, message ) ); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsService.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsService.java new file mode 100644 index 0000000..5fada7f --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsService.java @@ -0,0 +1,46 @@ +package com.example.turtlelsp.lsp.text; + +import java.util.List; + +import com.example.turtlelsp.aspect.diagnostics.AspectDiagnosticMapper; +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import org.eclipse.lsp4j.Diagnostic; + +public class DocumentDiagnosticsService { + private final TurtleSyntaxValidationService syntaxValidationService; + private final AspectDiagnosticMapper aspectDiagnosticMapper; + private final DocumentDiagnosticsStore diagnosticsStore; + + public DocumentDiagnosticsService() { + this( new TurtleSyntaxValidationService(), new AspectDiagnosticMapper(), new DocumentDiagnosticsStore() ); + } + + DocumentDiagnosticsService( + TurtleSyntaxValidationService syntaxValidationService, + AspectDiagnosticMapper aspectDiagnosticMapper, + DocumentDiagnosticsStore diagnosticsStore ) { + this.syntaxValidationService = syntaxValidationService; + this.aspectDiagnosticMapper = aspectDiagnosticMapper; + this.diagnosticsStore = diagnosticsStore; + } + + public void updateSyntax( String uri, String content ) { + diagnosticsStore.putSyntax( uri, syntaxValidationService.validate( content ) ); + } + + public void updateAspect( String uri, AspectValidationResult result ) { + diagnosticsStore.putAspect( uri, aspectDiagnosticMapper.toDiagnostics( uri, result ) ); + } + + public void clearAspect( String uri ) { + diagnosticsStore.clearAspect( uri ); + } + + public void clearAll( String uri ) { + diagnosticsStore.clear( uri ); + } + + public List getCombined( String uri ) { + return diagnosticsStore.getCombined( uri ); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsStore.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsStore.java new file mode 100644 index 0000000..66142c1 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsStore.java @@ -0,0 +1,37 @@ +package com.example.turtlelsp.lsp.text; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.lsp4j.Diagnostic; + +public class DocumentDiagnosticsStore { + private final Map> syntaxDiagnostics = new ConcurrentHashMap<>(); + private final Map> aspectDiagnostics = new ConcurrentHashMap<>(); + + public void putSyntax( String uri, List diagnostics ) { + syntaxDiagnostics.put( uri, List.copyOf( diagnostics ) ); + } + + public void putAspect( String uri, List diagnostics ) { + aspectDiagnostics.put( uri, List.copyOf( diagnostics ) ); + } + + public void clearAspect( String uri ) { + aspectDiagnostics.remove( uri ); + } + + public void clear( String uri ) { + syntaxDiagnostics.remove( uri ); + aspectDiagnostics.remove( uri ); + } + + public List getCombined( String uri ) { + List diagnostics = new ArrayList<>(); + diagnostics.addAll( syntaxDiagnostics.getOrDefault( uri, List.of() ) ); + diagnostics.addAll( aspectDiagnostics.getOrDefault( uri, List.of() ) ); + return diagnostics; + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentStore.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentStore.java new file mode 100644 index 0000000..e2fe287 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/DocumentStore.java @@ -0,0 +1,24 @@ +package com.example.turtlelsp.lsp.text; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class DocumentStore { + private final Map documents = new ConcurrentHashMap<>(); + + public void put( String uri, String content ) { + documents.put( uri, content ); + } + + public String get( String uri ) { + return documents.get( uri ); + } + + public String getOrDefault( String uri, String fallback ) { + return documents.getOrDefault( uri, fallback ); + } + + public void remove( String uri ) { + documents.remove( uri ); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TextDocumentClientNotifier.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TextDocumentClientNotifier.java new file mode 100644 index 0000000..fde7cf8 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TextDocumentClientNotifier.java @@ -0,0 +1,43 @@ +package com.example.turtlelsp.lsp.text; + +import java.util.List; + +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.services.LanguageClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TextDocumentClientNotifier { + private static final Logger LOGGER = LoggerFactory.getLogger( TextDocumentClientNotifier.class ); + + private final DocumentDiagnosticsService diagnosticsService; + private LanguageClient client; + + public TextDocumentClientNotifier( DocumentDiagnosticsService diagnosticsService ) { + this.diagnosticsService = diagnosticsService; + } + + public void connect( LanguageClient client ) { + this.client = client; + } + + public void publishCombinedDiagnostics( String uri ) { + if ( client == null ) { + LOGGER.warn( "[publishDiagnostics] client is null, skipping for uri={}", uri ); + return; + } + + List diagnostics = diagnosticsService.getCombined( uri ); + LOGGER.debug( "[publish diagnostics] publishing {} diagnostic(s) for uri={}", diagnostics.size(), uri ); + client.publishDiagnostics( new PublishDiagnosticsParams( uri, diagnostics ) ); + } + + public void publishEmptyDiagnostics( String uri ) { + if ( client == null ) { + return; + } + + client.publishDiagnostics( new PublishDiagnosticsParams( uri, List.of() ) ); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TurtleSyntaxValidationService.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TurtleSyntaxValidationService.java new file mode 100644 index 0000000..7c994bb --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TurtleSyntaxValidationService.java @@ -0,0 +1,56 @@ +package com.example.turtlelsp.lsp.text; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFParser; +import org.apache.jena.riot.RiotException; +import org.apache.jena.riot.RiotParseException; +import org.apache.jena.riot.system.ErrorHandlerFactory; +import org.apache.jena.riot.system.StreamRDFLib; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TurtleSyntaxValidationService { + private static final Logger LOGGER = LoggerFactory.getLogger( TurtleSyntaxValidationService.class ); + private static final String SYNTAX_SOURCE = "lsp-server.syntax"; + + public List validate( String content ) { + List diagnostics = new ArrayList<>(); + + try { + RDFParser.create() + .fromString( content ) + .lang( Lang.TTL ) + .errorHandler( ErrorHandlerFactory.errorHandlerStrictNoLogging ) + .parse( StreamRDFLib.sinkNull() ); + LOGGER.debug( "[validate] turtle parsing successful" ); + } catch ( RiotParseException exception ) { + LOGGER.warn( "[validate] parse error at line={}, col={}: {}", exception.getLine(), exception.getCol(), exception.getMessage() ); + diagnostics.add( toDiagnostic( exception.getMessage(), exception.getLine(), exception.getCol() ) ); + } catch ( RiotException exception ) { + LOGGER.warn( "[validate] rdf error: {}", exception.getMessage() ); + diagnostics.add( toDiagnostic( exception.getMessage(), 1, 1 ) ); + } + + LOGGER.debug( "[validate] found {} diagnostic(s)", diagnostics.size() ); + return diagnostics; + } + + private Diagnostic toDiagnostic( String message, long line, long column ) { + int safeLine = (int) Math.max( 0, line - 1 ); + int safeColumn = (int) Math.max( 0, column - 1 ); + + Diagnostic diagnostic = new Diagnostic(); + diagnostic.setSource( SYNTAX_SOURCE ); + diagnostic.setSeverity( DiagnosticSeverity.Error ); + diagnostic.setMessage( message != null ? message : "Invalid Turtle syntax" ); + diagnostic.setRange( new Range( new Position( safeLine, safeColumn ), new Position( safeLine, safeColumn + 1 ) ) ); + return diagnostic; + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TurtleTextDocumentService.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TurtleTextDocumentService.java new file mode 100644 index 0000000..fcbfb5a --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/text/TurtleTextDocumentService.java @@ -0,0 +1,130 @@ +package com.example.turtlelsp.lsp.text; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import com.example.turtlelsp.aspect.service.AspectModelValidationService; +import com.example.turtlelsp.aspect.service.AspectValidationCoordinator; +import com.example.turtlelsp.aspect.service.DefaultAspectModelValidationService; +import com.example.turtlelsp.turtle.navigation.TurtlePrefixDefinitionService; +import org.eclipse.lsp4j.DefinitionParams; +import org.eclipse.lsp4j.DidChangeTextDocumentParams; +import org.eclipse.lsp4j.DidCloseTextDocumentParams; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; +import org.eclipse.lsp4j.DidSaveTextDocumentParams; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TurtleTextDocumentService implements TextDocumentService { + private static final Logger LOGGER = LoggerFactory.getLogger( TurtleTextDocumentService.class ); + private final DocumentStore documentStore; + private final DocumentDiagnosticsService diagnosticsService; + private final TextDocumentClientNotifier clientNotifier; + private final TurtlePrefixDefinitionService prefixDefinitionService; + private final DocumentAspectValidationService documentValidationService; + private final AspectDiagnosticsWorkflow aspectDiagnosticsWorkflow; + private final AspectValidationCoordinator aspectValidationCoordinator; + + public TurtleTextDocumentService() { + this( new DefaultAspectModelValidationService() ); + } + + public TurtleTextDocumentService( AspectModelValidationService aspectValidationService ) { + this( + new DocumentStore(), + new DocumentDiagnosticsService(), + new TurtlePrefixDefinitionService(), + new AspectValidationCoordinator( aspectValidationService ) + ); + } + + TurtleTextDocumentService( + DocumentStore documentStore, + DocumentDiagnosticsService diagnosticsService, + TurtlePrefixDefinitionService prefixDefinitionService, + AspectValidationCoordinator aspectValidationCoordinator ) { + this.documentStore = documentStore; + this.diagnosticsService = diagnosticsService; + this.prefixDefinitionService = prefixDefinitionService; + this.aspectValidationCoordinator = aspectValidationCoordinator; + this.clientNotifier = new TextDocumentClientNotifier( diagnosticsService ); + this.documentValidationService = new DocumentAspectValidationService( aspectValidationCoordinator ); + this.aspectDiagnosticsWorkflow = new AspectDiagnosticsWorkflow( aspectValidationCoordinator, diagnosticsService, clientNotifier ); + } + + public void connect( LanguageClient client ) { + clientNotifier.connect( client ); + } + + public void shutdown() { + aspectValidationCoordinator.close(); + } + + public AspectValidationResult validateDocument( String uri ) { + return documentValidationService.validateDocument( uri, documentStore.get( uri ) ); + } + + @Override + public void didOpen( DidOpenTextDocumentParams params ) { + String uri = params.getTextDocument().getUri(); + String content = params.getTextDocument().getText(); + LOGGER.info( "[didOpen] uri={}, contentLength={}", uri, content.length() ); + documentStore.put( uri, content ); + diagnosticsService.updateSyntax( uri, content ); + clientNotifier.publishCombinedDiagnostics( uri ); + } + + @Override + public void didChange( DidChangeTextDocumentParams params ) { + String uri = params.getTextDocument().getUri(); + String content = params.getContentChanges().isEmpty() ? + documentStore.getOrDefault( uri, "" ) : + params.getContentChanges().getLast().getText(); + LOGGER.debug( "[didChange] uri={}, contentLength={}, changes={}", uri, content.length(), params.getContentChanges().size() ); + documentStore.put( uri, content ); + diagnosticsService.updateSyntax( uri, content ); + aspectDiagnosticsWorkflow.onDocumentChanged( uri ); + clientNotifier.publishCombinedDiagnostics( uri ); + } + + @Override + public void didClose( DidCloseTextDocumentParams params ) { + String uri = params.getTextDocument().getUri(); + LOGGER.info( "[didClose] uri={}", uri ); + documentStore.remove( uri ); + aspectDiagnosticsWorkflow.onDocumentClosed( uri ); + clientNotifier.publishEmptyDiagnostics( uri ); + } + + @Override + public void didSave( DidSaveTextDocumentParams params ) { + String uri = params.getTextDocument().getUri(); + String content = documentStore.getOrDefault( uri, "" ); + LOGGER.info( "[didSave] uri={}, contentLength={}", uri, content.length() ); + diagnosticsService.updateSyntax( uri, content ); + clientNotifier.publishCombinedDiagnostics( uri ); + aspectDiagnosticsWorkflow.onDocumentSaved( uri ); + } + + @Override + public CompletableFuture, List>> definition( + DefinitionParams params ) { + String uri = params.getTextDocument().getUri(); + String content = documentStore.get( uri ); + if ( content == null ) { + return CompletableFuture.completedFuture( Either.forLeft( List.of() ) ); + } + + Location declaration = prefixDefinitionService.findPrefixDeclaration( uri, content, params.getPosition() ); + if ( declaration == null ) { + return CompletableFuture.completedFuture( Either.forLeft( List.of() ) ); + } + + return CompletableFuture.completedFuture( Either.forLeft( List.of( declaration ) ) ); + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/lsp/workspace/TurtleWorkspaceService.java b/lsp-server/src/main/java/com/example/turtlelsp/lsp/workspace/TurtleWorkspaceService.java new file mode 100644 index 0000000..e5bfb28 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/lsp/workspace/TurtleWorkspaceService.java @@ -0,0 +1,20 @@ +package com.example.turtlelsp.lsp.workspace; + +import com.example.turtlelsp.lsp.text.TurtleTextDocumentService; + +import org.eclipse.lsp4j.DidChangeConfigurationParams; +import org.eclipse.lsp4j.DidChangeWatchedFilesParams; +import org.eclipse.lsp4j.services.WorkspaceService; + +public class TurtleWorkspaceService implements WorkspaceService { + public TurtleWorkspaceService( TurtleTextDocumentService textDocumentService ) { + } + + @Override + public void didChangeConfiguration( DidChangeConfigurationParams params ) { + } + + @Override + public void didChangeWatchedFiles( DidChangeWatchedFilesParams params ) { + } +} diff --git a/lsp-server/src/main/java/com/example/turtlelsp/turtle/navigation/TurtlePrefixDefinitionService.java b/lsp-server/src/main/java/com/example/turtlelsp/turtle/navigation/TurtlePrefixDefinitionService.java new file mode 100644 index 0000000..65f36e8 --- /dev/null +++ b/lsp-server/src/main/java/com/example/turtlelsp/turtle/navigation/TurtlePrefixDefinitionService.java @@ -0,0 +1,95 @@ +package com.example.turtlelsp.turtle.navigation; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; + +public class TurtlePrefixDefinitionService { + private static final Pattern PREFIX_DECLARATION_PATTERN = Pattern.compile( + "^\\s*@prefix\\s+([A-Za-z][A-Za-z0-9_-]*)?:\\s*<[^>]*>\\s*\\.", + Pattern.CASE_INSENSITIVE + ); + + public Location findPrefixDeclaration(String uri, String content, Position position) { + String prefix = findPrefixAtPosition(content, position); + if (prefix == null) { + return null; + } + + String[] lines = content.split("\\R", -1); + for (int line = 0; line < lines.length; line++) { + Matcher matcher = PREFIX_DECLARATION_PATTERN.matcher(lines[line]); + if (!matcher.find()) { + continue; + } + + String declaredPrefix = matcher.group(1); + String normalizedPrefix = declaredPrefix == null ? "" : declaredPrefix; + if (!normalizedPrefix.equals(prefix)) { + continue; + } + + return new Location(uri, new Range(new Position(line, 0), new Position(line, lines[line].length()))); + } + + return null; + } + + public String findPrefixAtPosition(String content, Position position) { + int lineStart = 0; + int currentLine = 0; + while (currentLine < position.getLine() && lineStart < content.length()) { + if (content.charAt(lineStart++) == '\n') { + currentLine++; + } + } + if (currentLine != position.getLine()) { + return null; + } + + int lineEnd = lineStart; + while (lineEnd < content.length() && content.charAt(lineEnd) != '\n') { + lineEnd++; + } + + int character = Math.max(0, Math.min(position.getCharacter(), lineEnd - lineStart)); + int offset = lineStart + character; + if (offset > lineStart && (offset == lineEnd || !isPrefixedNameChar(content.charAt(offset)))) { + offset--; + } + if (offset < lineStart || offset >= lineEnd || !isPrefixedNameChar(content.charAt(offset))) { + return null; + } + + int start = offset; + while (start > lineStart && isPrefixedNameChar(content.charAt(start - 1))) { + start--; + } + + int end = offset + 1; + while (end < lineEnd && isPrefixedNameChar(content.charAt(end))) { + end++; + } + + String token = content.substring(start, end); + int colonIndex = token.indexOf(':'); + if (colonIndex < 0 || colonIndex == token.length() - 1) { + return null; + } + + String prefix = token.substring(0, colonIndex); + String localPart = token.substring(colonIndex + 1); + if (localPart.isEmpty()) { + return null; + } + + return prefix; + } + + private boolean isPrefixedNameChar(char ch) { + return Character.isLetterOrDigit(ch) || ch == ':' || ch == '_' || ch == '-'; + } +} diff --git a/lsp-server/src/main/resources/log4j2.xml b/lsp-server/src/main/resources/log4j2.xml new file mode 100644 index 0000000..b66e901 --- /dev/null +++ b/lsp-server/src/main/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + logs + ${logDir}/lsp-server.log + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t] %c{1} - %msg%n + + + + + + + + + + + + + + + + + + + diff --git a/lsp-server/src/test/java/com/example/turtlelsp/TurtleDefinitionTest.java b/lsp-server/src/test/java/com/example/turtlelsp/TurtleDefinitionTest.java new file mode 100644 index 0000000..b5ef12b --- /dev/null +++ b/lsp-server/src/test/java/com/example/turtlelsp/TurtleDefinitionTest.java @@ -0,0 +1,63 @@ +package com.example.turtlelsp; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import com.example.turtlelsp.lsp.text.TurtleTextDocumentService; +import org.eclipse.lsp4j.DefinitionParams; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; +import org.eclipse.lsp4j.InitializeResult; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentItem; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.junit.jupiter.api.Test; + +class TurtleDefinitionTest { + @Test + void initializeAdvertisesDefinitionProvider() { + TurtleLanguageServer server = new TurtleLanguageServer(); + + InitializeResult result = server.initialize(null).join(); + + assertThat(result.getCapabilities().getDefinitionProvider().getLeft()).isTrue(); + } + + @Test + void findsDeclaredPrefixDefinition() { + String content = """ + @prefix ex: . + + ex:Alice ex:name "Alice" . + """; + + List locations = definition(content, new Position(2, 1)); + + assertThat(locations).hasSize(1); + assertThat(locations.getFirst().getRange().getStart().getLine()).isZero(); + } + + @Test + void returnsEmptyListWhenDocumentWasNotOpened() { + TurtleTextDocumentService service = new TurtleTextDocumentService(); + Either, List> result = service.definition( + new DefinitionParams(new TextDocumentIdentifier("file:///missing.ttl"), new Position(0, 0)) + ).join(); + + assertThat(result.getLeft()).isEmpty(); + } + + private List definition(String content, Position position) { + TurtleTextDocumentService service = new TurtleTextDocumentService(); + String uri = "file:///test.ttl"; + service.didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "turtle", 1, content))); + + Either, List> result = service.definition( + new DefinitionParams(new TextDocumentIdentifier(uri), position) + ).join(); + + return result.getLeft(); + } +} diff --git a/lsp-server/src/test/java/com/example/turtlelsp/aspect/service/DefaultAspectModelValidationServiceTest.java b/lsp-server/src/test/java/com/example/turtlelsp/aspect/service/DefaultAspectModelValidationServiceTest.java new file mode 100644 index 0000000..94e7507 --- /dev/null +++ b/lsp-server/src/test/java/com/example/turtlelsp/aspect/service/DefaultAspectModelValidationServiceTest.java @@ -0,0 +1,83 @@ +package com.example.turtlelsp.aspect.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Files; +import java.nio.file.Path; + +import com.example.turtlelsp.aspect.model.AspectValidationErrorType; +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class DefaultAspectModelValidationServiceTest { + private final DefaultAspectModelValidationService service = new DefaultAspectModelValidationService(); + + @TempDir + Path tempDir; + + @Test + void validatesAspectModelWhenFileIsValid() throws Exception { + Path modelFile = tempDir.resolve("Aspect.ttl"); + Files.writeString(modelFile, validAspectModel()); + + AspectValidationResult result = service.validate(modelFile); + + assertThat(result.valid()).isTrue(); + assertThat(result.violations()).isEmpty(); + assertThat(result.error()).isNull(); + } + + @Test + void returnsViolationsWhenAspectModelIsInvalid() throws Exception { + Path modelFile = tempDir.resolve("InvalidAspect.ttl"); + Files.writeString(modelFile, invalidSyntaxAspectModel()); + + AspectValidationResult result = service.validate(modelFile); + + assertThat(result.valid()).isFalse(); + assertThat(result.violations()).isNotEmpty(); + assertThat(result.report()).isNotBlank(); + assertThat(result.error()).isNotNull(); + assertThat(result.error().type()).isEqualTo( AspectValidationErrorType.PARSE); + } + + @Test + void returnsLoadErrorWhenFileDoesNotExist() { + Path missingFile = tempDir.resolve("missing.ttl"); + + AspectValidationResult result = service.validate(missingFile); + + assertThat(result.valid()).isFalse(); + assertThat(result.violations()).isEmpty(); + assertThat(result.error()).isNotNull(); + assertThat(result.error().type()).isEqualTo(AspectValidationErrorType.LOAD); + } + + private String validAspectModel() { + return """ + @prefix : . + @prefix samm: . + @prefix samm-c: . + @prefix xsd: . + + :Aspect a samm:Aspect ; + samm:preferredName "Test Aspect"@en ; + samm:description "This is a test description"@en ; + samm:properties ( ) ; + samm:operations ( ) . + """; + } + + private String invalidSyntaxAspectModel() { + return """ + @prefix : . + @prefix samm: . + + :InvalidSyntax a samm:Aspect; + samm:preferredName "Test Aspect"@en + samm:properties () ; + samm:operations () . + """; + } +} diff --git a/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/AspectDiagnosticsWorkflowTest.java b/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/AspectDiagnosticsWorkflowTest.java new file mode 100644 index 0000000..c2760c6 --- /dev/null +++ b/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/AspectDiagnosticsWorkflowTest.java @@ -0,0 +1,118 @@ +package com.example.turtlelsp.lsp.text; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.nio.file.Path; +import java.util.function.BiConsumer; + +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import com.example.turtlelsp.aspect.service.AspectValidationCoordinator; +import org.junit.jupiter.api.Test; + +class AspectDiagnosticsWorkflowTest { + @Test + void onDocumentChangedCancelsValidationAndClearsAspectDiagnostics() { + AspectValidationCoordinator coordinator = mock(AspectValidationCoordinator.class); + DocumentDiagnosticsService diagnosticsService = mock(DocumentDiagnosticsService.class); + TextDocumentClientNotifier notifier = mock(TextDocumentClientNotifier.class); + AspectDiagnosticsWorkflow workflow = new AspectDiagnosticsWorkflow(coordinator, diagnosticsService, notifier); + + workflow.onDocumentChanged("file:///test.ttl"); + + verify(coordinator).cancel("file:///test.ttl"); + verify(diagnosticsService).clearAspect("file:///test.ttl"); + } + + @Test + void onDocumentClosedCancelsValidationAndClearsAllDiagnostics() { + AspectValidationCoordinator coordinator = mock(AspectValidationCoordinator.class); + DocumentDiagnosticsService diagnosticsService = mock(DocumentDiagnosticsService.class); + TextDocumentClientNotifier notifier = mock(TextDocumentClientNotifier.class); + AspectDiagnosticsWorkflow workflow = new AspectDiagnosticsWorkflow(coordinator, diagnosticsService, notifier); + + workflow.onDocumentClosed("file:///test.ttl"); + + verify(coordinator).cancel("file:///test.ttl"); + verify(diagnosticsService).clearAll("file:///test.ttl"); + } + + @Test + void onDocumentSavedForNonFileUriClearsAspectDiagnosticsAndPublishesCombined() { + AspectValidationCoordinator coordinator = mock(AspectValidationCoordinator.class); + DocumentDiagnosticsService diagnosticsService = mock(DocumentDiagnosticsService.class); + TextDocumentClientNotifier notifier = mock(TextDocumentClientNotifier.class); + AspectDiagnosticsWorkflow workflow = new AspectDiagnosticsWorkflow(coordinator, diagnosticsService, notifier); + + workflow.onDocumentSaved("untitled:Aspect.ttl"); + + verify(diagnosticsService).clearAspect("untitled:Aspect.ttl"); + verify(notifier).publishCombinedDiagnostics("untitled:Aspect.ttl"); + verify(coordinator, never()).submit(any(), any(), any(Long.class), any()); + } + + @Test + void onDocumentSavedForFileUriSubmitsValidation() { + AspectValidationCoordinator coordinator = mock(AspectValidationCoordinator.class); + DocumentDiagnosticsService diagnosticsService = mock(DocumentDiagnosticsService.class); + TextDocumentClientNotifier notifier = mock(TextDocumentClientNotifier.class); + AspectDiagnosticsWorkflow workflow = new AspectDiagnosticsWorkflow(coordinator, diagnosticsService, notifier); + String uri = Path.of("pom.xml").toAbsolutePath().toUri().toString(); + when(coordinator.nextGeneration(uri)).thenReturn(7L); + + workflow.onDocumentSaved(uri); + + verify(coordinator).nextGeneration(uri); + verify(coordinator).submit(eq(uri), any(Path.class), eq(7L), any()); + } + + @Test + void onDocumentSavedUpdatesDiagnosticsAndPublishesWhenResultIsCurrent() { + AspectValidationCoordinator coordinator = mock(AspectValidationCoordinator.class); + DocumentDiagnosticsService diagnosticsService = mock(DocumentDiagnosticsService.class); + TextDocumentClientNotifier notifier = mock(TextDocumentClientNotifier.class); + AspectDiagnosticsWorkflow workflow = new AspectDiagnosticsWorkflow(coordinator, diagnosticsService, notifier); + String uri = Path.of("pom.xml").toAbsolutePath().toUri().toString(); + AspectValidationResult result = mock(AspectValidationResult.class); + when(coordinator.nextGeneration(uri)).thenReturn(3L); + when(coordinator.currentGeneration(uri)).thenReturn(3L); + doAnswer(invocation -> { + BiConsumer callback = invocation.getArgument(3); + callback.accept(3L, result); + return null; + }).when(coordinator).submit(eq(uri), any(Path.class), eq(3L), any()); + + workflow.onDocumentSaved(uri); + + verify(diagnosticsService).updateAspect(uri, result); + verify(notifier).publishCombinedDiagnostics(uri); + } + + @Test + void onDocumentSavedIgnoresStaleResult() { + AspectValidationCoordinator coordinator = mock(AspectValidationCoordinator.class); + DocumentDiagnosticsService diagnosticsService = mock(DocumentDiagnosticsService.class); + TextDocumentClientNotifier notifier = mock(TextDocumentClientNotifier.class); + AspectDiagnosticsWorkflow workflow = new AspectDiagnosticsWorkflow(coordinator, diagnosticsService, notifier); + String uri = Path.of("pom.xml").toAbsolutePath().toUri().toString(); + AspectValidationResult result = mock(AspectValidationResult.class); + when(coordinator.nextGeneration(uri)).thenReturn(3L); + when(coordinator.currentGeneration(uri)).thenReturn(4L); + doAnswer(invocation -> { + BiConsumer callback = invocation.getArgument(3); + callback.accept(3L, result); + return null; + }).when(coordinator).submit(eq(uri), any(Path.class), eq(3L), any()); + + workflow.onDocumentSaved(uri); + + verify(diagnosticsService, never()).updateAspect(uri, result); + verify(notifier, never()).publishCombinedDiagnostics(uri); + } + +} diff --git a/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsServiceTest.java b/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsServiceTest.java new file mode 100644 index 0000000..388d37c --- /dev/null +++ b/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsServiceTest.java @@ -0,0 +1,99 @@ +package com.example.turtlelsp.lsp.text; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; + +import com.example.turtlelsp.aspect.diagnostics.AspectDiagnosticMapper; +import com.example.turtlelsp.aspect.model.AspectValidationResult; +import org.eclipse.lsp4j.Diagnostic; +import org.junit.jupiter.api.Test; + +class DocumentDiagnosticsServiceTest { + @Test + void updateSyntaxDelegatesToValidatorAndStore() { + TurtleSyntaxValidationService syntaxValidationService = mock(TurtleSyntaxValidationService.class); + AspectDiagnosticMapper aspectDiagnosticMapper = mock(AspectDiagnosticMapper.class); + DocumentDiagnosticsStore diagnosticsStore = mock(DocumentDiagnosticsStore.class); + DocumentDiagnosticsService service = new DocumentDiagnosticsService( + syntaxValidationService, + aspectDiagnosticMapper, + diagnosticsStore + ); + List diagnostics = List.of(new Diagnostic()); + when(syntaxValidationService.validate("content")).thenReturn(diagnostics); + + service.updateSyntax("file:///test.ttl", "content"); + + verify(syntaxValidationService).validate("content"); + verify(diagnosticsStore).putSyntax("file:///test.ttl", diagnostics); + } + + @Test + void updateAspectDelegatesToMapperAndStore() { + TurtleSyntaxValidationService syntaxValidationService = mock(TurtleSyntaxValidationService.class); + AspectDiagnosticMapper aspectDiagnosticMapper = mock(AspectDiagnosticMapper.class); + DocumentDiagnosticsStore diagnosticsStore = mock(DocumentDiagnosticsStore.class); + DocumentDiagnosticsService service = new DocumentDiagnosticsService( + syntaxValidationService, + aspectDiagnosticMapper, + diagnosticsStore + ); + AspectValidationResult result = mock(AspectValidationResult.class); + List diagnostics = List.of(new Diagnostic()); + when(aspectDiagnosticMapper.toDiagnostics("file:///test.ttl", result)).thenReturn(diagnostics); + + service.updateAspect("file:///test.ttl", result); + + verify(aspectDiagnosticMapper).toDiagnostics("file:///test.ttl", result); + verify(diagnosticsStore).putAspect("file:///test.ttl", diagnostics); + } + + @Test + void clearAspectDelegatesToStore() { + DocumentDiagnosticsStore diagnosticsStore = mock(DocumentDiagnosticsStore.class); + DocumentDiagnosticsService service = new DocumentDiagnosticsService( + mock(TurtleSyntaxValidationService.class), + mock(AspectDiagnosticMapper.class), + diagnosticsStore + ); + + service.clearAspect("file:///test.ttl"); + + verify(diagnosticsStore).clearAspect("file:///test.ttl"); + } + + @Test + void clearAllDelegatesToStore() { + DocumentDiagnosticsStore diagnosticsStore = mock(DocumentDiagnosticsStore.class); + DocumentDiagnosticsService service = new DocumentDiagnosticsService( + mock(TurtleSyntaxValidationService.class), + mock(AspectDiagnosticMapper.class), + diagnosticsStore + ); + + service.clearAll("file:///test.ttl"); + + verify(diagnosticsStore).clear("file:///test.ttl"); + } + + @Test + void getCombinedDelegatesToStore() { + DocumentDiagnosticsStore diagnosticsStore = mock(DocumentDiagnosticsStore.class); + DocumentDiagnosticsService service = new DocumentDiagnosticsService( + mock(TurtleSyntaxValidationService.class), + mock(AspectDiagnosticMapper.class), + diagnosticsStore + ); + List diagnostics = List.of(new Diagnostic()); + when(diagnosticsStore.getCombined("file:///test.ttl")).thenReturn(diagnostics); + + List result = service.getCombined("file:///test.ttl"); + + assertThat(result).isSameAs(diagnostics); + verify(diagnosticsStore).getCombined("file:///test.ttl"); + } +} diff --git a/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsStoreTest.java b/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsStoreTest.java new file mode 100644 index 0000000..7424b66 --- /dev/null +++ b/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/DocumentDiagnosticsStoreTest.java @@ -0,0 +1,43 @@ +package com.example.turtlelsp.lsp.text; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.eclipse.lsp4j.Diagnostic; +import org.junit.jupiter.api.Test; + +class DocumentDiagnosticsStoreTest { + @Test + void combinesSyntaxAndAspectDiagnosticsInOrder() { + DocumentDiagnosticsStore store = new DocumentDiagnosticsStore(); + Diagnostic syntax = new Diagnostic(); + syntax.setMessage("syntax"); + Diagnostic aspect = new Diagnostic(); + aspect.setMessage("aspect"); + + store.putSyntax("file:///test.ttl", List.of(syntax)); + store.putAspect("file:///test.ttl", List.of(aspect)); + + assertThat(store.getCombined("file:///test.ttl")) + .extracting(Diagnostic::getMessage) + .containsExactly("syntax", "aspect"); + } + + @Test + void clearAspectKeepsSyntaxDiagnostics() { + DocumentDiagnosticsStore store = new DocumentDiagnosticsStore(); + Diagnostic syntax = new Diagnostic(); + syntax.setMessage("syntax"); + Diagnostic aspect = new Diagnostic(); + aspect.setMessage("aspect"); + + store.putSyntax("file:///test.ttl", List.of(syntax)); + store.putAspect("file:///test.ttl", List.of(aspect)); + store.clearAspect("file:///test.ttl"); + + assertThat(store.getCombined("file:///test.ttl")) + .extracting(Diagnostic::getMessage) + .containsExactly("syntax"); + } +} diff --git a/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/TextDocumentClientNotifierTest.java b/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/TextDocumentClientNotifierTest.java new file mode 100644 index 0000000..2360635 --- /dev/null +++ b/lsp-server/src/test/java/com/example/turtlelsp/lsp/text/TextDocumentClientNotifierTest.java @@ -0,0 +1,56 @@ +package com.example.turtlelsp.lsp.text; + +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.services.LanguageClient; +import org.junit.jupiter.api.Test; + +class TextDocumentClientNotifierTest { + @Test + void publishCombinedDiagnosticsUsesStoredDiagnostics() { + DocumentDiagnosticsService diagnosticsService = mock(DocumentDiagnosticsService.class); + LanguageClient client = mock(LanguageClient.class); + TextDocumentClientNotifier notifier = new TextDocumentClientNotifier(diagnosticsService); + List diagnostics = List.of(new Diagnostic()); + when(diagnosticsService.getCombined("file:///test.ttl")).thenReturn(diagnostics); + notifier.connect(client); + + notifier.publishCombinedDiagnostics("file:///test.ttl"); + + verify(diagnosticsService).getCombined("file:///test.ttl"); + verify(client).publishDiagnostics(argThat(params -> + "file:///test.ttl".equals(params.getUri()) && params.getDiagnostics().equals(diagnostics) + )); + } + + @Test + void publishEmptyDiagnosticsSendsEmptyList() { + LanguageClient client = mock(LanguageClient.class); + TextDocumentClientNotifier notifier = new TextDocumentClientNotifier(mock(DocumentDiagnosticsService.class)); + notifier.connect(client); + + notifier.publishEmptyDiagnostics("file:///test.ttl"); + + verify(client).publishDiagnostics(argThat(params -> + "file:///test.ttl".equals(params.getUri()) && params.getDiagnostics().isEmpty() + )); + } + + @Test + void publishMethodsDoNothingWithoutClient() { + DocumentDiagnosticsService diagnosticsService = mock(DocumentDiagnosticsService.class); + TextDocumentClientNotifier notifier = new TextDocumentClientNotifier(diagnosticsService); + + notifier.publishCombinedDiagnostics("file:///test.ttl"); + notifier.publishEmptyDiagnostics("file:///test.ttl"); + + verify(diagnosticsService, never()).getCombined("file:///test.ttl"); + } +} diff --git a/lsp-server/src/test/java/com/example/turtlelsp/turtle/navigation/TurtlePrefixDefinitionServiceTest.java b/lsp-server/src/test/java/com/example/turtlelsp/turtle/navigation/TurtlePrefixDefinitionServiceTest.java new file mode 100644 index 0000000..7c77938 --- /dev/null +++ b/lsp-server/src/test/java/com/example/turtlelsp/turtle/navigation/TurtlePrefixDefinitionServiceTest.java @@ -0,0 +1,57 @@ +package com.example.turtlelsp.turtle.navigation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.junit.jupiter.api.Test; + +class TurtlePrefixDefinitionServiceTest { + private final TurtlePrefixDefinitionService service = new TurtlePrefixDefinitionService(); + + @Test + void extractsDeclaredPrefixAtPosition() { + String content = """ + @prefix ex: . + + ex:Alice ex:name "Alice" . + """; + + assertThat(service.findPrefixAtPosition(content, new Position(2, 1))).isEqualTo("ex"); + } + + @Test + void extractsDefaultPrefixAtPosition() { + String content = """ + @prefix : . + + :Entity a :Type . + """; + + assertThat(service.findPrefixAtPosition(content, new Position(2, 1))).isEmpty(); + } + + @Test + void returnsNullWhenPositionIsNotOnPrefixedName() { + String content = """ + @prefix ex: . + + ex:Alice ex:name "Alice" . + """; + + assertThat(service.findPrefixAtPosition(content, new Position(2, 18))).isNull(); + } + + @Test + void findsMatchingPrefixDeclaration() { + String content = """ + @prefix ex: . + + ex:Alice ex:name "Alice" . + """; + + Location location = service.findPrefixDeclaration("file:///test.ttl", content, new Position(2, 1)); + + assertThat(location.getRange().getStart().getLine()).isZero(); + } +} From f1d648ae58e7e3802ff95027b17ee4146942f5db Mon Sep 17 00:00:00 2001 From: e-filchenko-bosh Date: Mon, 20 Apr 2026 14:57:34 +0300 Subject: [PATCH 2/3] make project follow conventions --- README.md | 3 +- extension/CONTRIBUTING.md | 200 +++++++++++++ extension/CONVENTIONS.md | 28 ++ extension/LICENSE | 373 ++++++++++++++++++++++++ extension/README.md | 112 ++++++++ lsp-server/CONTRIBUTING.md | 186 ++++++++++++ lsp-server/CONVENTIONS.md | 102 +++++++ lsp-server/LICENSE | 373 ++++++++++++++++++++++++ lsp-server/NOTICE.md | 573 +++++++++++++++++++++++++++++++++++++ lsp-server/README.md | 45 +++ 10 files changed, 1993 insertions(+), 2 deletions(-) create mode 100644 extension/CONTRIBUTING.md create mode 100644 extension/CONVENTIONS.md create mode 100644 extension/LICENSE create mode 100644 extension/README.md create mode 100644 lsp-server/CONTRIBUTING.md create mode 100644 lsp-server/CONVENTIONS.md create mode 100644 lsp-server/LICENSE create mode 100644 lsp-server/NOTICE.md create mode 100644 lsp-server/README.md diff --git a/README.md b/README.md index 828aaeb..0c725c6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# esmf-vs-code-plugin -VS Code extension for editing Aspect Models +# esmf-vs-code-plugin \ No newline at end of file diff --git a/extension/CONTRIBUTING.md b/extension/CONTRIBUTING.md new file mode 100644 index 0000000..75b71fa --- /dev/null +++ b/extension/CONTRIBUTING.md @@ -0,0 +1,200 @@ +# Contribution Guideline ESMF Visual Studio Code Plugin + +Thank you for your interest in contributing to the ESMF Visual Studio Code Plugin. Use this repository to +contribute to +the project as easy and transparent as possible, whether it is: + +* Reporting a bug +* Submitting a fix +* Proposing new features +* something else + +The ESMF Visual Studio Code Plugin is developed in the context of the [Eclipse Semantic Modeling +Framework](https://projects.eclipse.org/projects/dt.esmf/). It is based on the Semantic Aspect Meta +Model (SAMM) and supports its use. + +# Contributing Source Code (using GitHub) + +* We use this GitHub repository to track issues and feature requests. +* For discussions specific to development, the preferred way is + the [developer mailing list](https://accounts.eclipse.org/mailing-list/esmf-dev). + +## Branching + +We follow +the [Git branching guidance](https://docs.microsoft.com/en-us/azure/devops/repos/git/git-branching-guidance?view=azure-devops). + +More specifically the repository has the following branches: + + name of branch | description +-----------------------------------|------------------------------------------------------------------ + `main` | Contains the latest state of the repository + `v{version_number}-RC{rc_number}` | A "release candidate": A version that freezes major features and + +can be considered a pre-release of the next full release. +`v{version_number}` | A full release of the respective version. +`feature/#{issue_number}-{feature_name}` | Contains the development on a specific feature and is +intended to be merged back into the `main` branch as soon as possible. Note, that it is recommended +for contributors to create and develop feature branches in a personal fork and not the upstream +repository. +`bug/#{issue_number}-{bug_name}` | Contains the development of (usually smaller) changes in files of +the repository that do not introduce new functionality but fix mistakes, errors or inconsistencies. +These branches should be merged back into the `main`branch as soon as possible. + +## Issues + +We use the `Issues` feature of GitHub for tracking all types of work in the repository. + +We distinguish between the following types of issues; + + Issue Types | Description +--------------|----------------------------------------------------------------------------------------- + `Bug Report` | This `Issue` is dedicated to reporting a problem. + `Task` | This `Issue` is used for describing and proposing a new work item (e.g., a new feature) + +If there are issues that link to the same topic, the creator of the issue shall mention those other tasks in the +description. To group tasks that can belong together, one could further create an issue mentioning and describing +the overall user story for the referenced tasks. + +## Pull Requests + +Proposals for changes to the content of the repository are managed through Pull Requests (`PRs`). + +### Opening Pull Requests + +To open such a `PR`, implement the changes in a new `feature branch`. Each `PR` must reference an issue and follows the +naming schema: `-`. For a new `PR` the target branch is the `main` branch while the source +branch is your `feature branch` The `feature branch` branch should be developed in a fork of the upstream repository. +So before working on your first feature, you need to create such a fork (e.g., by pressing the `Fork` button in the top +right corner of the GitHub page) + +When opening a `PR` please consider the following topics: + +* optional: Rebase your development on the branch to which you plan to create the `PR`. +* Each `PR` must be linked to an `Issue`: + - Reference the `Issue` number in the name of your `feature branch` and the description of the `PR`. + - Mention the `Issue` in one of the commit messages associated to the `PR` together with a GitHub keyword like + `closes #IssueNumber` or `fixes #IssuesNumber`. For more details visit the + [GitHub documentation on linking PR with Issues](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) +* Each `PR` should only contain changes related to a single work item. If the changes cover more than one work item or + feature, then create one `PR` per work item. You may need to create new more specific `Issues` to reference if you + split up the work into multiple `feature branches`. +* Commit changes often. A `PR` may contain one or more commits. + +## Eclipse Development Process + +This Eclipse Foundation open project is governed by the Eclipse Foundation Development Process and +operates under the terms of the Eclipse IP Policy. + +* https://eclipse.org/projects/dev_process +* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf + +## Eclipse Contributor Agreement + +In order to be able to contribute to Eclipse Foundation projects you must electronically sign the +Eclipse Contributor Agreement (ECA). + +* http://www.eclipse.org/legal/ECA.php + +The ECA provides the Eclipse Foundation with a permanent record that you agree that each of your +contributions will comply with the commitments documented in the Developer Certificate of Origin +(DCO). Having an ECA on file associated with the email address matching the "Author" field of your +contribution's Git commits fulfills the DCO's requirement that you sign-off on your contributions. + +For more information, please see the Eclipse Committer Handbook: +https://www.eclipse.org/projects/handbook/#resources-commit + +## Commit Messages + +Separate the subject from the body with a blank line because the subject line is shown in the Git +history and should summarize the commit body. Use the body to explain what and why with less focus +on the details of the how. This [blog post](https://chris.beams.io/posts/git-commit/#seven-rules) +has more tips and details. Before you push your commits to a repository, you should squash your +commits into one or more logical units of work, e.g., you should organize a new feature in a single +commit. + +## License Headers & Licensing + +All files contributed require headers - this will ensure the license and copyright clearing at the +end. Also, all contributions must have the same license as the source. The header should follow the +following template: + +``` +/* + * Copyright (c) {YEAR} {NAME OF COMPANY X} + * Copyright (c) {YEAR} {NAME OF COMPANY Y} + * + * See the AUTHORS file(s) distributed with this work for additional + * information regarding authorship. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ +``` + +When using the template, one must replace "{NAME OF COMPANY X}" with the name of the involved +companies and "{YEAR}" with the year of the contribution. For each involved company you need a new +line starting with "Copyright" as outlined in the example. + +The example is taken from a Java source file. If your file is of another type you may have to adapt +the comment syntax accordingly. + +If you use third-party content (e.g., import / include ...), you are required to list each +third-party content explicitly with its version number in the documentation and your pull-request +comment. Please also check used third party material for license compatibility with the MPL-2.0. +E.g. software licensed under GPL, AGPL or, a similar strong copy-left license cannot be approved. + +# Code Conventions + +The ESMF Visual Studio Code Plugin is written in Typescript. Please have a look into our [Code +Conventions](CONVENTIONS.md). + +## Versioning + +We use Semantic Versioning to identify released versions of the ESMF Visual Studio Code Plugin. Semantic Versioning +is +documented [here](https://semver.org). It proposes to have a versioning number with the following +elements: + +```` +Given a version number MAJOR.MINOR.PATCH, increment the: +- MAJOR version when you make incompatible API changes, +- MINOR version when you add functionality in a backwards compatible manner, and +- PATCH version when you make backwards compatible bug fixes. +Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. +```` + +Whereas the Major version must be incremented if the API has backward-incompatible changes (e.g., has breaking changes), +the Minor version must be changed if new backward-compatible features are introduced and, +the Patch version must be incremented if backward-compatible bugfixes are introduced. + +### Breaking Changes + +For the definition of a breaking change, we follow the definition as in the [Microsoft REST API +Guidelines](https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#123-definition-of-a-breaking-change) +which are licensed under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0). This definition +states: + +```` +Changes to the contract of an API are considered a breaking change. Changes that impact the backwards compatibility +of an API are a breaking change. +````` + +### Version Syntax for Specific Environments + +Git version tag + +vX.Y.Z-[pre-release-identifier] + +Examples: + +v1.0.0-RC1, v1.0.0 + +# Resources + +* [For a Repo](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) +* [Issue Creation](https://help.github.com/en/github/managing-your-work-on-github/creating-an-issue) +* [PR Creation](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request) diff --git a/extension/CONVENTIONS.md b/extension/CONVENTIONS.md new file mode 100644 index 0000000..e21b961 --- /dev/null +++ b/extension/CONVENTIONS.md @@ -0,0 +1,28 @@ +# ESMF Code Conventions +The following document contains a compilation of conventions and guidelines to format, structure and +write code for the ESMF Visual Studio Code Plugin schematics. + +## General Conventions + +Our code conventions are based on the [Google Typescript Style Guide](https://google.github.io/styleguide/tsguide.html) +but +detailed and adjusted for the needs of the ESMF Visual Studio Code Plugin JS schematics. + +## Copyright header +See [CONTRIBUTING](CONTRIBUTING.md) + +## Code Recommendations + +This project uses the library [Prettier](https://www.npmjs.com/package/prettier) and should also be created with it, so +that a clear code can be created. + +## Documentation + +### Developer Documentation +Developer documentation is put into a README.md placed in the project root. This should contain documentation like: +* Checking out the source code and getting it to run/build +* Mandatory (external system) dependencies and how to set them up (e.g. databases) +* Configuration options and how to apply them +* General important concepts that are relevant to working on the project but are not directly obvious from the source code + itself. Links to further readings and information, e.g. wiki or other external sources. + diff --git a/extension/LICENSE b/extension/LICENSE new file mode 100644 index 0000000..fa0086a --- /dev/null +++ b/extension/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. \ No newline at end of file diff --git a/extension/README.md b/extension/README.md new file mode 100644 index 0000000..b283371 --- /dev/null +++ b/extension/README.md @@ -0,0 +1,112 @@ +# Turtle LSP Extension + +VS Code extension for the Turtle language server. The extension supports prefix `Go to Definition`, fast syntax feedback while typing, and server-driven heavy Aspect validation for SAMM-style Turtle models. + +## Requirements + +- Java must be available in `PATH`. +- The server project must be checked out next to this extension at `../lsp-server`. +- Build the Maven server JAR before launching the extension: + +```bash +cd ../lsp-server +mvn package +``` + +The extension starts the server from `../lsp-server/target/lsp-server.jar`. + +## Features + +- Prefix `Go to Definition` inside Turtle files. +- Two-level validation: + - Fast feedback on type from the regular Turtle parser diagnostics provided by the server. + - Heavy Aspect validation from the server for model-level issues. +- Manual validation command: + - `Validate Aspect Model Now` +- Standard diagnostics flow: + - errors appear in the editor + - errors appear in `Problems` + +## Run The Server And Extension Together + +1. Build the server in `../lsp-server` with `mvn package`. +2. In this extension project, install dependencies with `npm install`. +3. Compile the extension with `npm run compile`. +4. Press `F5` in VS Code to open an Extension Development Host. +5. Open a Turtle file such as [samples/valid.ttl](/Users/Evgenii_Filchenko/vs-code-project/extension/samples/valid.ttl) or your Aspect model file. + +If the server JAR is missing, the extension shows an error and does not start the language client. + +## Validation Behavior + +Fast feedback on type: + +- Driven by the server's regular Turtle parsing diagnostics. +- Intended for quick editor feedback while you type. + +Heavy Aspect validation: + +- Runs on the server, not in the extension. +- Uses standard LSP diagnostics so the result appears in the editor and in `Problems`. +- Always uses detailed server validation messaging when the server returns report text. +- Always shows visible progress for long-running validation. +- Runs automatically on save and can also be triggered manually. + +When each validation runs: + +- On type: fast syntax feedback only. +- On save: heavy Aspect validation for Turtle documents. +- Manual: `Validate Aspect Model Now` for the active Turtle document. + +## Commands + +- `Turtle LSP: Validate Aspect Model Now` + - Sends a server request for the active Turtle document. + +## UX During Long-Running Validation + +- Manual validation shows a progress notification while the request is running. +- Save-triggered validation always uses a short status-bar progress indicator instead of repeated popups. +- After completion, the user gets a summary that includes the first detailed server report line when available. +- Automatic save validation keeps progress and completion feedback in the status bar. + +## Verify Go To Definition + +Use [samples/valid.ttl](/Users/Evgenii_Filchenko/vs-code-project/extension/samples/valid.ttl): + +1. Open `samples/valid.ttl`. +2. Place the cursor on `foaf:Person`, `foaf:name`, or another prefixed name. +3. Run `Go to Definition`. +4. Confirm that VS Code jumps to the matching `@prefix` declaration. + +Expected behavior: + +- `foaf:*` resolves to `@prefix foaf: ...`. +- `ex:*` resolves to `@prefix ex: ...`. + +## Verify Aspect Validation + +Use an Aspect model file, for example: + +```turtle +@prefix : . +@prefix samm: . + +:InvalidSyntax a samm:Aspect; + samm:preferredName "Test Aspect"@en + samm:properties () ; + samm:operations () . +``` + +Manual check: + +1. Open the model file. +2. Run `Turtle LSP: Validate Aspect Model Now`. +3. Wait for the progress indicator to finish. +4. Confirm that diagnostics appear in the editor and in `Problems`. + +On-save check: + +1. Save the model file. +2. Confirm that the status bar shows validation progress. +3. Confirm that diagnostics are refreshed after completion. \ No newline at end of file diff --git a/lsp-server/CONTRIBUTING.md b/lsp-server/CONTRIBUTING.md new file mode 100644 index 0000000..00c1ad9 --- /dev/null +++ b/lsp-server/CONTRIBUTING.md @@ -0,0 +1,186 @@ +# Contribution Guideline ESMF Visual Studio Code Plugin Conde + +Thank you for your interest in contributing to the ESMF Visual Studio Code Plugin Conde. Use this repository to contribute to +the project as easy and transparent as possible, whether it is: + +* Reporting a bug +* Submitting a fix +* Proposing new features +* something else + +The ESMF Visual Studio Code Plugin Conde is developed in the context of the [Eclipse Semantic Modeling +Framework](https://projects.eclipse.org/projects/dt.esmf/). It is based on the Semantic Aspect Meta +Model (SAMM) and supports its use. + +# Contributing Source Code (using GitHub) + +* We use this GitHub repository to track issues and feature requests. +* For general discussions of the ESMF, modeling questions etc. we use the [ESMF Chat](https://chat.eclipse.org/#/room/#eclipse-semantic-modeling-framework:matrix.eclipse.org). +* For discussions specific to development, the preferred way is the [developer mailing list](https://accounts.eclipse.org/mailing-list/esmf-dev). + +## Branching +We follow the [Git branching guidance](https://docs.microsoft.com/en-us/azure/devops/repos/git/git-branching-guidance?view=azure-devops). + +More specifically the repository has the following branches: + +name of branch | description +----| ---- +`main` | Contains the latest state of the repository +`v{version_number}-RC{rc_number}` | A "release candidate": A version that freezes major features and +can be considered a pre-release of the next full release. +`v{version_number}` | A full release of the respective version. +`feature/#{issue_number}-{feature_name}` | Contains the development on a specific feature and is +intended to be merged back into the `main` branch as soon as possible. Note, that it is recommended +for contributors to create and develop feature branches in a personal fork and not the upstream +repository. +`bug/#{issue_number}-{bug_name}` | Contains the development of (usually smaller) changes in files of +the repository that do not introduce new functionality but fix mistakes, errors or inconsistencies. +These branches should be merged back into the `main`branch as soon as possible. + +## Issues +We use the `Issues` feature of GitHub for tracking all types of work in the repository. + +We distinguish between the following types of issues; + +Issue Types | Description +-------------------| ------------------------------------------------------ +`Bug Report` | This `Issue` is dedicated to reporting a problem. + `Task` | This `Issue` is used for describing and proposing a new work item (e.g., a new feature) + + If there are issues that link to the same topic, the creator of the issue shall mention those other tasks in the + description. To group tasks that can belong together, one could further create an issue mentioning and describing + the overall user story for the referenced tasks. + +## Pull Requests +Proposals for changes to the content of the repository are managed through Pull Requests (`PRs`). + +### Opening Pull Requests +To open such a `PR`, implement the changes in a new `feature branch`. Each `PR` must reference an issue and follows the +naming schema: `-`. For a new `PR` the target branch is the `main` branch while the source +branch is your `feature branch` The `feature branch` branch should be developed in a fork of the upstream repository. +So before working on your first feature, you need to create such a fork (e.g., by pressing the `Fork` button in the top +right corner of the GitHub page) + +When opening a `PR` please consider the following topics: + +* optional: Rebase your development on the branch to which you plan to create the `PR`. +* Each `PR` must be linked to an `Issue`: + - Reference the `Issue` number in the name of your `feature branch` and the description of the `PR`. + - Mention the `Issue` in one of the commit messages associated to the `PR` together with a GitHub keyword like + `closes #IssueNumber` or `fixes #IssuesNumber`. For more details visit the + [GitHub documentation on linking PR with Issues](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) +* Each `PR` should only contain changes related to a single work item. If the changes cover more than one work item or + feature, then create one `PR` per work item. You may need to create new more specific `Issues` to reference if you + split up the work into multiple `feature branches`. +* Commit changes often. A `PR` may contain one or more commits. + +## Eclipse Development Process + +This Eclipse Foundation open project is governed by the Eclipse Foundation Development Process and +operates under the terms of the Eclipse IP Policy. + +* https://eclipse.org/projects/dev_process +* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf + +## Eclipse Contributor Agreement + +In order to be able to contribute to Eclipse Foundation projects you must electronically sign the +Eclipse Contributor Agreement (ECA). + +* http://www.eclipse.org/legal/ECA.php + +The ECA provides the Eclipse Foundation with a permanent record that you agree that each of your +contributions will comply with the commitments documented in the Developer Certificate of Origin +(DCO). Having an ECA on file associated with the email address matching the "Author" field of your +contribution's Git commits fulfills the DCO's requirement that you sign-off on your contributions. + +For more information, please see the Eclipse Committer Handbook: +https://www.eclipse.org/projects/handbook/#resources-commit + +## Commit Messages +Separate the subject from the body with a blank line because the subject line is shown in the Git +history and should summarize the commit body. Use the body to explain what and why with less focus +on the details of the how. This [blog post](https://chris.beams.io/posts/git-commit/#seven-rules) +has more tips and details. Before you push your commits to a repository, you should squash your +commits into one or more logical units of work, e.g., you should organize a new feature in a single +commit. + +## License Headers & Licensing +All files contributed require headers - this will ensure the license and copyright clearing at the +end. Also, all contributions must have the same license as the source. The header should follow the +following template: + +``` +/* + * Copyright (c) {YEAR} {NAME OF COMPANY X} + * Copyright (c) {YEAR} {NAME OF COMPANY Y} + * + * See the AUTHORS file(s) distributed with this work for additional + * information regarding authorship. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ +``` + +When using the template, one must replace "{NAME OF COMPANY X}" with the name of the involved +companies and "{YEAR}" with the year of the contribution. For each involved company you need a new +line starting with "Copyright" as outlined in the example. + +The example is taken from a Java source file. If your file is of another type you may have to adapt +the comment syntax accordingly. + +If you use third-party content (e.g., import / include ...), you are required to list each +third-party content explicitly with its version number in the documentation and your pull-request +comment. Please also check used third party material for license compatibility with the MPL-2.0. +E.g. software licensed under GPL, AGPL or, a similar strong copy-left license cannot be approved. + +# Code Conventions +The ESMF Visual Studio Code Plugin Conde is written in the Java Programming Language. Please have a look into our [Code +Conventions](CONVENTIONS.md). + +## Versioning +We use Semantic Versioning to identify released versions of the ESMF Visual Studio Code Plugin Conde. Semantic Versioning is +documented [here](https://semver.org). It proposes to have a versioning number with the following +elements: + +```` +Given a version number MAJOR.MINOR.PATCH, increment the: +- MAJOR version when you make incompatible API changes, +- MINOR version when you add functionality in a backwards compatible manner, and +- PATCH version when you make backwards compatible bug fixes. +Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. +```` + +Whereas the Major version must be incremented if the API has backward-incompatible changes (e.g., has breaking changes), +the Minor version must be changed if new backward-compatible features are introduced and, +the Patch version must be incremented if backward-compatible bugfixes are introduced. + +### Breaking Changes +For the definition of a breaking change, we follow the definition as in the [Microsoft REST API +Guidelines](https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#123-definition-of-a-breaking-change) +which are licensed under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0). This definition +states: +```` +Changes to the contract of an API are considered a breaking change. Changes that impact the backwards compatibility +of an API are a breaking change. +````` + +### Version Syntax for Specific Environments + +Git version tag + +vX.Y.Z-[pre-release-identifier] + +Examples: + +v1.0.0-RC1, v1.0.0 + +# Resources + +* [For a Repo](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) +* [Issue Creation](https://help.github.com/en/github/managing-your-work-on-github/creating-an-issue) +* [PR Creation](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request) diff --git a/lsp-server/CONVENTIONS.md b/lsp-server/CONVENTIONS.md new file mode 100644 index 0000000..9c3292f --- /dev/null +++ b/lsp-server/CONVENTIONS.md @@ -0,0 +1,102 @@ +# ESMF Code Conventions +The following document contains a compilation of conventions and guidelines to format, structure and +write code for the ESMF VS Code Plugin. + +## General Conventions +Our code conventions are loosely based on the [Google Java Style +Guide](https://google.github.io/styleguide/javaguide.html) but detailed and adjusted for the needs +of the ESMF VS Code Plugin. The code style is described using the Eclipse code style formatter XML and can be +found in the file `.development/esmf-eclipse-codestyle.xml`. + +* If you develop using the Eclipse IDE, you can import this file as project code style. +* If you develop using IntelliJ, you can install the (third part) "Adapter for Eclipse Code + Formatter" plugin, then configure the plugin to use the file. +* In any way, you can use `mvn spotless:check` to validate the code style of current state of your + copy of the code base; and `mvn spotless:apply` to automatically apply the code style to the whole + code base. + +Additional conventions are described using [Checkstyle](https://checkstyle.sourceforge.io/) which can +be found in the file `.development/esmf-checkstyle.xml`. You can validate if your code adheres to the +rules using `mvn checkstyle:check`. + +Furthermore, the files `.development/esmf-intellij-codestyle.xml` and +`.development/esmf-intellij-inspections.xml` are provided that can be +[imported](https://www.jetbrains.com/help/idea/configuring-code-style.html#import-export-schemes) in +the Java code style settings and +[imported](https://www.jetbrains.com/help/idea/inspections-settings.html#profile_management) in the +Inspections, respectively, in the IntelliJ IDEA IDE. Note however, that there might be slight +differences in automatic formatting due to technical limitations; the leading code style description +is esmf-eclipse-codestyle.xml as described above. + +## Copyright header +See [CONTRIBUTING](CONTRIBUTING.md) + +## Code Recommendations + +### Utility Classes +[Utility classes](https://wiki.c2.com/?UtilityClasses) as such should be avoided - domain concepts +must be expressed in domain classes. Thus, only for "real", non-domain operations not belonging to +any class, utility methods and classes may be used. However, chances are pretty close to 100% that +all of everyday utility needs are already covered by high-quality 3rd-party libraries. + +Usually we apply the following rule to decide on the introduction of new libraries +1. Check your framework's and its dependency's utility/static constants classes (e.g. Spring or Vert.x) +2. If not covered, use Guava (https://github.com/google/guava/wiki) +3. If not covered, use Apache Commons (usually .lang module, https://commons.apache.org) +4. If really not covered, write your own (highly unlikely) + +### Optional<> usage +The Optional<> type, common for some time in Guava and in the Java core since Version 8, has found +widespread use for return values, however still a lot of discussions emerge concerning a fitting +scope of usage. You may not return null where an Optional<> is expected Whenever an Optional<> is +passed, you may safely assume it to be non-null. So the following snippet must never appear +anywhere: +``` +if (someOptional != null && someOptional.isPresent()) ... +``` + +* Using Optional<> as return types for values that might be missing is always fine +* Using Optional<> for fields is fine (see notes about "Avoid optionality" though) +* Using Optional<> for method parameters is fine (see notes about "Avoid optionality" though) +* Writing if-Statements checking for a present instance (and calling .get() explicitly) is considered an anti-pattern. You should + 1. Use .map() or .ifPresent() functional style patterns + 2. Use .orElse*() methods for clearly defined fallbacks (or exceptions) +* When having collections/streams of Optionals use .filter(Optional::isPresent) accordingly +* Using Optional.ofNullable(someValue).orElseThrow() to create one-liner check/assignment combinations is considered an anti-pattern. +* You should be using Objects.requireNonNull() for those sort of checks (or Guava's Preconditions if you're having more types of assertions than non-null and aim for a maximum of consistency). + +### Lombok +Lombok was used in the past, but is not used anymore in the ESMF VS Code Plugin. Instead of the `@Value` or +`@Data` annotations, consider using Java records. + +## Documentation + +### Source Code Documentation +Public classes and interfaces should carry appropriate JavaDoc explaining the responsibility of the +class. All public methods except getters/setters/toString etc. must be documented as well. Private +methods should be simple enough and well-named such that they don't need documentation. If +appropriate they of course may be documented as well. Inline comments, especially those that merely +separate logical blocks of code, must be avoided as they are usually an indicator that a private +method can be extracted or that bad naming was used that needs explaining. + +### Developer Documentation +Developer documentation is put into a README.md placed in the project root. This should contain documentation like: +* Checking out the source code and getting it to run/build +* Mandatory (external system) dependencies and how to set them up (e.g. databases) +* Configuration options and how to apply them +* General important concepts that are relevant to working on the project but are not directly obvious from the source code +itself. Links to further readings and information, e.g. wiki or other external sources. + +### User documentation +User documentation (this includes technical documentation on how to use an application or tool from the project) should be on +its own. +It is written in AsciiDoc, rendered with [Antora](https://antora.org) and the generated static content is +publically hosted for direct user access. +The source files of the documentation are placed in a subfolder /documentation from the project root. +Documentation is structured so that it can be processed by Antora. This e.g. involves structuring the documentation files +according to [Antora's specification](https://docs.antora.org/antora/2.3/organize-content-files/) and organizing resources +so that Antora [can handle them](https://docs.antora.org/antora/2.3/page/resource-id/). +[AsciiDoc's syntax](https://docs.antora.org/antora/2.3/asciidoc/asciidoc/) is pretty close to Markdown, however it is +way more targeted towards writing fully fledged documents and with its multitude of backends (HTML, PDF, ...) it is a +very good source format. +Publishing is realized by means of [Github pages](https://docs.antora.org/antora/2.3/publish-to-github-pages/). diff --git a/lsp-server/LICENSE b/lsp-server/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/lsp-server/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/lsp-server/NOTICE.md b/lsp-server/NOTICE.md new file mode 100644 index 0000000..4f646ee --- /dev/null +++ b/lsp-server/NOTICE.md @@ -0,0 +1,573 @@ +## panzoom:9.4.2 + +This package contains the following copyright statements and is licensed under the following declared licenses. +### Copyright Statements + +Copyright (c) 2016 - 2021 Andrei Kashcha + +### Declared License (MIT) +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +## tailwindcss:2.2.7 + +This package contains the following copyright statements and is licensed under the following declared licenses. +### Copyright Statements + +Copyright (c) Adam Wathan +Copyright (c) Jonathan Reinink + +### Declared License (MIT) +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +## tcobot:4.11-1 + +This package contains the following copyright statements and is licensed under the following declared licenses. +### Copyright Statements + +Copyright (c) 2016 Tim Scanlin + +### Declared License (MIT) +``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + + +## RobotoCondensed-Regular + +This package contains the following copyright statements and is licensed under the following declared licenses. +### Copyright Statements + +Copyright (C) Christian Robertson + + +### Declared License (Apache-2.0) +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +``` + +## normalize.css:2.1.2 + +This package contains the following copyright statements and is licensed under the following declared licenses. +### Copyright Statements + +Copyright (C) Nicolas Gallagher + +### Declared License (MIT) +``` +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +## Font Awesome:4.7.0 + +This package contains the following copyright statements and is licensed under the following declared licenses. +### Copyright Statements + +Copyright (C) @davegandy + +### Declared License (MIT) +``` +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### Declared License (SIL OFL 1.1) +``` +Copyright (c) , (), +with Reserved Font Name . +Copyright (c) , (), +with Reserved Font Name . +Copyright (c) , (). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +``` + +## Font Cairo:3.116 + +This package contains the following copyright statements and is licensed under the following declared licenses. +### Copyright Statements + +Copyright (C) Mohamed Gaber + +### Declared License (SIL OFL 1.1) +``` +Copyright (c) , (), +with Reserved Font Name . +Copyright (c) , (), +with Reserved Font Name . +Copyright (c) , (). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +``` diff --git a/lsp-server/README.md b/lsp-server/README.md new file mode 100644 index 0000000..2949415 --- /dev/null +++ b/lsp-server/README.md @@ -0,0 +1,45 @@ +# ESMF VS Code Plugin + +## Table of Contents + +- [Introduction](#introduction) +- [Build and contribute](#build-and-contribute) +- [Project Structure](#project-structure) +- [License](#license) + +## Introduction + +TODO + +## Build and contribute + +The top level elements of the Project Structure are all carried out as Maven multimodule projects. +Building the SDK requires Java 25. + +To build the project, run the following command: +```bash +mvn clean install +``` + +We are always looking forward to your contributions. For more details on how to contribute just take +a look at the [contribution guidelines](CONTRIBUTING.md). Please create an issue first before +opening a pull request. + +To quickly check if your contribution adheres to the project conventions, you can run `mvn +spotless:check` and `mvn checkstyle:check`; to automatically apply the project code style to your +changes, you can also use `mvn spotless:apply`. For more details, please see our +[conventions](CONVENTIONS.md.) + +## Project Structure + +TODO + + +## License + +SPDX-License-Identifier: MPL-2.0 + +This program and the accompanying materials are made available under the terms of the +[Mozilla Public License, v. 2.0](LICENSE). + +The [Notice file](NOTICE.md) details contained third party materials. From 10b110e9e2c9ae6a6d9e6a0b1aae02b85dc2d6e6 Mon Sep 17 00:00:00 2001 From: Andreas Textor Date: Tue, 21 Apr 2026 08:13:46 +0200 Subject: [PATCH 3/3] Remove unintended local file system path --- extension/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extension/README.md b/extension/README.md index b283371..38c65f0 100644 --- a/extension/README.md +++ b/extension/README.md @@ -33,7 +33,7 @@ The extension starts the server from `../lsp-server/target/lsp-server.jar`. 2. In this extension project, install dependencies with `npm install`. 3. Compile the extension with `npm run compile`. 4. Press `F5` in VS Code to open an Extension Development Host. -5. Open a Turtle file such as [samples/valid.ttl](/Users/Evgenii_Filchenko/vs-code-project/extension/samples/valid.ttl) or your Aspect model file. +5. Open a Turtle file such as [samples/valid.ttl](samples/valid.ttl) or your Aspect model file. If the server JAR is missing, the extension shows an error and does not start the language client. @@ -109,4 +109,4 @@ On-save check: 1. Save the model file. 2. Confirm that the status bar shows validation progress. -3. Confirm that diagnostics are refreshed after completion. \ No newline at end of file +3. Confirm that diagnostics are refreshed after completion.