diff --git a/app/package-lock.json b/app/package-lock.json index 9fb57d3..dce3e8a 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -12,8 +12,12 @@ "@blueprintjs/icons": "5.15.1", "@blueprintjs/select": "5.3.6", "@blueprintjs/table": "5.3.0", + "@monaco-editor/react": "^4.6.0", "@xyflow/react": "^12.3.6", "axios": "1.7.9", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.468.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.4.0", @@ -24,13 +28,18 @@ }, "devDependencies": { "@eslint/js": "9.16.0", + "@types/node": "^22.10.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "4.3.4", + "autoprefixer": "^10.4.20", "eslint": "9.16.0", + "eslint-import-resolver-typescript": "^3.7.0", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-react-hooks": "5.1.0", "eslint-plugin-react-refresh": "0.4.16", "globals": "15.13.0", + "postcss": "^8.4.49", "sass-embedded": "1.82.0", "typescript": "5.7.2", "typescript-eslint": "8.18.0", @@ -1126,6 +1135,32 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@monaco-editor/loader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", + "integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==", + "license": "MIT", + "dependencies": { + "state-local": "^1.0.6" + }, + "peerDependencies": { + "monaco-editor": ">= 0.21.0 < 1" + } + }, + "node_modules/@monaco-editor/react": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz", + "integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==", + "license": "MIT", + "dependencies": { + "@monaco-editor/loader": "^1.4.0" + }, + "peerDependencies": { + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1164,6 +1199,16 @@ "node": ">= 8" } }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -1398,6 +1443,13 @@ "win32" ] }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1512,6 +1564,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", @@ -1882,12 +1951,185 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/axios": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", @@ -1970,6 +2212,56 @@ "dev": true, "license": "MIT/X11" }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2042,6 +2334,18 @@ "tslib": "^2.0.3" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/classcat": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", @@ -2054,6 +2358,35 @@ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/colorjs.io": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", @@ -2226,6 +2559,60 @@ "node": ">=12" } }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -2251,6 +2638,42 @@ "dev": true, "license": "MIT" }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2260,6 +2683,19 @@ "node": ">=0.4.0" } }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -2280,6 +2716,21 @@ "tslib": "^2.0.3" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.65", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz", @@ -2287,19 +2738,172 @@ "dev": true, "license": "ISC" }, - "node_modules/esbuild": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", - "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">=18" - }, + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.23.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.6.tgz", + "integrity": "sha512-Ifco6n3yj2tMZDWNLyloZrytt9lqqlwvS83P3HtaETR0NUOYnIULGGHpktqYGObGy+8wc1okO25p8TjemhImvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.7", + "get-intrinsic": "^1.2.6", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.0.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-regex-test": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.3", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.0", "@esbuild/android-arm": "0.24.0", @@ -2410,6 +3014,136 @@ } } }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.7.0.tgz", + "integrity": "sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.3.7", + "enhanced-resolve": "^5.15.0", + "fast-glob": "^3.3.2", + "get-tsconfig": "^4.7.5", + "is-bun-module": "^1.0.2", + "is-glob": "^4.0.3", + "stable-hash": "^0.0.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", @@ -2496,26 +3230,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/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/eslint/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/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2732,181 +3446,815 @@ "engines": { "node": ">=4.0" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.7.tgz", + "integrity": "sha512-2g4x+HqTJKM9zcJqBSpjoRmdcPFtJM60J3xJisTQSXBWka5XqyBN/2tNUgma1mztTXyDuUsEtYe5qcs7xYzYQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", + "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "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/globals": { + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz", + "integrity": "sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "license": "MIT", + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "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/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz", + "integrity": "sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.6.3" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", + "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, "engines": { - "node": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "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": ">=6.9.0" + "node": ">=0.12.0" } }, - "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==", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globals": { - "version": "15.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz", - "integrity": "sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==", + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/header-case": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", - "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, "license": "MIT", "dependencies": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/immutable": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", - "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/is-typed-array": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.14.tgz", + "integrity": "sha512-lQUsHzcTb7rH57dajbOuZEuMDXjs9f04ZloER4QOpjpKcaw4f98BRUrs8aiO9Z4G7i7B0Xhgarg6SCgYcYi8Nw==", "dev": true, "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, "engines": { - "node": ">=0.8.19" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "call-bound": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, "engines": { - "node": ">=0.12.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2914,6 +4262,18 @@ "dev": true, "license": "ISC" }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3058,6 +4418,25 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.468.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", + "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/math-intrinsics": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz", + "integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3116,6 +4495,23 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/monaco-editor": { + "version": "0.52.2", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz", + "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==", + "license": "MIT", + "peer": true + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3166,6 +4562,16 @@ "dev": true, "license": "MIT" }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/normalize.css": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", @@ -3181,6 +4587,100 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3294,6 +4794,13 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3314,6 +4821,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.49", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", @@ -3343,6 +4860,13 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3569,12 +5093,72 @@ } } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", + "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "dunder-proto": "^1.0.0", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "license": "MIT" }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.9", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", + "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3585,6 +5169,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3666,6 +5260,44 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/sass-embedded": { "version": "1.82.0", "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.82.0.tgz", @@ -4087,6 +5719,40 @@ "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", "license": "MIT" }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4110,6 +5776,82 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -4130,6 +5872,88 @@ "node": ">=0.10.0" } }, + "node_modules/stable-hash": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", + "integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", + "license": "MIT" + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4159,6 +5983,19 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/sync-child-process": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", @@ -4182,6 +6019,16 @@ "node": ">=16.0.0" } }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4208,6 +6055,32 @@ "typescript": ">=4.2.0" } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", @@ -4233,6 +6106,84 @@ "node": ">= 0.8.0" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz", + "integrity": "sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", @@ -4270,6 +6221,32 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -4455,6 +6432,93 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", + "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/app/package.json b/app/package.json index 0547816..6ace774 100644 --- a/app/package.json +++ b/app/package.json @@ -14,8 +14,12 @@ "@blueprintjs/icons": "5.15.1", "@blueprintjs/select": "5.3.6", "@blueprintjs/table": "5.3.0", + "@monaco-editor/react": "^4.6.0", "@xyflow/react": "^12.3.6", "axios": "1.7.9", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.468.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.4.0", @@ -26,16 +30,21 @@ }, "devDependencies": { "@eslint/js": "9.16.0", + "@types/node": "^22.10.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "4.3.4", + "autoprefixer": "^10.4.20", "eslint": "9.16.0", + "eslint-import-resolver-typescript": "^3.7.0", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-react-hooks": "5.1.0", "eslint-plugin-react-refresh": "0.4.16", "globals": "15.13.0", + "postcss": "^8.4.49", "sass-embedded": "1.82.0", "typescript": "5.7.2", "typescript-eslint": "8.18.0", "vite": "6.0.3" } -} +} \ No newline at end of file diff --git a/app/src/components/OneLineMonacoEditor/hooks/useRegisterCompletionItemProvider.ts b/app/src/components/OneLineMonacoEditor/hooks/useRegisterCompletionItemProvider.ts new file mode 100644 index 0000000..a9f34d7 --- /dev/null +++ b/app/src/components/OneLineMonacoEditor/hooks/useRegisterCompletionItemProvider.ts @@ -0,0 +1,36 @@ +import { useMonaco } from '@monaco-editor/react'; +import { languages } from 'monaco-editor'; +import { useEffect } from 'react'; + +const useRegisterCompletionItemProvider = ( + languageId: string, + completionItemProviders: languages.CompletionItemProvider[], +) => { + const monaco = useMonaco(); + + useEffect(() => { + if (!monaco) return; + if ( + monaco.languages + .getLanguages() + .findIndex(lang => lang.id === languageId) === -1 + ) { + return; + } + + const disposers = completionItemProviders.map(provider => { + return monaco.languages.registerCompletionItemProvider( + languageId, + provider, + ); + }); + + return () => { + disposers.forEach(dispose => dispose.dispose()); + }; + }, [monaco, languageId, completionItemProviders]); + + return; +}; + +export default useRegisterCompletionItemProvider; diff --git a/app/src/components/OneLineMonacoEditor/hooks/useRegisterLanguage.ts b/app/src/components/OneLineMonacoEditor/hooks/useRegisterLanguage.ts new file mode 100644 index 0000000..ac87a68 --- /dev/null +++ b/app/src/components/OneLineMonacoEditor/hooks/useRegisterLanguage.ts @@ -0,0 +1,28 @@ +import { useMonaco } from '@monaco-editor/react'; +import { languages } from 'monaco-editor'; + +const useRegisterLanguage = ( + languageId: string, + languageDef: languages.IMonarchLanguage, + languageConfiguration: languages.LanguageConfiguration, +) => { + const monaco = useMonaco(); + + if (!monaco) return; + + if ( + monaco.languages + .getLanguages() + .findIndex(lang => lang.id === languageId) !== -1 + ) { + return; + } + + monaco.languages.register({ id: languageId }); + monaco.languages.setMonarchTokensProvider(languageId, languageDef); + monaco.languages.setLanguageConfiguration(languageId, languageConfiguration); + + return languageId; +}; + +export default useRegisterLanguage; diff --git a/app/src/components/OneLineMonacoEditor/hooks/useRegisterTheme.ts b/app/src/components/OneLineMonacoEditor/hooks/useRegisterTheme.ts new file mode 100644 index 0000000..a3787f8 --- /dev/null +++ b/app/src/components/OneLineMonacoEditor/hooks/useRegisterTheme.ts @@ -0,0 +1,17 @@ +import { useMonaco } from '@monaco-editor/react'; +import { editor } from 'monaco-editor'; +import { useEffect } from 'react'; + +const useRegisterTheme = ( + themeName: string, + themeData: editor.IStandaloneThemeData, +) => { + const monaco = useMonaco(); + useEffect(() => { + if (monaco) { + monaco.editor.defineTheme(themeName, themeData); + } + }, [monaco, themeName, themeData]); +}; + +export default useRegisterTheme; diff --git a/app/src/components/OneLineMonacoEditor/index.tsx b/app/src/components/OneLineMonacoEditor/index.tsx new file mode 100644 index 0000000..d3abdd6 --- /dev/null +++ b/app/src/components/OneLineMonacoEditor/index.tsx @@ -0,0 +1,83 @@ +'use client'; + +import { Editor } from '@monaco-editor/react'; +import { IKeyboardEvent, editor } from 'monaco-editor'; + +import { useRef } from 'react'; + +const OneLineMonaco = ({ + value, + language = 'mapping', + onChange, + disabled, + ...rest +}: { + value: string; + language?: string; + onChange: (value: string | undefined) => void; + disabled?: boolean; + [key: string]: unknown; +}) => { + const editorRef = useRef(null); + + const onEnter = (e: IKeyboardEvent) => { + if (editorRef.current === null) return; + // Write a code that prevents new lines + if (e.code === 'Enter' || e.code === 'NumpadEnter') { + e.preventDefault(); + } + }; + + function handleEditorDidMount(editor: editor.ICodeEditor) { + editorRef.current = editor; + + editor.onKeyDown(onEnter); + // Now you can use the instance of monaco editor + // in this component whenever you want + } + + // Prevent new lines + + return ( + + ); +}; + +export default OneLineMonaco; diff --git a/app/src/consts/mapping-language.ts b/app/src/consts/mapping-language.ts new file mode 100644 index 0000000..73fa90d --- /dev/null +++ b/app/src/consts/mapping-language.ts @@ -0,0 +1,12 @@ +import { languages } from 'monaco-editor'; + +export const mapping_language: languages.IMonarchLanguage = { + tokenizer: { + root: [ + // Regex that will capture the prefixes like a: ex: etc + ['[a-zA-Z0-9-_]+:', 'prefix'], + // Regex that will capture the references like $(abcABC) it can use any character/digit and - and _ except ()$ + ['\\$\\([a-zA-Z0-9-_]+\\)', 'ref'], + ], + }, +}; diff --git a/app/src/consts/mapping-theme.ts b/app/src/consts/mapping-theme.ts new file mode 100644 index 0000000..f96a17d --- /dev/null +++ b/app/src/consts/mapping-theme.ts @@ -0,0 +1,17 @@ +import { editor } from 'monaco-editor'; + +const mapping_theme: editor.IStandaloneThemeData = { + base: 'vs-dark', + inherit: true, + encodedTokensColors: [], + colors: {}, + rules: [ + { token: 'prefix', foreground: 'FFA500' }, // Orange for prefixes + { token: 'ref', foreground: 'FF00FF' }, // Purple for references + { token: 'protocol', foreground: '00FF00' }, // Green for protocols + { token: 'domain', foreground: 'FF0000' }, // Red for domains + { token: 'path', foreground: '0000FF' }, // Blue for paths + ], +}; + +export default mapping_theme; diff --git a/app/src/consts/xsdTypes.ts b/app/src/consts/xsdTypes.ts new file mode 100644 index 0000000..ef70118 --- /dev/null +++ b/app/src/consts/xsdTypes.ts @@ -0,0 +1,175 @@ +import { OntologyClass } from '@/lib/api/ontology_api/types'; + +export const xsdTypes: OntologyClass[] = [ + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#string', + label: [{ value: 'string' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#boolean', + label: [{ value: 'boolean' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#decimal', + label: [{ value: 'decimal' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#float', + label: [{ value: 'float' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#double', + label: [{ value: 'double' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#duration', + label: [{ value: 'duration' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#dateTime', + label: [{ value: 'dateTime' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#time', + label: [{ value: 'time' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#date', + label: [{ value: 'date' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#gYearMonth', + label: [{ value: 'gYearMonth' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#gYear', + label: [{ value: 'gYear' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#gMonthDay', + label: [{ value: 'gMonthDay' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#gDay', + label: [{ value: 'gDay' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#gMonth', + label: [{ value: 'gMonth' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#hexBinary', + label: [{ value: 'hexBinary' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#base64Binary', + label: [{ value: 'base64Binary' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#anyURI', + label: [{ value: 'anyURI' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#QName', + label: [{ value: 'QName' }], + super_classes: [], + is_deprecated: false, + }, + { + type: 'class', + belongs_to: 'http://www.w3.org/2001/XMLSchema#', + description: [], + full_uri: 'http://www.w3.org/2001/XMLSchema#NOTATION', + label: [{ value: 'NOTATION' }], + super_classes: [], + is_deprecated: false, + }, +]; diff --git a/app/src/hooks/useErrorToast.ts b/app/src/hooks/useErrorToast.ts index 3ff4b2b..9674970 100644 --- a/app/src/hooks/useErrorToast.ts +++ b/app/src/hooks/useErrorToast.ts @@ -1,5 +1,5 @@ +import toast from '@/consts/toast'; import { useEffect } from 'react'; -import toast from '../consts/toast'; const useErrorToast = (error: string | null) => { useEffect(() => { diff --git a/app/src/index.scss b/app/src/index.scss index 94c5ad1..eabfcdb 100644 --- a/app/src/index.scss +++ b/app/src/index.scss @@ -4,6 +4,7 @@ @use '@blueprintjs/select/lib/css/blueprint-select.css'; @use '@blueprintjs/table/lib/css/table.css'; + .bp5-dark { background-color: #293742; } diff --git a/app/src/lib/api/mapping_service/types.ts b/app/src/lib/api/mapping_service/types.ts index db8187e..53d4a19 100644 --- a/app/src/lib/api/mapping_service/types.ts +++ b/app/src/lib/api/mapping_service/types.ts @@ -1,5 +1,3 @@ -import { Property } from '../ontology_api/types'; - export type MappingNodeType = 'entity' | 'literal' | 'uri_ref'; export type MappingNode = { @@ -8,7 +6,7 @@ export type MappingNode = { label: string; uri_pattern: string; rdf_type: string[]; - properties: Property[]; + properties: string[]; }; export type MappingLiteral = { diff --git a/app/src/lib/api/ontology_api/types.ts b/app/src/lib/api/ontology_api/types.ts index b83437d..3f6f3dc 100644 --- a/app/src/lib/api/ontology_api/types.ts +++ b/app/src/lib/api/ontology_api/types.ts @@ -7,17 +7,9 @@ interface Literal { language?: string; } -enum NamedNodeType { - CLASS = 'class', - INDIVIDUAL = 'individual', - PROPERTY = 'property', -} +type NamedNodeType = 'class' | 'individual' | 'property'; -enum PropertyType { - OBJECT = 'object', - DATATYPE = 'datatype', - ANNOTATION = 'annotation', -} +type PropertyType = 'object' | 'datatype' | 'annotation' | 'any'; interface NamedNode { /** @@ -35,7 +27,7 @@ interface Individual extends NamedNode { /** * An individual is a named node that is an instance of a class. */ - type: NamedNodeType.INDIVIDUAL; + type: 'individual'; } interface OntologyClass extends NamedNode { @@ -43,7 +35,7 @@ interface OntologyClass extends NamedNode { * A class is a named node that is a class. */ super_classes: string[]; - type: NamedNodeType.CLASS; + type: 'class'; } interface Property extends NamedNode { @@ -53,7 +45,7 @@ interface Property extends NamedNode { property_type: PropertyType; range: string[]; domain: string[]; - type: NamedNodeType.PROPERTY; + type: 'property'; } interface Ontology { diff --git a/app/src/lib/api/source_api/index.ts b/app/src/lib/api/source_api/index.ts new file mode 100644 index 0000000..e13aaa2 --- /dev/null +++ b/app/src/lib/api/source_api/index.ts @@ -0,0 +1,28 @@ +import ApiService from '../../services/api_service'; +import { Source } from './types'; + +class SourceApi { + private static getApiClient(): ApiService { + return ApiService.getInstance('default'); + } + + public static async getSource(sourceUuid: string): Promise { + const result = await this.getApiClient().callApi( + `/sources/${sourceUuid}`, + { + method: 'GET', + parser: data => data as Source, + }, + ); + + if (result.type === 'success') { + return result.data; + } + + throw new Error( + `Failed to get source: ${result.message} (status: ${result.status})`, + ); + } +} + +export default SourceApi; diff --git a/app/src/lib/api/source_api/types.ts b/app/src/lib/api/source_api/types.ts new file mode 100644 index 0000000..0d2bfd9 --- /dev/null +++ b/app/src/lib/api/source_api/types.ts @@ -0,0 +1,7 @@ +export interface Source { + uuid: string; + type: 'csv' | 'json'; + references: string[]; + file_uuid: string; + extra: Record; +} diff --git a/app/src/pages/mapping_page/components/MainPanel/components/BaseHandle.tsx b/app/src/pages/mapping_page/components/MainPanel/components/BaseHandle.tsx new file mode 100644 index 0000000..fbce297 --- /dev/null +++ b/app/src/pages/mapping_page/components/MainPanel/components/BaseHandle.tsx @@ -0,0 +1,26 @@ +import { cn } from '@/utils/cn'; +import { Handle, HandleProps } from '@xyflow/react'; +import React from 'react'; + +export const BaseHandle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & HandleProps +>(({ className, ...props }, ref) => ( + +)); +BaseHandle.displayName = 'BaseHandle'; diff --git a/app/src/pages/mapping_page/components/MainPanel/components/BaseNode.tsx b/app/src/pages/mapping_page/components/MainPanel/components/BaseNode.tsx index e69de29..c086230 100644 --- a/app/src/pages/mapping_page/components/MainPanel/components/BaseNode.tsx +++ b/app/src/pages/mapping_page/components/MainPanel/components/BaseNode.tsx @@ -0,0 +1,20 @@ +import { cn } from '@/utils/cn'; +import React from 'react'; + +export const BaseNode = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & { selected?: boolean } +>(({ className, selected, ...props }, ref) => ( +
+)); +BaseNode.displayName = 'BaseNode'; diff --git a/app/src/pages/mapping_page/components/MainPanel/components/ConnectionLineComponent.tsx b/app/src/pages/mapping_page/components/MainPanel/components/ConnectionLineComponent.tsx new file mode 100644 index 0000000..f8c25da --- /dev/null +++ b/app/src/pages/mapping_page/components/MainPanel/components/ConnectionLineComponent.tsx @@ -0,0 +1,25 @@ +import { getSmoothStepPath } from '@xyflow/react'; + +const ConnectionLineComponent = ({ + fromX, + fromY, + toX, + toY, +}: { + fromX: number; + fromY: number; + toX: number; + toY: number; +}) => { + const [edgePath] = getSmoothStepPath({ + sourceX: fromX, + sourceY: fromY, + targetX: toX, + targetY: toY, + borderRadius: 10, + }); + + return ; +}; + +export default ConnectionLineComponent; diff --git a/app/src/pages/mapping_page/components/MainPanel/components/EntityNode.tsx b/app/src/pages/mapping_page/components/MainPanel/components/EntityNode.tsx index 76a675e..e94d8cb 100644 --- a/app/src/pages/mapping_page/components/MainPanel/components/EntityNode.tsx +++ b/app/src/pages/mapping_page/components/MainPanel/components/EntityNode.tsx @@ -1,35 +1,87 @@ -import { Card, H3 } from '@blueprintjs/core'; -import { Handle, NodeProps, NodeResizer, Position } from '@xyflow/react'; -import { EntityNodeType } from '../types'; +import { Property } from '@/lib/api/ontology_api/types'; +import { BaseNode } from '@/pages/mapping_page/components/MainPanel/components/BaseNode'; +import { LabeledHandle } from '@/pages/mapping_page/components/MainPanel/components/LabeledHandle'; +import { EntityNodeType } from '@/pages/mapping_page/components/MainPanel/types'; +import useMappingPage from '@/pages/mapping_page/state'; +import { Card, HTMLTable, Icon } from '@blueprintjs/core'; +import { NodeProps } from '@xyflow/react'; +import { useEffect, useState } from 'react'; + +export function EntityNode({ data, selected }: NodeProps) { + const ontologies = useMappingPage(state => state.ontologies); + + const [properties, setProperties] = useState([]); + + useEffect(() => { + if (ontologies) { + const allProperties = ontologies.flatMap(ontology => ontology.properties); + const _properties = data.properties.map(property_1 => { + const _property = allProperties.find( + property_2 => property_2.full_uri === property_1, + ); + return ( + _property ?? + ({ + type: 'property', + belongs_to: 'Created', + description: [], + domain: [], + full_uri: property_1, + label: [], + property_type: 'any', + range: [], + is_deprecated: false, + } as Property) + ); + }); + setProperties(_properties); + } + }, [ontologies, data.properties]); -const EntityNode: React.FC> = node => { return ( - <> - - - + -

{node.data.label}

+
+ +
+ + + + + {properties.map(entry => ( + + + + + + ))} + +
- - - +
); -}; - -export default EntityNode; +} diff --git a/app/src/pages/mapping_page/components/MainPanel/components/FloatingEdge.tsx b/app/src/pages/mapping_page/components/MainPanel/components/FloatingEdge.tsx new file mode 100644 index 0000000..3673fd6 --- /dev/null +++ b/app/src/pages/mapping_page/components/MainPanel/components/FloatingEdge.tsx @@ -0,0 +1,56 @@ +import { getEdgePosition } from '@/pages/mapping_page/components/MainPanel/utils'; +import { EdgeProps, getSmoothStepPath, useInternalNode } from '@xyflow/react'; + +import './styles.scss'; + +function FloatingEdge({ + id, + source, + target, + markerEnd, + style, + sourceHandleId, + targetHandleId, + selected, +}: EdgeProps) { + const sourceNode = useInternalNode(source); + const targetNode = useInternalNode(target); + + if (!sourceNode || !targetNode) { + return null; + } + + const { sx, sy, sp, tx, ty, tp, cx, cy } = getEdgePosition( + sourceNode, + sourceHandleId ?? '', + targetNode, + targetHandleId ?? '', + ); + + const [edgePath] = getSmoothStepPath({ + sourceX: sx, + sourceY: sy, + sourcePosition: sp, + targetX: tx, + targetY: ty, + targetPosition: tp, + borderRadius: 10, + centerX: cx, + centerY: cy, + }); + + return ( + + ); +} + +export default FloatingEdge; diff --git a/app/src/pages/mapping_page/components/MainPanel/components/LabeledHandle.tsx b/app/src/pages/mapping_page/components/MainPanel/components/LabeledHandle.tsx new file mode 100644 index 0000000..c2b0667 --- /dev/null +++ b/app/src/pages/mapping_page/components/MainPanel/components/LabeledHandle.tsx @@ -0,0 +1,51 @@ +import { BaseHandle } from '@/pages/mapping_page/components/MainPanel/components/BaseHandle'; +import { HandleProps, Position } from '@xyflow/react'; +import React from 'react'; + +const LabeledHandle = React.forwardRef< + HTMLDivElement, + Omit & + React.HTMLAttributes & { + title: string; + bigTitle?: boolean; + handleClassName?: string; + labelClassName?: string; + } +>(({ className, labelClassName, title, bigTitle, ...props }, ref) => ( +
+ + {bigTitle ? ( +

+ {title} +

+ ) : ( + + )} +
+)); + +LabeledHandle.displayName = 'LabeledHandle'; + +export { LabeledHandle }; diff --git a/app/src/pages/mapping_page/components/MainPanel/components/LiteralNode.tsx b/app/src/pages/mapping_page/components/MainPanel/components/LiteralNode.tsx new file mode 100644 index 0000000..a41fa95 --- /dev/null +++ b/app/src/pages/mapping_page/components/MainPanel/components/LiteralNode.tsx @@ -0,0 +1,39 @@ +import { BaseNode } from '@/pages/mapping_page/components/MainPanel/components/BaseNode'; +import { LabeledHandle } from '@/pages/mapping_page/components/MainPanel/components/LabeledHandle'; +import { LiteralNodeType } from '@/pages/mapping_page/components/MainPanel/types'; +import { Card, H6, Icon } from '@blueprintjs/core'; +import { NodeProps } from '@xyflow/react'; + +export function LiteralNode({ data, selected }: NodeProps) { + const typeRender = () => { + if (data.literal_type.includes('http://www.w3.org/2001/XMLSchema#')) { + return data.literal_type.split('#')[1]; + } + return data.literal_type; + }; + return ( + + +
+ +
+ +
{typeRender()}
+
+
+ ); +} diff --git a/app/src/pages/mapping_page/components/MainPanel/components/URIRefNode.tsx b/app/src/pages/mapping_page/components/MainPanel/components/URIRefNode.tsx new file mode 100644 index 0000000..697271c --- /dev/null +++ b/app/src/pages/mapping_page/components/MainPanel/components/URIRefNode.tsx @@ -0,0 +1,37 @@ +import { BaseNode } from '@/pages/mapping_page/components/MainPanel/components/BaseNode'; +import { LabeledHandle } from '@/pages/mapping_page/components/MainPanel/components/LabeledHandle'; +import { URIRefNodeType } from '@/pages/mapping_page/components/MainPanel/types'; +import { Card, Icon } from '@blueprintjs/core'; +import { NodeProps } from '@xyflow/react'; + +export function URIRefNode({ data, selected }: NodeProps) { + return ( + + +
+ +
+ +
+
+ ); +} diff --git a/app/src/pages/mapping_page/components/MainPanel/components/styles.scss b/app/src/pages/mapping_page/components/MainPanel/components/styles.scss new file mode 100644 index 0000000..e69de29 diff --git a/app/src/pages/mapping_page/components/MainPanel/index.tsx b/app/src/pages/mapping_page/components/MainPanel/index.tsx index add49d9..cbc3458 100644 --- a/app/src/pages/mapping_page/components/MainPanel/index.tsx +++ b/app/src/pages/mapping_page/components/MainPanel/index.tsx @@ -5,6 +5,8 @@ import { Background, Connection, Controls, + EdgeTypes, + MarkerType, NodeTypes, ReactFlow, useEdgesState, @@ -15,7 +17,12 @@ import { import '@xyflow/react/dist/style.css'; import { useCallback } from 'react'; import { MappingGraph } from '../../../../lib/api/mapping_service/types'; -import EntityNode from './components/EntityNode'; + +import ConnectionLineComponent from '@/pages/mapping_page/components/MainPanel/components/ConnectionLineComponent'; +import { EntityNode } from '@/pages/mapping_page/components/MainPanel/components/EntityNode'; +import FloatingEdge from '@/pages/mapping_page/components/MainPanel/components/FloatingEdge'; +import { LiteralNode } from '@/pages/mapping_page/components/MainPanel/components/LiteralNode'; +import { URIRefNode } from '@/pages/mapping_page/components/MainPanel/components/UriRefNode'; import { XYEdgeType, XYNodeTypes } from './types'; type MainPanelProps = { @@ -23,6 +30,23 @@ type MainPanelProps = { }; const nodeTypes: NodeTypes = { entity: EntityNode, + uri_ref: URIRefNode, + literal: LiteralNode, +}; + +const edgeTypes: EdgeTypes = { + floating: FloatingEdge, +}; + +const defaultEdgeOptions = { + type: 'floating', + markerEnd: { + type: MarkerType.ArrowClosed, + }, + style: { + stroke: '#b1b1b7', + strokeWidth: 4, + }, }; const MainPanel = ({ initialGraph }: MainPanelProps) => { @@ -52,8 +76,7 @@ const MainPanel = ({ initialGraph }: MainPanelProps) => { properties: [], type: 'entity', }, - width: 200, - height: 100, + position: reactflow.screenToFlowPosition({ x: e.clientX, y: e.clientY, @@ -65,14 +88,60 @@ const MainPanel = ({ initialGraph }: MainPanelProps) => { [setNodes, reactflow], ); + const handleAddUriRefNode = useCallback( + (e: React.MouseEvent) => { + setNodes(nodes => [ + ...nodes, + { + id: `node-${nodes.length}`, + data: { + id: `node-${nodes.length}`, + uri_pattern: 'http://example.com/', + type: 'uri_ref', + }, + position: reactflow.screenToFlowPosition({ + x: e.clientX, + y: e.clientY, + }), + type: 'uri_ref', + }, + ]); + }, + [setNodes, reactflow], + ); + + const handleAddLiteralNode = useCallback( + (e: React.MouseEvent) => { + setNodes(nodes => [ + ...nodes, + { + id: `node-${nodes.length}`, + data: { + id: `node-${nodes.length}`, + label: 'New Literal', + value: '', + literal_type: 'string', + type: 'literal', + }, + position: reactflow.screenToFlowPosition({ + x: e.clientX, + y: e.clientY, + }), + type: 'literal', + }, + ]); + }, + [setNodes, reactflow], + ); + const openMenu = (e: React.MouseEvent) => { e.preventDefault(); showContextMenu({ content: ( - - + + ), targetOffset: { left: e.clientX, top: e.clientY }, @@ -87,6 +156,9 @@ const MainPanel = ({ initialGraph }: MainPanelProps) => { nodes={nodes} edges={edges} nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + defaultEdgeOptions={defaultEdgeOptions} + connectionLineComponent={ConnectionLineComponent} onConnect={onConnect} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} diff --git a/app/src/pages/mapping_page/components/MainPanel/types.ts b/app/src/pages/mapping_page/components/MainPanel/types.ts index 941b2ae..9b56e20 100644 --- a/app/src/pages/mapping_page/components/MainPanel/types.ts +++ b/app/src/pages/mapping_page/components/MainPanel/types.ts @@ -1,12 +1,14 @@ import { Edge, Node } from '@xyflow/react'; import { MappingEdge, + MappingLiteral, MappingNode, + MappingURIRef, } from '../../../../lib/api/mapping_service/types'; export type EntityNodeType = Node; -export type LiteralNodeType = Node; -export type URIRefNodeType = Node; +export type LiteralNodeType = Node; +export type URIRefNodeType = Node; export type XYNodeTypes = EntityNodeType | LiteralNodeType | URIRefNodeType; diff --git a/app/src/pages/mapping_page/components/MainPanel/utils.ts b/app/src/pages/mapping_page/components/MainPanel/utils.ts new file mode 100644 index 0000000..31e7252 --- /dev/null +++ b/app/src/pages/mapping_page/components/MainPanel/utils.ts @@ -0,0 +1,184 @@ +import { InternalNode, Position } from '@xyflow/react'; + +export function getHandlePosition( + node: InternalNode, + handleId: string, +): { x: number; y: number } { + const nodeLocation = node.internals.positionAbsolute; + + const nodeHandle = [ + ...(node.internals.handleBounds?.source ?? []), + ...(node.internals.handleBounds?.target ?? []), + ].find(handle => handle.id === handleId); + + if (!nodeHandle) { + throw new Error('Handle not found'); + } + + const nodeHandleDimensions = { + width: nodeHandle.width, + height: nodeHandle.height, + }; + + return { + x: nodeHandle.x + nodeLocation.x + nodeHandleDimensions.width / 2, + y: nodeHandle.y + nodeLocation.y + nodeHandleDimensions.height / 2, + }; +} + +export function getOuterBoundaryOfHandle( + node: InternalNode, + handleAbsoluteLocation: { x: number; y: number }, + position: Position, +): { x: number; y: number } { + const width = node.internals.bounds?.width ?? 250; + const height = node.internals.bounds?.height ?? 100; + + switch (position) { + case Position.Top: + return { + x: handleAbsoluteLocation.x, + y: handleAbsoluteLocation.y - height / 2, + }; + case Position.Bottom: + return { + x: handleAbsoluteLocation.x, + y: handleAbsoluteLocation.y + height / 2, + }; + case Position.Left: + return { + x: handleAbsoluteLocation.x - width / 4, + y: handleAbsoluteLocation.y, + }; + case Position.Right: + return { + x: handleAbsoluteLocation.x + width / 4, + y: handleAbsoluteLocation.y, + }; + } +} + +export function getCenter( + node: InternalNode, + handleId: string, + handlePosition: Position, + sourceY: number, + targetY: number, +): { x: number; y: number } { + // Center's x is always located on left or right side of the handle + // Center's y is always located between two handles + const offset = 150; + const handle = [ + ...(node.internals.handleBounds?.source ?? []), + ...(node.internals.handleBounds?.target ?? []), + ].find(handle => handle.id === handleId); + + if (!handle) { + throw new Error('Handle not found'); + } + + const handleDimensions = { + width: handle.width, + height: handle.height, + }; + + const nodeLocation = node.internals.positionAbsolute; + + const handleX = handle.x + nodeLocation.x + handleDimensions.width / 2; + + const x = + handlePosition === Position.Left ? handleX - offset : handleX + offset; + const y = (sourceY + targetY) / 2; + + return { x, y }; +} + +export function getHandlePositionType( + sourceHandleLocation: { x: number; y: number }, + targetHandleLocation: { x: number; y: number }, + preventTopBottom: boolean = false, +): Position { + const dx = targetHandleLocation.x - sourceHandleLocation.x; + const dy = targetHandleLocation.y - sourceHandleLocation.y; + + if (Math.abs(dx) > Math.abs(dy) || preventTopBottom) { + return dx >= 0 ? Position.Right : Position.Left; + } + if (preventTopBottom) { + return Position.Left; + } + + return dy > 0 ? Position.Bottom : Position.Top; +} + +export function getCenterForSameNode( + sourceHandleLocation: { x: number; y: number }, + targetHandleLocation: { x: number; y: number }, +): { x: number; y: number } { + // Place the center far left of the source handle + const x = sourceHandleLocation.x - 400; + const y = (sourceHandleLocation.y + targetHandleLocation.y) / 2; + + return { x, y }; +} + +export function getEdgePosition( + source: InternalNode, + sourceHandleId: string, + target: InternalNode, + targetHandleId: string, +): { + sx: number; + sy: number; + sp: Position; + tx: number; + ty: number; + tp: Position; + cx: number; + cy: number; +} { + const sourceLocation = getHandlePosition(source, sourceHandleId); + const targetLocation = getHandlePosition(target, targetHandleId); + + const isSameNode = source.id === target.id; + + const sourcePos = isSameNode + ? Position.Left + : getHandlePositionType(sourceLocation, targetLocation, true); + const targetPos = isSameNode + ? Position.Left + : getHandlePositionType(targetLocation, sourceLocation); + + const { x: centerX, y: centerY } = isSameNode + ? getCenterForSameNode(sourceLocation, targetLocation) + : getCenter( + source, + sourceHandleId, + sourcePos, + sourceLocation.y, + targetLocation.y, + ); + + const sourceOuterBoundary = getOuterBoundaryOfHandle( + source, + sourceLocation, + sourcePos, + ); + + const targetOuterBoundary = getOuterBoundaryOfHandle( + target, + targetLocation, + targetPos, + ); + + return { + sx: sourceOuterBoundary.x, + sy: sourceOuterBoundary.y, + sp: sourcePos, + tx: targetOuterBoundary.x, + ty: targetOuterBoundary.y, + tp: targetPos, + cx: centerX, + cy: centerY, + }; +} diff --git a/app/src/pages/mapping_page/components/MainPanel/utils_old.js b/app/src/pages/mapping_page/components/MainPanel/utils_old.js new file mode 100644 index 0000000..d9a6efc --- /dev/null +++ b/app/src/pages/mapping_page/components/MainPanel/utils_old.js @@ -0,0 +1,105 @@ +import { Position } from '@xyflow/react'; + +// this helper function returns the intersection point +// of the line between the center of the intersectionNode and the target node +function getNodeIntersection( + intersectionNode, + intersectionNodeHandleId, + targetNode, + targetNodeHandleId, +) { + // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a + + const intersectionNodePosition = intersectionNode.internals.positionAbsolute; + const targetPosition = targetNode.internals.positionAbsolute; + + const intersectionNodeHandle = [ + ...(intersectionNode.internals.handleBounds.source || []), + ...(intersectionNode.internals.handleBounds.target || []), + ].find(handle => handle.id === intersectionNodeHandleId); + + const targetNodeHandle = [ + ...(targetNode.internals.handleBounds.source || []), + ...(targetNode.internals.handleBounds.target || []), + ].find(handle => handle.id === targetNodeHandleId); + + const w = intersectionNodeHandle.width / 2; + const h = intersectionNodeHandle.height / 2; + + const x2 = intersectionNodeHandle.x + intersectionNodePosition.x + w; + const y2 = intersectionNodeHandle.y + intersectionNodePosition.y + h; + const x1 = targetNodeHandle.x + targetPosition.x + targetNodeHandle.width / 2; + const y1 = + targetNodeHandle.y + targetPosition.y + targetNodeHandle.height / 2; + + const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h); + const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h); + const a = 1 / (Math.abs(xx1) + Math.abs(yy1) || 1); + const xx3 = a * xx1; + const yy3 = a * yy1; + + // Calculate y normally + const y = h * (-xx3 + yy3) + y2; + + // Clamp x to either left or right edge + const x = + x1 < x2 + ? intersectionNodeHandle.x + intersectionNodePosition.x // left edge + : intersectionNodeHandle.x + + intersectionNodePosition.x + + intersectionNodeHandle.width; // right edge + + return { x, y }; +} + +// returns the position (top,right,bottom or right) passed node compared to the intersection point +function getEdgePosition(node, intersectionPoint) { + const n = { ...node.measured.positionAbsolute, ...node }; + const nx = Math.round(n.x); + const ny = Math.round(n.y); + const px = Math.round(intersectionPoint.x); + const py = Math.round(intersectionPoint.y); + + if (px <= nx + 1) { + return Position.Left; + } + if (px >= nx + n.measured.width - 1) { + return Position.Right; + } + if (py <= ny + 1) { + return Position.Top; + } + if (py >= n.y + n.measured.height - 1) { + return Position.Bottom; + } + + return Position.Top; +} + +// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge +export function getEdgeParams(source, sourceHandleId, target, targetHandleId) { + const sourceIntersectionPoint = getNodeIntersection( + source, + sourceHandleId, + target, + targetHandleId, + ); + const targetIntersectionPoint = getNodeIntersection( + target, + targetHandleId, + source, + sourceHandleId, + ); + + const sourcePos = getEdgePosition(source, sourceIntersectionPoint); + const targetPos = getEdgePosition(target, targetIntersectionPoint); + + return { + sx: sourceIntersectionPoint.x, + sy: sourceIntersectionPoint.y, + tx: targetIntersectionPoint.x, + ty: targetIntersectionPoint.y, + sourcePos, + targetPos, + }; +} diff --git a/app/src/pages/mapping_page/components/NodeProperties/components/EntityProperties.tsx b/app/src/pages/mapping_page/components/NodeProperties/components/EntityProperties.tsx new file mode 100644 index 0000000..361751d --- /dev/null +++ b/app/src/pages/mapping_page/components/NodeProperties/components/EntityProperties.tsx @@ -0,0 +1,314 @@ +import GroupedSelectRenderer from '@/components/GroupedSelectRenderer'; +import OneLineMonaco from '@/components/OneLineMonacoEditor'; +import toast from '@/consts/toast'; +import { + NamedNode, + OntologyClass, + Property, +} from '@/lib/api/ontology_api/types'; +import { EntityNodeType } from '@/pages/mapping_page/components/MainPanel/types'; +import useClassOrderer from '@/pages/mapping_page/hooks/useClassOrderer'; +import useDomainOrderer from '@/pages/mapping_page/hooks/useDomainOrderer'; +import useMappingPage from '@/pages/mapping_page/state'; +import { FormGroup, H5, InputGroup, MenuItem } from '@blueprintjs/core'; +import { ItemListRendererProps, MultiSelect } from '@blueprintjs/select'; +import { useReactFlow } from '@xyflow/react'; +import { useCallback, useMemo } from 'react'; + +import './styles.scss'; + +const EntityNodeProperties = ({ node }: { node: EntityNodeType }) => { + const ontologies = useMappingPage(state => state.ontologies); + const reactflow = useReactFlow(); + + const properties = useMemo<(Property & { group: string })[]>( + () => + node.data.properties + .map( + uri => + ontologies + ?.flatMap(o => o.properties) + .find(p => p.full_uri === uri) || + ({ + type: 'property', + full_uri: uri, + label: [{ value: uri }], + belongs_to: 'Created', + property_type: 'any', + domain: [], + range: [], + description: [], + is_deprecated: false, + } as Property), + ) + .map(p => ({ ...p, group: p.belongs_to })), + [node.data.properties, ontologies], + ); + + const rdfType = useMemo<(OntologyClass & { group: string })[]>( + () => + node.data.rdf_type + .map( + uri => + ontologies?.flatMap(o => o.classes).find(c => c.full_uri === uri) || + ({ + full_uri: uri, + label: [{ value: uri }], + belongs_to: 'Created', + super_classes: [], + type: 'class', + description: [], + is_deprecated: false, + } as OntologyClass), + ) + .map(c => ({ ...c, group: c.belongs_to })), + [node.data.rdf_type, ontologies], + ); + + const possibleClasses = useClassOrderer(node); + const possibleProperties = useDomainOrderer(node); + + const updateNode = useCallback( + ( + label: string | null, + uriPattern: string | null, + rdfType: (OntologyClass & { group: string })[] | null, + properties: (Property & { group: string })[] | null, + ) => { + reactflow.updateNode(node.id, { + data: { + ...node.data, + label: label ?? node.data.label, + uri_pattern: uriPattern ?? node.data.uri_pattern, + rdf_type: rdfType?.map(c => c.full_uri) ?? node.data.rdf_type, + properties: properties?.map(p => p.full_uri) ?? node.data.properties, + }, + }); + }, + [node, reactflow], + ); + + const createNewClassItemFromQuery = (query: string) => { + return { + full_uri: query, + label: [{ value: query }], + belongs_to: '', + super_classes: [], + type: 'class', + group: 'Create', + description: [], + is_deprecated: false, + } as OntologyClass & { group: string }; + }; + + const createNewPropertyItemFromQuery = (query: string) => { + return { + full_uri: query, + label: [{ value: query }], + belongs_to: '', + property_type: 'any', + domain: [], + range: [], + type: 'property', + group: 'Create', + description: [], + is_deprecated: false, + } as Property & { group: string }; + }; + + const itemSearch = (query: string, item: NamedNode & { group: string }) => { + if (item.label.length > 0) { + return item.label[0].value.toLowerCase().includes(query.toLowerCase()); + } + return item.full_uri.toLowerCase().includes(query.toLowerCase()); + }; + + const createNewItemRenderer = ( + query: string, + active: boolean, + handleClick: React.MouseEventHandler, + ) => ( + ) => { + try { + new URL(query); + } catch { + toast.show({ + message: 'Invalid URI, please enter a valid URI', + intent: 'danger', + }); + return; + } + handleClick(e); + }} + active={active} + /> + ); + + const itemListRenderer = ( + props: ItemListRendererProps, + ) => { + return ( + + listProps={props} + initialContent={} + noResults={} + getGroup={item => item.group} + /> + ); + }; + + const tagRenderer = (item: NamedNode & { group: string }) => { + const ontology = ontologies?.find(o => o.base_uri === item.belongs_to); + if (ontology && item.label.length > 0) { + return `${ontology.name}:${item.label[0].value}`; + } + if (item.label.length > 0) { + return item.label[0].value; + } + return item.full_uri; + }; + + return ( + <> +
Entity Node
+ + ) => + updateNode(e.target.value, null, null, null) + } + /> + + + + fill + popoverProps={{ + matchTargetWidth: true, + popoverClassName: 'popover-scroll', + }} + onRemove={item => + updateNode( + null, + null, + rdfType.filter(c => c !== item), + null, + ) + } + itemRenderer={(item, { handleClick, modifiers }) => ( + 0 ? item.label[0].value : item.full_uri} + onClick={handleClick} + active={modifiers.active} + /> + )} + itemPredicate={itemSearch} + items={possibleClasses.classes.filter( + c => !rdfType.some(r => r.full_uri === c.full_uri), + )} + onItemSelect={item => { + try { + new URL(item.full_uri); + } catch { + toast.show({ + message: 'Invalid URI, please enter a valid URI', + intent: 'danger', + }); + return; + } + if (!rdfType.some(c => c.full_uri === item.full_uri)) { + updateNode( + null, + null, + [...rdfType, item as OntologyClass & { group: string }], + null, + ); + } + }} + selectedItems={rdfType} + itemListRenderer={itemListRenderer} + tagRenderer={tagRenderer} + createNewItemFromQuery={createNewClassItemFromQuery} + createNewItemRenderer={createNewItemRenderer} + itemsEqual={(a, b) => a.full_uri === b.full_uri} + resetOnQuery + resetOnSelect + /> + + + + fill + popoverProps={{ + matchTargetWidth: true, + popoverClassName: 'popover-scroll', + }} + onRemove={item => + updateNode( + null, + null, + null, + properties.filter(p => p !== item), + ) + } + itemRenderer={(item, { handleClick, modifiers }) => ( + 0 ? item.label[0].value : item.full_uri} + onClick={handleClick} + active={modifiers.active} + /> + )} + itemPredicate={itemSearch} + items={possibleProperties.filter( + p => !properties.some(prop => prop.full_uri === p.full_uri), + )} + onItemSelect={item => { + try { + new URL(item.full_uri); + } catch { + toast.show({ + message: 'Invalid URI, please enter a valid URI', + intent: 'danger', + }); + return; + } + if (!properties.some(p => p.full_uri === item.full_uri)) { + updateNode(null, null, null, [ + ...properties, + item as Property & { group: string }, + ]); + } + }} + selectedItems={properties} + itemListRenderer={itemListRenderer} + tagRenderer={tagRenderer} + createNewItemFromQuery={createNewPropertyItemFromQuery} + createNewItemRenderer={createNewItemRenderer} + itemsEqual={(a, b) => a.full_uri === b.full_uri} + resetOnQuery + resetOnSelect + /> + + + + updateNode(null, value ?? '', null, null) + } + value={node.data.uri_pattern} + language='mapping_language' + theme='mapping-theme' + /> + + + ); +}; + +export default EntityNodeProperties; diff --git a/app/src/pages/mapping_page/components/NodeProperties/components/LiteralProperties.tsx b/app/src/pages/mapping_page/components/NodeProperties/components/LiteralProperties.tsx new file mode 100644 index 0000000..52755bb --- /dev/null +++ b/app/src/pages/mapping_page/components/NodeProperties/components/LiteralProperties.tsx @@ -0,0 +1,211 @@ +import GroupedSelectRenderer from '@/components/GroupedSelectRenderer'; +import OneLineMonaco from '@/components/OneLineMonacoEditor'; +import toast from '@/consts/toast'; +import { LiteralNodeType } from '@/pages/mapping_page/components/MainPanel/types'; +import useClassOrderer from '@/pages/mapping_page/hooks/useClassOrderer'; +import { FormGroup, H5, InputGroup, MenuItem } from '@blueprintjs/core'; +import { ItemListRendererProps, Select } from '@blueprintjs/select'; +import { useReactFlow } from '@xyflow/react'; +import { useCallback, useMemo } from 'react'; + +import { xsdTypes } from '@/consts/xsdTypes'; +import { NamedNode, OntologyClass } from '@/lib/api/ontology_api/types'; +import './styles.scss'; + +const LiteralNodeProperties = ({ node }: { node: LiteralNodeType }) => { + const reactflow = useReactFlow(); + + const dataType = useMemo<(OntologyClass & { group: string }) | null>(() => { + const dataType = xsdTypes.find(t => t.full_uri === node.data.literal_type); + if (dataType) { + return { + ...dataType, + group: 'XML Schema', + }; + } + return null; + }, [node.data.literal_type]); + + const possibleClasses = useClassOrderer(node); + + const updateNode = useCallback( + ( + label: string | null, + literalType: string | null, + value: string | null, + ) => { + reactflow.updateNode(node.id, { + data: { + ...node.data, + label: label ?? node.data.label, + literal_type: literalType ?? node.data.literal_type, + value: value ?? node.data.value, + }, + }); + }, + [node, reactflow], + ); + + const itemSearch = (query: string, item: NamedNode & { group: string }) => { + if (item.label.length > 0) { + return item.label[0].value.toLowerCase().includes(query.toLowerCase()); + } + return item.full_uri.toLowerCase().includes(query.toLowerCase()); + }; + + const createNewItemRenderer = ( + query: string, + active: boolean, + handleClick: React.MouseEventHandler, + ) => ( + ) => { + try { + new URL(query); + } catch { + toast.show({ + message: 'Invalid URI, please enter a valid URI', + intent: 'danger', + }); + return; + } + handleClick(e); + }} + active={active} + /> + ); + + const createNewTypeItemFromQuery = (query: string) => { + return { + full_uri: query, + label: [{ value: query }], + belongs_to: '', + super_classes: [], + type: 'class', + group: 'Create', + description: [], + is_deprecated: false, + } as OntologyClass & { group: string }; + }; + + const itemListRenderer = ( + props: ItemListRendererProps, + ) => { + return ( + + listProps={props} + initialContent={} + noResults={} + getGroup={item => item.group} + /> + ); + }; + + const tagRenderer = (item: NamedNode & { group: string }) => { + if (!item) return ''; + if (item.label.length > 0) { + return item.label[0].value; + } + return item.full_uri; + }; + + const createItemList = (): (NamedNode & { group: string })[] => { + const bestFits = possibleClasses.classes + .map(c => { + const type = xsdTypes.find(t => t.full_uri === c.full_uri); + if (type) { + return { + ...type, + group: c.group, + }; + } + }) + .filter(Boolean) as (OntologyClass & { group: string })[]; + const others = xsdTypes.filter( + t => !bestFits.find(b => b.full_uri === t.full_uri), + ); + + return [ + ...bestFits, + ...others.map(t => ({ + ...t, + group: 'XML Schema', + })), + ]; + }; + + return ( + <> +
Literal Node
+ + ) => + updateNode(e.target.value, null, null) + } + /> + + + + fill + popoverProps={{ + matchTargetWidth: true, + popoverClassName: 'popover-scroll', + }} + itemRenderer={(item, { handleClick, modifiers }) => ( + + )} + itemPredicate={itemSearch} + items={createItemList()} + onItemSelect={item => { + try { + new URL(item.full_uri); + } catch { + toast.show({ + message: 'Invalid URI, please enter a valid URI', + intent: 'danger', + }); + return; + } + updateNode(null, item.full_uri, null); + }} + itemListRenderer={itemListRenderer} + createNewItemFromQuery={createNewTypeItemFromQuery} + createNewItemRenderer={createNewItemRenderer} + itemsEqual={(a, b) => a.full_uri === b.full_uri} + resetOnQuery + resetOnSelect + > + + + + + + + updateNode(null, null, value ?? '') + } + value={node.data.value} + language='mapping_language' + theme='mapping-theme' + /> + + + ); +}; + +export default LiteralNodeProperties; diff --git a/app/src/pages/mapping_page/components/NodeProperties/components/URIRefProperties.tsx b/app/src/pages/mapping_page/components/NodeProperties/components/URIRefProperties.tsx new file mode 100644 index 0000000..e3da108 --- /dev/null +++ b/app/src/pages/mapping_page/components/NodeProperties/components/URIRefProperties.tsx @@ -0,0 +1,39 @@ +import OneLineMonaco from '@/components/OneLineMonacoEditor'; +import { URIRefNodeType } from '@/pages/mapping_page/components/MainPanel/types'; +import { FormGroup, H5 } from '@blueprintjs/core'; +import { useReactFlow } from '@xyflow/react'; +import { useCallback } from 'react'; + +import './styles.scss'; + +const URIRefProperties = ({ node }: { node: URIRefNodeType }) => { + const reactflow = useReactFlow(); + + const updateNode = useCallback( + (uriPattern: string | null) => { + reactflow.updateNode(node.id, { + data: { + ...node.data, + uri_pattern: uriPattern ?? node.data.uri_pattern, + }, + }); + }, + [node, reactflow], + ); + + return ( + <> +
URI Reference Node
+ + updateNode(value ?? null)} + value={node.data.uri_pattern} + language='mapping_language' + theme='mapping-theme' + /> + + + ); +}; + +export default URIRefProperties; diff --git a/app/src/pages/mapping_page/components/NodeProperties/components/styles.scss b/app/src/pages/mapping_page/components/NodeProperties/components/styles.scss new file mode 100644 index 0000000..a5e4bb6 --- /dev/null +++ b/app/src/pages/mapping_page/components/NodeProperties/components/styles.scss @@ -0,0 +1,5 @@ +.popover-scroll { + max-height: 40vh; + overflow-y: auto; + overflow-x: hidden; +} diff --git a/app/src/pages/mapping_page/components/NodeProperties/index.tsx b/app/src/pages/mapping_page/components/NodeProperties/index.tsx new file mode 100644 index 0000000..54153a4 --- /dev/null +++ b/app/src/pages/mapping_page/components/NodeProperties/index.tsx @@ -0,0 +1,52 @@ +import LiteralNodeProperties from '@/pages/mapping_page/components/NodeProperties/components/LiteralProperties'; +import URIRefProperties from '@/pages/mapping_page/components/NodeProperties/components/URIRefProperties'; +import { NonIdealState } from '@blueprintjs/core'; +import { useNodes } from '@xyflow/react'; +import { useEffect, useMemo, useState } from 'react'; +import { + EntityNodeType, + LiteralNodeType, + URIRefNodeType, + XYNodeTypes, +} from '../MainPanel/types'; +import EntityNodeProperties from './components/EntityProperties'; + +const NodeProperties = () => { + const nodes = useNodes(); + + const selectedNodes = useMemo(() => { + return nodes.filter(node => node.selected); + }, [nodes]); + + const [selectedNode, setSelectedNode] = useState(null); + + useEffect(() => { + if (selectedNodes.length === 1) { + setSelectedNode(selectedNodes[0]); + } else { + setSelectedNode(null); + } + }, [selectedNodes]); + + if (!selectedNode) { + return ( + + ); + } + + if (selectedNode.data.type === 'entity') { + return ; + } + if (selectedNode.data.type === 'uri_ref') { + return ; + } + if (selectedNode.data.type === 'literal') { + return ; + } +}; + +export default NodeProperties; diff --git a/app/src/pages/mapping_page/components/SidePanel/components/NodeProperties.tsx b/app/src/pages/mapping_page/components/SidePanel/components/NodeProperties.tsx deleted file mode 100644 index 830c057..0000000 --- a/app/src/pages/mapping_page/components/SidePanel/components/NodeProperties.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import { - FormGroup, - InputGroup, - MenuItem, - NonIdealState, -} from '@blueprintjs/core'; -import { ItemListRendererProps, MultiSelect } from '@blueprintjs/select'; -import { useNodes, useReactFlow } from '@xyflow/react'; -import { useEffect, useMemo, useState } from 'react'; -import GroupedSelectRenderer from '../../../../../components/GroupedSelectRenderer'; -import toast from '../../../../../consts/toast'; -import { OntologyClass } from '../../../../../lib/api/ontology_api/types'; -import useClassOrderer from '../../../hooks/useClassOrderer'; -import useMappingPage from '../../../state'; -import { EntityNodeType, XYNodeTypes } from '../../MainPanel/types'; - -const NodeProperties = () => { - const nodes = useNodes(); - - const selectedNodes = useMemo(() => { - return nodes.filter(node => node.selected); - }, [nodes]); - - const [selectedNode, setSelectedNode] = useState(null); - - useEffect(() => { - if (selectedNodes.length === 1) { - setSelectedNode(selectedNodes[0]); - } else { - setSelectedNode(null); - } - }, [selectedNodes]); - - if (!selectedNode) { - return ( - - ); - } - - if (selectedNode.data.type === 'entity') { - return ; - } -}; - -const EntityNodePropertiesForm = ({ node }: { node: EntityNodeType }) => { - const ontologies = useMappingPage(state => state.ontologies); - const reactflow = useReactFlow(); - const [label, setLabel] = useState(node.data.label); - const [uriPattern, setUriPattern] = useState(node.data.uri_pattern); - const [rdfType, setRdfType] = useState<(OntologyClass & { group: string })[]>( - [], - ); - // Update node_data when fields change - useEffect(() => { - reactflow.setNodes(nodes => - nodes.map(n => { - if (n.id === node.id) { - return { - ...n, - data: { - ...n.data, - label, - uri_pattern: uriPattern, - rdf_type: rdfType.map(c => c.full_uri), - }, - }; - } - return n; - }), - ); - }, [label, uriPattern, rdfType, node.id, reactflow]); - - useEffect(() => { - if (ontologies) { - const rdfTypes = node.data.rdf_type - .map( - uri => - ontologies.flatMap(o => o.classes).find(c => c.full_uri === uri) || - ({ - full_uri: uri, - label: [{ value: uri }], - belongs_to: '', - super_classes: [], - type: 'class', - group: 'Create', - description: [], - is_deprecated: false, - } as OntologyClass & { group: string }), - ) - .map(c => ({ - ...c, - group: c.belongs_to, - })); - setRdfType(rdfTypes); - } - // This effect should only run when the ontologies or the node id changes - // Otherwise, it will run every time the label, uriPattern or rdfType changes - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ontologies, node.id]); - - const possibleClasses = useClassOrderer(node); - - const createNewItemFromQuery = (query: string) => { - return { - full_uri: query, - label: [{ value: query }], - belongs_to: '', - super_classes: [], - type: 'class', - group: 'Create', - description: [], - is_deprecated: false, - } as OntologyClass & { group: string }; - }; - - const createNewItemRenderer = ( - query: string, - active: boolean, - handleClick: React.MouseEventHandler, - ) => ( - ) => { - try { - new URL(query); - } catch { - toast.show({ - message: 'Invalid URI, please enter a valid URI', - intent: 'danger', - }); - return; - } - handleClick(e); - }} - active={active} - /> - ); - - const itemListRenderer = ( - props: ItemListRendererProps, - ) => { - return ( - - listProps={props} - initialContent={} - noResults={} - getGroup={item => item.group} - /> - ); - }; - - const tagRenderer = (item: OntologyClass & { group: string }) => { - const ontology = ontologies?.find(o => o.base_uri === item.belongs_to); - if (ontology) { - return `${ontology.name}:${item.label[0].value}`; - } - return item.label[0].value; - }; - - return ( - <> - - ) => - setLabel(e.target.value) - } - /> - - - - fill - popoverProps={{ - matchTargetWidth: true, - }} - onRemove={item => setRdfType(rdfType.filter(c => c !== item))} - itemRenderer={(item, { handleClick, modifiers }) => ( - - )} - itemPredicate={(query, item) => - item.label[0].value.toLowerCase().includes(query.toLowerCase()) - } - items={possibleClasses.classes.filter( - c => !rdfType.some(r => r.full_uri === c.full_uri), - )} - onItemSelect={item => { - try { - new URL(item.full_uri); - } catch { - toast.show({ - message: 'Invalid URI, please enter a valid URI', - intent: 'danger', - }); - return; - } - if (!rdfType.some(c => c.full_uri === item.full_uri)) { - setRdfType([...rdfType, item]); - } - }} - selectedItems={rdfType} - itemListRenderer={itemListRenderer} - tagRenderer={tagRenderer} - createNewItemFromQuery={createNewItemFromQuery} - createNewItemRenderer={createNewItemRenderer} - itemsEqual={(a, b) => a.full_uri === b.full_uri} - resetOnQuery - resetOnSelect - /> - - - ) => - setUriPattern(e.target.value) - } - /> - - - ); -}; - -export default NodeProperties; diff --git a/app/src/pages/mapping_page/components/SidePanel/index.tsx b/app/src/pages/mapping_page/components/SidePanel/index.tsx index 10b1749..5e94f9a 100644 --- a/app/src/pages/mapping_page/components/SidePanel/index.tsx +++ b/app/src/pages/mapping_page/components/SidePanel/index.tsx @@ -1,5 +1,5 @@ import { Card } from '@blueprintjs/core'; -import NodeProperties from './components/NodeProperties'; +import NodeProperties from '../NodeProperties'; type SidePanelProps = { selectedTab: string | undefined; @@ -19,7 +19,7 @@ const SidePanel = ({ selectedTab }: SidePanelProps) => { case 'settings': return
Settings Panel Content
; default: - return
Select a tab to view panel content.
; + return ; } }; diff --git a/app/src/pages/mapping_page/components/VerticalTabs/index.tsx b/app/src/pages/mapping_page/components/VerticalTabs/index.tsx index 810a9e7..7082e53 100644 --- a/app/src/pages/mapping_page/components/VerticalTabs/index.tsx +++ b/app/src/pages/mapping_page/components/VerticalTabs/index.tsx @@ -1,5 +1,4 @@ import { Button } from '@blueprintjs/core'; -import { SetStateAction } from 'react'; import { VscBook, VscGear, @@ -9,12 +8,12 @@ import { } from 'react-icons/vsc'; type VerticalTabsProps = { - selectedTab: string | undefined; + selectedTab: 'properties' | 'ai' | 'references' | 'search' | 'settings'; isCollapsed: boolean; - handleTabClick: (tabId: SetStateAction) => void; + handleTabClick: (tabId: string) => void; }; -const tabs = [ +const tabs: { id: string; label: string; icon: JSX.Element }[] = [ { id: 'properties', label: 'Node Properties', icon: }, { id: 'ai', label: 'AI', icon: }, { id: 'search', label: 'Search', icon: }, diff --git a/app/src/pages/mapping_page/hooks/useClassOrderer.ts b/app/src/pages/mapping_page/hooks/useClassOrderer.ts index 3a959e7..8a88572 100644 --- a/app/src/pages/mapping_page/hooks/useClassOrderer.ts +++ b/app/src/pages/mapping_page/hooks/useClassOrderer.ts @@ -1,7 +1,8 @@ +import { xsdTypes } from '@/consts/xsdTypes'; import { getConnectedEdges, useEdges } from '@xyflow/react'; import { useEffect, useState } from 'react'; -import { OntologyClass } from '../../../lib/api/ontology_api/types'; -import { EntityNodeType, XYEdgeType } from '../components/MainPanel/types'; +import { NamedNode, OntologyClass } from '../../../lib/api/ontology_api/types'; +import { XYEdgeType, XYNodeTypes } from '../components/MainPanel/types'; import useMappingPage from '../state'; /** @@ -23,10 +24,8 @@ import useMappingPage from '../state'; * 10. Groups the remaining classes by their respective ontologies. * 11. Updates the classes state with the ordered classes. */ -export default function useClassOrderer(node: EntityNodeType) { - const [classes, setClasses] = useState<(OntologyClass & { group: string })[]>( - [], - ); +export default function useClassOrderer(node: XYNodeTypes) { + const [classes, setClasses] = useState<(NamedNode & { group: string })[]>([]); const edges = useEdges(); const ontologies = useMappingPage(state => state.ontologies); @@ -36,11 +35,17 @@ export default function useClassOrderer(node: EntityNodeType) { return; } - const allClasses = ontologies.flatMap(ontology => ontology.classes); + const allClasses = [ + ...ontologies.flatMap(ontology => ontology.classes), + ...xsdTypes, + ]; const allProperties = ontologies.flatMap(ontology => ontology.properties); - - const outgoingProperties = node.data.properties; - const incomingProperties = getConnectedEdges([node], edges) + const connectedEdges = getConnectedEdges([node], edges); + const outgoingProperties = connectedEdges + .filter(edge => edge.source === node.id) + .map(edge => allProperties.find(p => p.full_uri === edge.sourceHandle)) + .filter((p): p is (typeof allProperties)[number] => p !== undefined); + const incomingProperties = connectedEdges .filter(edge => edge.target === node.id) .map(edge => allProperties.find(p => p.full_uri === edge.sourceHandle)) .filter((p): p is (typeof allProperties)[number] => p !== undefined); @@ -91,6 +96,10 @@ export default function useClassOrderer(node: EntityNodeType) { }); setClasses(classesArr); + + return () => { + setClasses([]); + }; }, [edges, ontologies, node]); return { classes }; diff --git a/app/src/pages/mapping_page/hooks/useDomainOrderer.ts b/app/src/pages/mapping_page/hooks/useDomainOrderer.ts new file mode 100644 index 0000000..b99018f --- /dev/null +++ b/app/src/pages/mapping_page/hooks/useDomainOrderer.ts @@ -0,0 +1,56 @@ +import { Property } from '@/lib/api/ontology_api/types'; +import { EntityNodeType } from '@/pages/mapping_page/components/MainPanel/types'; +import useMappingPage from '@/pages/mapping_page/state'; +import { useEffect, useState } from 'react'; + +export default function useDomainOrderer(node: EntityNodeType) { + const [predicates, setPredicates] = useState< + (Property & { group: string })[] + >([]); + const ontologies = useMappingPage(state => state.ontologies); + + useEffect(() => { + if (!ontologies || ontologies.length === 0) { + setPredicates([]); + return; + } + + const allClasses = ontologies.flatMap(ontology => ontology.classes); + const nodeClasses = node.data.rdf_type.map(uri => + allClasses.find(c => c.full_uri === uri), + ); + const allNodeClassUris = nodeClasses + .flatMap(c => [c?.full_uri, ...(c?.super_classes ?? [])]) + .filter((uri): uri is string => uri !== undefined); + + const allProperties = ontologies.flatMap(ontology => ontology.properties); + + const bestFitProperties = allNodeClassUris.map(uri => + allProperties.filter(p => p.domain.includes(uri)), + ); + + const propertiesArr: (Property & { group: string })[] = []; + + bestFitProperties.forEach(properties => + propertiesArr.push(...properties.map(p => ({ ...p, group: 'Best Fit' }))), + ); + + allProperties.forEach(p => { + if (!propertiesArr.some(prop => prop.full_uri === p.full_uri)) { + propertiesArr.push({ + ...p, + group: + ontologies.find(o => o.base_uri === p.belongs_to)?.name ?? 'Other', + }); + } + }); + + setPredicates(propertiesArr); + + return () => { + setPredicates([]); + }; + }, [node, ontologies]); + + return predicates; +} diff --git a/app/src/pages/mapping_page/hooks/useRangeOrderer.ts b/app/src/pages/mapping_page/hooks/useRangeOrderer.ts new file mode 100644 index 0000000..8ddf623 --- /dev/null +++ b/app/src/pages/mapping_page/hooks/useRangeOrderer.ts @@ -0,0 +1,23 @@ +import { Property } from '@/lib/api/ontology_api/types'; +import { + EntityNodeType, + XYNodeTypes, +} from '@/pages/mapping_page/components/MainPanel/types'; +import useMappingPage from '@/pages/mapping_page/state'; +import { useNodes } from '@xyflow/react'; +import { useEffect, useState } from 'react'; + +export default function useRangeOrderer(node: EntityNodeType) { + const [predicates, setPredicates] = useState< + (Property & { group: string })[] + >([]); + const ontologies = useMappingPage(state => state.ontologies); + const nodes = useNodes(); + + useEffect(() => { + + + }, [node, nodes, ontologies]); + + return predicates; +} diff --git a/app/src/pages/mapping_page/index.tsx b/app/src/pages/mapping_page/index.tsx index f6d6c3f..e9debb7 100644 --- a/app/src/pages/mapping_page/index.tsx +++ b/app/src/pages/mapping_page/index.tsx @@ -1,5 +1,6 @@ import { ReactFlowProvider } from '@xyflow/react'; -import { SetStateAction, useEffect, useRef, useState } from 'react'; +import { languages } from 'monaco-editor'; +import { useEffect, useRef, useState } from 'react'; import { ImperativePanelHandle, Panel, @@ -7,6 +8,11 @@ import { PanelResizeHandle, } from 'react-resizable-panels'; import { useParams } from 'react-router-dom'; +import useRegisterCompletionItemProvider from '../../components/OneLineMonacoEditor/hooks/useRegisterCompletionItemProvider'; +import useRegisterLanguage from '../../components/OneLineMonacoEditor/hooks/useRegisterLanguage'; +import useRegisterTheme from '../../components/OneLineMonacoEditor/hooks/useRegisterTheme'; +import { mapping_language } from '../../consts/mapping-language'; +import mapping_theme from '../../consts/mapping-theme'; import useErrorToast from '../../hooks/useErrorToast'; import MainPanel from './components/MainPanel'; import Navbar from './components/Navbar'; @@ -22,14 +28,55 @@ type MappingPageURLProps = { const MappingPage = () => { const props = useParams(); - const [selectedTab, setSelectedTab] = useState(undefined); + const [selectedTab, setSelectedTab] = useState< + 'properties' | 'ai' | 'references' | 'search' | 'settings' + >('properties'); const [isCollapsed, setIsCollapsed] = useState(false); const mapping = useMappingPage(state => state.mapping); + const source = useMappingPage(state => state.source); + const prefixes = useMappingPage(state => state.prefixes); const isLoading = useMappingPage(state => state.isLoading); const error = useMappingPage(state => state.error); const loadMapping = useMappingPage(state => state.loadMapping); const saveMapping = useMappingPage(state => state.saveMapping); + useRegisterTheme('mapping-theme', mapping_theme); + useRegisterLanguage('mapping_language', mapping_language, {}); + useRegisterCompletionItemProvider('mapping_language', [ + { + provideCompletionItems(model, position, context, token) { + const word = model.getWordUntilPosition(position); + + const range = { + startLineNumber: position.lineNumber, + startColumn: word.startColumn, + endLineNumber: position.lineNumber, + endColumn: word.endColumn, + }; + return { + suggestions: [ + ...(prefixes?.map(prefix => ({ + label: prefix.prefix, + kind: languages.CompletionItemKind.Variable, + detail: 'Prefix', + insertText: prefix.prefix + ':', + range, + })) ?? []), + ...(source?.references + ? source.references.map(ref => ({ + label: ref, + kind: languages.CompletionItemKind.Class, + detail: 'Reference', + insertText: '$(' + ref + ')', + range, + })) + : []), + ], + } as languages.CompletionList; + }, + }, + ]); + const sidePanelHandle = useRef(null); useEffect(() => { @@ -50,13 +97,15 @@ const MappingPage = () => { useErrorToast(error); - const handleTabClick = (tabId: SetStateAction) => { + const handleTabClick = (tabId: string) => { if (selectedTab === tabId && !isCollapsed) { setIsCollapsed(true); - } else { - setSelectedTab(tabId); - setIsCollapsed(false); + return; } + setSelectedTab( + tabId as 'properties' | 'ai' | 'references' | 'search' | 'settings', + ); + setIsCollapsed(false); }; const handleSave = () => { diff --git a/app/src/pages/mapping_page/state.ts b/app/src/pages/mapping_page/state.ts index c4e15d8..75ac069 100644 --- a/app/src/pages/mapping_page/state.ts +++ b/app/src/pages/mapping_page/state.ts @@ -6,10 +6,13 @@ import OntologyApi from '../../lib/api/ontology_api'; import { Ontology } from '../../lib/api/ontology_api/types'; import PrefixApi from '../../lib/api/prefix_api'; import { Prefix } from '../../lib/api/prefix_api/types'; +import SourceApi from '../../lib/api/source_api'; +import { Source } from '../../lib/api/source_api/types'; import { ZustandActions } from '../../utils/zustand'; interface MappingPageState { mapping: MappingGraph | null; + source: Source | null; ontologies: Ontology[] | null; prefixes: Prefix[] | null; isLoading: string | null; @@ -27,6 +30,7 @@ interface MappingPageStateActions { const defaultState: MappingPageState = { mapping: null, + source: null, ontologies: null, prefixes: null, isLoading: null, @@ -55,7 +59,9 @@ const functions: ZustandActions = ( prefixes_promise, ]); - set({ mapping, ontologies, prefixes, error: null }); + const source = await SourceApi.getSource(mapping.source_id); + + set({ mapping, source, ontologies, prefixes, error: null }); } catch (error) { if (error instanceof Error) { set({ error: error.message }); diff --git a/app/src/utils/cn.ts b/app/src/utils/cn.ts new file mode 100644 index 0000000..4d1d135 --- /dev/null +++ b/app/src/utils/cn.ts @@ -0,0 +1,3 @@ +export function cn(...classes: (string | undefined)[]): string { + return classes.filter(Boolean).join(' '); +} diff --git a/app/tsconfig.app.json b/app/tsconfig.app.json index f0a2350..da6172b 100644 --- a/app/tsconfig.app.json +++ b/app/tsconfig.app.json @@ -1,24 +1,35 @@ { - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"] -} + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "module": "ESNext", + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + }, + "allowJs": true + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/app/tsconfig.json b/app/tsconfig.json index 1ffef60..fe4fb0a 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -1,7 +1,20 @@ { - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] -} + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + }, + "allowJs": true + } +} \ No newline at end of file diff --git a/app/tsconfig.node.json b/app/tsconfig.node.json index 0d3d714..fb6735e 100644 --- a/app/tsconfig.node.json +++ b/app/tsconfig.node.json @@ -1,22 +1,30 @@ { - "compilerOptions": { - "target": "ES2022", - "lib": ["ES2023"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["vite.config.ts"] -} + "compilerOptions": { + "target": "ES2022", + "lib": [ + "ES2023" + ], + "module": "ESNext", + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + } + }, + "include": [ + "vite.config.ts" + ] +} \ No newline at end of file diff --git a/app/vite.config.ts b/app/vite.config.ts index 36f7f4e..6ccae4a 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -1,7 +1,13 @@ -import react from '@vitejs/plugin-react' -import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react'; +import path from 'path'; +import { defineConfig } from 'vite'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, +}); diff --git a/server/facades/workspace/source/get_source_facade.py b/server/facades/workspace/source/get_source_facade.py new file mode 100644 index 0000000..0699635 --- /dev/null +++ b/server/facades/workspace/source/get_source_facade.py @@ -0,0 +1,33 @@ +from kink import inject + +from server.facades import BaseFacade, FacadeResponse +from server.models.source import Source +from server.services.local.local_source_service import ( + SourceServiceProtocol, +) + + +@inject +class GetSourceFacade(BaseFacade): + def __init__( + self, + source_service: SourceServiceProtocol, + ): + super().__init__() + self.source_service = source_service + + @BaseFacade.error_wrapper + def execute( + self, + source_uuid: str, + ) -> FacadeResponse: + self.logger.info("Getting source") + + source: Source = self.source_service.get_source( + source_id=source_uuid + ) + + return self._success_response( + message="Source fetched successfully", + data=source, + ) diff --git a/server/logger.py b/server/logger.py index 74cc17b..4748503 100644 --- a/server/logger.py +++ b/server/logger.py @@ -24,6 +24,7 @@ def setup_logging( ) shared_processors: list[Processor] = [ + structlog.contextvars.merge_contextvars, structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, structlog.stdlib.PositionalArgumentsFormatter(), @@ -72,7 +73,7 @@ def setup_logging( handler = logging.StreamHandler() # Use OUR `ProcessorFormatter` to format all `logging` entries. - handler.setFormatter(formatter) + handler.setFormatter(fmt=formatter) root_logger = logging.getLogger() root_logger.addHandler(handler) root_logger.setLevel(log_level.upper()) diff --git a/server/models/mapping.py b/server/models/mapping.py index 92fe5a6..41f4f27 100644 --- a/server/models/mapping.py +++ b/server/models/mapping.py @@ -21,7 +21,7 @@ class MappingNode: label (str): The label of the node uri_pattern (str): The URI pattern of the node rdf_type (list[str]): The RDF type/s of the node - properties (list[Property]): The properties of the node + properties (list[str]): The properties of the node """ id: str @@ -29,7 +29,7 @@ class MappingNode: label: str uri_pattern: str rdf_type: list[str] - properties: list[Property] + properties: list[str] def to_dict(self): return { @@ -38,9 +38,7 @@ def to_dict(self): "label": self.label, "uri_pattern": self.uri_pattern, "rdf_type": self.rdf_type, - "properties": [ - prop.to_dict() for prop in self.properties - ], + "properties": self.properties, } @classmethod @@ -63,10 +61,7 @@ def from_dict(cls, data): label=data["label"], uri_pattern=data["uri_pattern"], rdf_type=data["rdf_type"], - properties=[ - Property.from_dict(prop) - for prop in data["properties"] - ], + properties=data["properties"], ) diff --git a/server/models/ontology.py b/server/models/ontology.py index 94b1fa1..92bba13 100644 --- a/server/models/ontology.py +++ b/server/models/ontology.py @@ -57,6 +57,7 @@ class PropertyType(StrEnum): OBJECT = "object" DATATYPE = "datatype" ANNOTATION = "annotation" + ANY = "any" @dataclass(kw_only=True) diff --git a/server/routers/sources/__init__.py b/server/routers/sources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/routers/sources/sources.py b/server/routers/sources/sources.py new file mode 100644 index 0000000..62cbf09 --- /dev/null +++ b/server/routers/sources/sources.py @@ -0,0 +1,38 @@ +from typing import Annotated + +from fastapi.exceptions import HTTPException +from fastapi.params import Depends +from fastapi.routing import APIRouter +from kink.container import di + +from server.facades.workspace.source.get_source_facade import ( + GetSourceFacade, +) +from server.models.source import Source + +router = APIRouter() + + +GetSourceFacadeDep = Annotated[ + GetSourceFacade, Depends(lambda: di[GetSourceFacade]) +] + + +@router.get("/{source_uuid}") +async def get_source( + source_uuid: str, get_source_facade: GetSourceFacadeDep +) -> Source: + facade_response = get_source_facade.execute( + source_uuid=source_uuid, + ) + + if ( + facade_response.status // 100 == 2 + and facade_response.data + ): + return facade_response.data + + raise HTTPException( + status_code=facade_response.status, + detail=facade_response.to_dict(), + ) diff --git a/server/server.py b/server/server.py index 446aa33..bb71080 100644 --- a/server/server.py +++ b/server/server.py @@ -17,6 +17,9 @@ ) from bootstrap import bootstrap, teardown +from server.routers.sources.sources import ( + router as sources_router, +) from server.routers.workspaces.workspaces import ( router as workspaces_router, ) @@ -121,12 +124,6 @@ async def logging_middleware( app.middleware_stack = app.build_middleware_stack() -# Example API endpoint -@app.get("/api/data") -async def get_data(): - return {"message": "Hello from FastAPI!"} - - app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -143,6 +140,12 @@ async def get_data(): tags=["workspaces"], ) +app.include_router( + sources_router, + prefix="/api/sources", + tags=["sources"], +) + if not DEBUG: # Serve the React app current_dir = Path(__file__).parent.parent diff --git a/server/utils/ontology_indexer.py b/server/utils/ontology_indexer.py index 7b3a7d4..b22f7b8 100644 --- a/server/utils/ontology_indexer.py +++ b/server/utils/ontology_indexer.py @@ -66,6 +66,28 @@ class OntologyIndexer: }} """ + GET_PROPERTY_RANGE_OWL_UNION_SPARQL = f""" + {PREFIXES} + SELECT DISTINCT ?range + WHERE {{ + <___property_uri___> rdfs:range ?rangeValue . + ?rangeValue owl:unionOf ?list . + ?list rdf:rest*/rdf:first ?range . + FILTER (!isBlank(?range)) . + }} + """ + + GET_PROPERTY_RANGE_OWL_INTERSECTION_SPARQL = f""" + {PREFIXES} + SELECT DISTINCT ?range + WHERE {{ + <___property_uri___> rdfs:range ?rangeValue . + ?rangeValue owl:intersectionOf ?list . + ?list rdf:rest*/rdf:first ?range . + FILTER (!isBlank(?range)) . + }} + """ + GET_PROPERTY_DOMAIN_SPARQL = f""" {PREFIXES} @@ -76,6 +98,28 @@ class OntologyIndexer: }} """ + GET_PROPERTY_DOMAIN_OWL_UNION_SPARQL = f""" + {PREFIXES} + SELECT DISTINCT ?domain + WHERE {{ + <___property_uri___> rdfs:domain ?domainValue . + ?domainValue owl:unionOf ?list . + ?list rdf:rest*/rdf:first ?domain . + FILTER (!isBlank(?domain)) . + }} + """ + + GET_PROPERTY_DOMAIN_OWL_INTERSECTION_SPARQL = f""" + {PREFIXES} + SELECT DISTINCT ?domain + WHERE {{ + <___property_uri___> rdfs:domain ?domainValue . + ?domainValue owl:intersectionOf ?list . + ?list rdf:rest*/rdf:first ?domain . + FILTER (!isBlank(?domain)) . + }} + """ + GET_INDIVIDUAL_SPARQL = f""" {PREFIXES} SELECT DISTINCT ?individual ?label ?description ?isDeprecated @@ -250,18 +294,50 @@ def _initialize_property_data( "___property_uri___", property_uri ) ) + range_union_result = g.query( + self.GET_PROPERTY_RANGE_OWL_UNION_SPARQL.replace( + "___property_uri___", property_uri + ) + ) + range_intersection_result = g.query( + self.GET_PROPERTY_RANGE_OWL_INTERSECTION_SPARQL.replace( + "___property_uri___", property_uri + ) + ) range_values = [ str(cast(ResultRow, range_row)["range"]) - for range_row in range_result + for range_row in [ + *range_result, + *range_union_result, + *range_intersection_result, + ] ] + domain_result = g.query( self.GET_PROPERTY_DOMAIN_SPARQL.replace( "___property_uri___", property_uri ) ) + + domain_union_result = g.query( + self.GET_PROPERTY_DOMAIN_OWL_UNION_SPARQL.replace( + "___property_uri___", property_uri + ) + ) + + domain_intersection_result = g.query( + self.GET_PROPERTY_DOMAIN_OWL_INTERSECTION_SPARQL.replace( + "___property_uri___", property_uri + ) + ) + domain_values = [ str(cast(ResultRow, domain_row)["domain"]) - for domain_row in domain_result + for domain_row in [ + *domain_result, + *domain_union_result, + *domain_intersection_result, + ] ] is_deprecated = cast( Literal, property_result["isDeprecated"]