diff --git a/.babelrc b/.babelrc index ded830aa4d..dc32fa352c 100644 --- a/.babelrc +++ b/.babelrc @@ -1,11 +1,6 @@ { "presets": [ - ["@babel/preset-env", { - "targets": { - "browsers": "defaults and supports webgl2" - }, - "modules": false - }] + ["@babel/preset-typescript"] ], "plugins": [ ["module-resolver", { "root": ["./src"] } ], @@ -17,9 +12,11 @@ ".css" ] }], - ["module-extension-resolver"], - ["@babel/plugin-transform-runtime", { - "regenerator": false + ["module-extension-resolver", { + "srcExtensions": [ + ".ts", + ".js" + ] }], ["minify-replace", { "replacements": [{ diff --git a/.eslintrc.cjs b/.eslintrc.cjs index c2544517f6..7bedc41bc4 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,11 +1,16 @@ module.exports = { root: true, + plugins: [ + '@typescript-eslint', + ], + parser: '@typescript-eslint/parser', extends: [ 'eslint-config-airbnb-base', 'eslint-config-airbnb-base/rules/strict', + 'plugin:@typescript-eslint/recommended', ], parserOptions: { - ecmaVersion: 13, + ecmaVersion: 2023, sourceType: 'module', ecmaFeatures: { impliedStrict: true, @@ -13,8 +18,8 @@ module.exports = { }, settings: { 'import/resolver': { - webpack: { - config: './webpack.config.cjs', + 'babel-module': { + extensions: ['.js', '.ts'], }, }, }, @@ -25,9 +30,22 @@ module.exports = { commonjs: true, }, rules: { + 'import/extensions': [ + 'off', + ], 'no-trailing-spaces': 'warn', 'padded-blocks': 'warn', - 'no-unused-vars': 'warn', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/no-this-alias': 'off', + '@typescript-eslint/no-explicit-any': 'off', 'no-plusplus': 'off', // this option sets a specific tab width for your code // http://eslint.org/docs/rules/indent @@ -108,6 +126,18 @@ module.exports = { // change default-param-last to on, but there are several breaking changes or default params to add. 'default-param-last': 'off', }, + overrides: [ + { + files: ['*.ts'], + rules: { + 'valid-jsdoc': 'off', + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { allowExpressions: true }, + ], + }, + }, + ], globals: { __DEBUG__: false, }, diff --git a/.gitignore b/.gitignore index 5ebc710462..0de7c2761b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ potree coverage .nyc_output/ /src/ThreeExtended/ +types/ diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 0000000000..001ec6fbf8 --- /dev/null +++ b/babel.config.json @@ -0,0 +1,5 @@ +{ + "babelrcRoots": [ + "." + ] +} diff --git a/examples/effects_postprocessing.html b/examples/effects_postprocessing.html index b00f52c593..41827ae382 100644 --- a/examples/effects_postprocessing.html +++ b/examples/effects_postprocessing.html @@ -1,113 +1,160 @@ - - Itowns - postprocessing - - - - - - - - -
- - - - - - + + + - - + - // iTowns namespace defined here - var viewerDiv = document.getElementById('viewerDiv'); - var view = new itowns.GlobeView(viewerDiv, placement); - - // Simple postprocessing setup - // - var postprocessScene = new itowns.THREE.Scene(); - var quad = new itowns.THREE.Mesh(new itowns.THREE.PlaneGeometry(2, 2), null); - var cam = new itowns.THREE.OrthographicCamera(-1, 1, 1, -1, 0, 10); - - setupLoadingScreen(viewerDiv, view); - - quad.frustumCulled = false; - quad.material = new itowns.THREE.ShaderMaterial({ - uniforms: { - tDiffuse: { value: null }, - tSize: { value: new itowns.THREE.Vector2(256, 256) }, - center: { value: new itowns.THREE.Vector2(0.5, 0.5) }, - angle: { value: 1.57 }, - scale: { value: 1.0 }, - }, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - }); - postprocessScene.add(quad); - - view.render = function render() { - var g = view.mainLoop.gfxEngine; - var r = g.renderer; - r.setRenderTarget(g.fullSizeRenderTarget); - r.clear(); - r.render(view.scene, view.camera3D); - - quad.material.uniforms.tDiffuse.value = g.fullSizeRenderTarget.texture; - quad.material.uniforms.tSize.value.set( - g.fullSizeRenderTarget.width, g.fullSizeRenderTarget.height); - - r.setRenderTarget(null); - r.clear(); - r.render(postprocessScene, cam); - }; - - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ElevationLayer(config.id, config); - view.addLayer(layer); - }); - - diff --git a/examples/geoid_geoidLayer.html b/examples/geoid_geoidLayer.html index ab7cc2220b..e1c36b44b9 100644 --- a/examples/geoid_geoidLayer.html +++ b/examples/geoid_geoidLayer.html @@ -1,102 +1,105 @@ - - Itowns - GeoidLayer - - + + Itowns - GeoidLayer - - + + - - - -
+ + - - - - - - + + - + + + + + - + // ---------- DEBUG TOOLS : ---------- + + debug.createTileDebugUI(debugMenu.gui, view); + + + + diff --git a/examples/js/GUI/LoadingScreen.js b/examples/js/GUI/LoadingScreen.js index 50062357ec..3da4a89f3e 100644 --- a/examples/js/GUI/LoadingScreen.js +++ b/examples/js/GUI/LoadingScreen.js @@ -1,6 +1,6 @@ /* global itowns */ -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line @typescript-eslint/no-unused-vars function setupLoadingScreen(viewerDiv, view) { let loadingScreenContainer; diff --git a/examples/layers/JSONLayers/OPENSM.json b/examples/layers/JSONLayers/OPENSM.json index d9377913c3..bd05e500b8 100644 --- a/examples/layers/JSONLayers/OPENSM.json +++ b/examples/layers/JSONLayers/OPENSM.json @@ -4,7 +4,7 @@ "crs": "EPSG:3857", "isInverted": true, "format": "image/png", - "url": "http://osm.oslandia.io/styles/klokantech-basic/${z}/${x}/${y}.png", + "url": "https://maps.pole-emploi.fr/styles/klokantech-basic/${z}/${x}/${y}.png", "attribution": { "name":"OpenStreetMap", "url": "http://www.openstreetmap.org/" diff --git a/package-lock.json b/package-lock.json index 13c9b288e9..cd718e62e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,11 @@ "@babel/cli": "^7.22.5", "@babel/plugin-transform-runtime": "^7.22.5", "@babel/preset-env": "^7.22.5", + "@babel/preset-typescript": "^7.24.1", "@babel/register": "^7.22.5", "@types/three": "^0.159.0", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", "@xmldom/xmldom": "^0.8.10", "babel-inline-import-loader": "^1.0.1", "babel-loader": "^9.1.3", @@ -44,8 +47,9 @@ "cross-env": "^7.0.3", "eslint": "^8.55.0", "eslint-config-airbnb-base": "^15.0.0", + "eslint-import-resolver-babel-module": "^5.3.2", "eslint-import-resolver-webpack": "^0.13.8", - "eslint-plugin-import": "^2.29.0", + "eslint-plugin-import": "^2.29.1", "eslint-webpack-plugin": "^4.0.1", "github-url-from-git": "^1.5.0", "grunt": "^1.6.1", @@ -260,6 +264,8 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "license": "MIT", "dependencies": { @@ -299,19 +305,20 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.5", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.5.tgz", + "integrity": "sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.24.5", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "semver": "^6.3.0" + "@babel/helper-split-export-declaration": "^7.24.5", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -376,6 +383,8 @@ }, "node_modules/@babel/helper-hoist-variables": { "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "license": "MIT", "dependencies": { @@ -386,43 +395,46 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.22.5", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.5.tgz", + "integrity": "sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.5", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-optimise-call-expression": { @@ -439,9 +451,10 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -464,27 +477,29 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.5", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", + "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -504,38 +519,40 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -697,6 +714,8 @@ }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", "engines": { @@ -819,6 +838,8 @@ }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", "dependencies": { @@ -841,6 +862,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", @@ -951,8 +987,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", "dependencies": { @@ -1301,12 +1354,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", + "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-simple-access": "^7.22.5" }, "engines": { @@ -1350,6 +1404,8 @@ }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1650,6 +1706,24 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.5.tgz", + "integrity": "sha512-E0VWu/hk83BIFUWnsKZ4D81KXjN5L3MobvevOHErASk9IPwKHOkTgvqzvNo1yP/ePJWqqK2SpUR5z+KQbl6NVw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.5", + "@babel/helper-plugin-utils": "^7.24.5", + "@babel/plugin-syntax-typescript": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.22.5", "dev": true, @@ -1819,6 +1893,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz", + "integrity": "sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-syntax-jsx": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-typescript": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/register": { "version": "7.22.5", "dev": true, @@ -1839,6 +1932,8 @@ }, "node_modules/@babel/regjsgen": { "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", "dev": true, "license": "MIT" }, @@ -1895,13 +1990,13 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1926,6 +2021,8 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "license": "MIT", "dependencies": { @@ -1998,22 +2095,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -2022,6 +2119,8 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2033,9 +2132,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@hutson/parse-repository-url": { @@ -2154,6 +2253,8 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true, "license": "MIT" }, @@ -2184,6 +2285,8 @@ }, "node_modules/@kurkle/color": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", "dev": true, "license": "MIT" }, @@ -2204,6 +2307,8 @@ }, "node_modules/@mapbox/mapbox-gl-style-spec": { "version": "13.28.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.28.0.tgz", + "integrity": "sha512-B8xM7Fp1nh5kejfIl4SWeY0gtIeewbuRencqO3cJDrCHZpaPg7uY+V8abuR+esMeuOjRl5cLhVTP40v+1ywxbg==", "license": "ISC", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", @@ -2253,6 +2358,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { @@ -2265,6 +2372,8 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", "engines": { @@ -2273,6 +2382,8 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { @@ -2537,16 +2648,16 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "dev": true, - "license": "MIT" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true, - "license": "MIT" + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true }, "node_modules/@types/linkify-it": { "version": "3.0.2", @@ -2612,6 +2723,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, "node_modules/@types/send": { "version": "0.17.1", "dev": true, @@ -2705,6 +2822,256 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz", + "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/type-utils": "7.8.0", + "@typescript-eslint/utils": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", + "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", + "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz", + "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/utils": "7.8.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", + "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", + "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz", + "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", + "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "semver": "^7.6.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", + "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -2722,11 +3089,15 @@ }, "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true, "license": "MIT" }, @@ -2737,6 +3108,8 @@ }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "license": "MIT", "dependencies": { @@ -2747,6 +3120,8 @@ }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true, "license": "MIT" }, @@ -2763,6 +3138,8 @@ }, "node_modules/@webassemblyjs/ieee754": { "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "license": "MIT", "dependencies": { @@ -2771,6 +3148,8 @@ }, "node_modules/@webassemblyjs/leb128": { "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2779,6 +3158,8 @@ }, "node_modules/@webassemblyjs/utf8": { "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true, "license": "MIT" }, @@ -2900,6 +3281,8 @@ }, "node_modules/acorn-import-assertions": { "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2970,6 +3353,8 @@ }, "node_modules/ajv-formats/node_modules/ajv": { "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "license": "MIT", "dependencies": { @@ -3051,6 +3436,8 @@ }, "node_modules/anymatch": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", "dependencies": { @@ -3133,6 +3520,15 @@ "node": ">=0.10.0" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/array.prototype.find": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.2.2.tgz", @@ -3461,20 +3857,16 @@ } }, "node_modules/babel-plugin-module-resolver": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.0.tgz", - "integrity": "sha512-g0u+/ChLSJ5+PzYwLwP8Rp8Rcfowz58TJNCe+L/ui4rpzE/mg//JVX0EWBUYoxaextqnwuGHzfGp2hh0PPV25Q==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz", + "integrity": "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg==", "dev": true, - "license": "MIT", "dependencies": { - "find-babel-config": "^2.0.0", - "glob": "^8.0.3", + "find-babel-config": "^2.1.1", + "glob": "^9.3.3", "pkg-up": "^3.1.0", "reselect": "^4.1.7", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">= 16" + "resolve": "^1.22.8" } }, "node_modules/babel-plugin-module-resolver/node_modules/brace-expansion": { @@ -3482,38 +3874,41 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/babel-plugin-module-resolver/node_modules/glob": { - "version": "8.1.0", + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" }, "engines": { - "node": ">=12" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/babel-plugin-module-resolver/node_modules/minimatch": { - "version": "5.1.6", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/babel-plugin-polyfill-corejs2": { @@ -3707,13 +4102,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -4096,6 +4490,8 @@ }, "node_modules/colorette": { "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true, "license": "MIT" }, @@ -4215,6 +4611,8 @@ }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, "license": "MIT", "engines": { @@ -4614,15 +5012,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cosmiconfig/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -4837,6 +5226,18 @@ "node": ">=0.3.1" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -4883,6 +5284,8 @@ }, "node_modules/earcut": { "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", "license": "ISC" }, "node_modules/ee-first": { @@ -5120,16 +5523,16 @@ } }, "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -5194,6 +5597,23 @@ "eslint-plugin-import": "^2.25.2" } }, + "node_modules/eslint-import-resolver-babel-module": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-babel-module/-/eslint-import-resolver-babel-module-5.3.2.tgz", + "integrity": "sha512-K7D8n0O6p/JJncPote8yiuB7chJfu26Yn/Q3gzT53cNzJNS0NUCkI0iuimj4/vWVRHVQvPnYWeq07V8RvKjz/A==", + "dev": true, + "dependencies": { + "pkg-up": "^3.1.0", + "resolve": "^1.20.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "babel-plugin-module-resolver": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, "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", @@ -5303,9 +5723,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "dependencies": { "array-includes": "^3.1.7", @@ -5324,7 +5744,7 @@ "object.groupby": "^1.0.1", "object.values": "^1.1.7", "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" @@ -5428,6 +5848,8 @@ }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { @@ -5510,6 +5932,8 @@ }, "node_modules/esquery": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5751,6 +6175,22 @@ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -5766,6 +6206,8 @@ }, "node_modules/fastest-levenshtein": { "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, "license": "MIT", "engines": { @@ -5822,11 +6264,10 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5871,15 +6312,13 @@ "license": "MIT" }, "node_modules/find-babel-config": { - "version": "2.0.0", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.1.1.tgz", + "integrity": "sha512-5Ji+EAysHGe1OipH7GN4qDjok5Z1uw5KAwDCbicU/4wyTZY7CqOCzcWbG7J5ad9mazq67k89fXlbc1MuIfl9uA==", "dev": true, - "license": "MIT", "dependencies": { - "json5": "^2.1.1", + "json5": "^2.2.3", "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" } }, "node_modules/find-babel-config/node_modules/path-exists": { @@ -5887,7 +6326,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -5943,6 +6381,8 @@ }, "node_modules/findup-sync": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", + "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6033,6 +6473,8 @@ }, "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": { @@ -6115,6 +6557,20 @@ "dev": true, "license": "ISC" }, + "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, + "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", @@ -6318,6 +6774,8 @@ }, "node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "license": "ISC", "dependencies": { @@ -6412,6 +6870,8 @@ }, "node_modules/globalthis": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, "license": "MIT", "dependencies": { @@ -6424,8 +6884,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/gopd": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "license": "MIT", "dependencies": { @@ -6437,11 +6928,15 @@ }, "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" }, @@ -6640,6 +7135,8 @@ }, "node_modules/grunt/node_modules/dateformat": { "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", "dev": true, "license": "MIT", "engines": { @@ -6902,6 +7399,8 @@ }, "node_modules/hpack.js/node_modules/readable-stream": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "dependencies": { @@ -6971,6 +7470,8 @@ }, "node_modules/http-parser-js": { "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "dev": true, "license": "MIT" }, @@ -7065,6 +7566,8 @@ }, "node_modules/iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { @@ -7095,9 +7598,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -7282,14 +7785,23 @@ "node": ">= 0.10" } }, - "node_modules/ip": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", - "dev": true + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } }, "node_modules/ipaddr.js": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", "dev": true, "license": "MIT", "engines": { @@ -7375,6 +7887,8 @@ }, "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": { @@ -7479,7 +7993,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -7512,6 +8025,8 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", "engines": { @@ -7925,8 +8440,16 @@ "xmlcreate": "^2.0.4" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, "node_modules/jsdoc": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7965,6 +8488,8 @@ }, "node_modules/jsdoc/node_modules/marked": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "license": "MIT", "bin": { @@ -8022,6 +8547,8 @@ }, "node_modules/json5": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -8067,6 +8594,8 @@ }, "node_modules/jszip": { "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "license": "(MIT OR GPL-3.0-or-later)", "dependencies": { "lie": "~3.3.0", @@ -8083,6 +8612,8 @@ }, "node_modules/jszip/node_modules/readable-stream": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", @@ -8127,6 +8658,8 @@ }, "node_modules/klaw": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, "license": "MIT", "dependencies": { @@ -8324,6 +8857,8 @@ }, "node_modules/lru-cache": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", "dependencies": { @@ -8405,6 +8940,8 @@ }, "node_modules/markdown-it-anchor": { "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", "dev": true, "license": "Unlicense", "peerDependencies": { @@ -8484,6 +9021,15 @@ "dev": true, "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/meshoptimizer": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", @@ -8588,11 +9134,22 @@ }, "node_modules/minimist": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -8740,6 +9297,8 @@ }, "node_modules/multicast-dns": { "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "license": "MIT", "dependencies": { @@ -9170,6 +9729,8 @@ }, "node_modules/open": { "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9308,13 +9869,12 @@ } }, "node_modules/pac-resolver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", - "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, "dependencies": { "degenerator": "^5.0.0", - "ip": "^1.1.8", "netmask": "^2.0.2" }, "engines": { @@ -9513,6 +10073,40 @@ "node": ">=0.10.0" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -9520,6 +10114,15 @@ "dev": true, "license": "MIT" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/pbf": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", @@ -9643,7 +10246,6 @@ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", "dev": true, - "license": "MIT", "dependencies": { "find-up": "^3.0.0" }, @@ -9656,7 +10258,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^3.0.0" }, @@ -9669,7 +10270,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -9683,7 +10283,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -9699,7 +10298,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^2.0.0" }, @@ -9885,6 +10483,8 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -10165,6 +10765,8 @@ }, "node_modules/regexpu-core": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10181,6 +10783,8 @@ }, "node_modules/regjsparser": { "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10260,6 +10864,8 @@ }, "node_modules/replace-in-file/node_modules/glob": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "license": "ISC", "dependencies": { @@ -10278,6 +10884,8 @@ }, "node_modules/replace-in-file/node_modules/minimatch": { "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -10302,6 +10910,8 @@ }, "node_modules/replace-in-file/node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -10366,6 +10976,8 @@ }, "node_modules/requizzle": { "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "license": "MIT", "dependencies": { @@ -10374,8 +10986,9 @@ }, "node_modules/reselect": { "version": "4.1.8", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "dev": true }, "node_modules/resolve": { "version": "1.22.8", @@ -10462,6 +11075,8 @@ }, "node_modules/reusify": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "license": "MIT", "engines": { @@ -10487,6 +11102,8 @@ }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -10597,6 +11214,8 @@ }, "node_modules/schema-utils/node_modules/ajv": { "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "license": "MIT", "dependencies": { @@ -10612,6 +11231,8 @@ }, "node_modules/schema-utils/node_modules/ajv-keywords": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", "dependencies": { @@ -10847,6 +11468,8 @@ }, "node_modules/setimmediate": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "license": "MIT" }, "node_modules/setprototypeof": { @@ -10902,6 +11525,8 @@ }, "node_modules/shpjs": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/shpjs/-/shpjs-4.0.4.tgz", + "integrity": "sha512-+IcS2DoiTGqAONUEN46ZociKGJ2ecs1EVwJuSqnAOkMafxWC8noO3X/zI959RCbqHGvBr1RM1bdk4jc7fYONVg==", "license": "MIT", "dependencies": { "jszip": "^3.5.0", @@ -11000,6 +11625,8 @@ }, "node_modules/sockjs": { "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11009,16 +11636,16 @@ } }, "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, @@ -11036,12 +11663,6 @@ "node": ">= 14" } }, - "node_modules/socks/node_modules/ip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", - "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", - "dev": true - }, "node_modules/sort-asc": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz", @@ -11157,6 +11778,8 @@ }, "node_modules/spdy-transport/node_modules/readable-stream": { "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", "dependencies": { @@ -11188,11 +11811,10 @@ } }, "node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true, - "license": "BSD-3-Clause" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true }, "node_modules/statuses": { "version": "2.0.1", @@ -11294,6 +11916,15 @@ "node": ">=8" } }, + "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, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -11469,6 +12100,8 @@ }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "license": "MIT", "dependencies": { @@ -11572,6 +12205,8 @@ }, "node_modules/through2/node_modules/readable-stream": { "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "dependencies": { @@ -11623,7 +12258,6 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -11647,10 +12281,23 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "license": "MIT" }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/tsconfig-paths": { - "version": "3.14.2", + "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", @@ -11660,8 +12307,9 @@ }, "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" }, @@ -11669,16 +12317,6 @@ "json5": "lib/cli.js" } }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -11866,6 +12504,8 @@ }, "node_modules/underscore": { "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true, "license": "MIT" }, @@ -11909,6 +12549,8 @@ }, "node_modules/unicode-match-property-value-ecmascript": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true, "license": "MIT", "engines": { @@ -11917,6 +12559,8 @@ }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, "license": "MIT", "engines": { @@ -12201,6 +12845,8 @@ }, "node_modules/webpack-cli/node_modules/@webpack-cli/configtest": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, "license": "MIT", "engines": { @@ -12213,6 +12859,8 @@ }, "node_modules/webpack-cli/node_modules/@webpack-cli/info": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, "license": "MIT", "engines": { @@ -12225,6 +12873,8 @@ }, "node_modules/webpack-cli/node_modules/@webpack-cli/serve": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, "license": "MIT", "engines": { @@ -12242,6 +12892,8 @@ }, "node_modules/webpack-cli/node_modules/commander": { "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, "license": "MIT", "engines": { @@ -12250,6 +12902,8 @@ }, "node_modules/webpack-cli/node_modules/interpret": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, "license": "MIT", "engines": { @@ -12258,6 +12912,8 @@ }, "node_modules/webpack-cli/node_modules/rechoir": { "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12410,6 +13066,8 @@ }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "license": "MIT", "dependencies": { @@ -12524,11 +13182,15 @@ }, "node_modules/wildcard": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true, "license": "MIT" }, "node_modules/wkt-parser": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.3.3.tgz", + "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==", "license": "MIT" }, "node_modules/wordwrap": { @@ -12629,6 +13291,8 @@ }, "node_modules/yallist": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" }, diff --git a/package.json b/package.json index 23016fba1b..03464381ab 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "A JS/WebGL framework for 3D geospatial data visualization", "type": "module", "main": "lib/Main.js", + "types": "types/Main.d.ts", "exports": { ".": "./lib/Main.js", "./widgets": "./lib/Utils/gui/Main.js" @@ -21,7 +22,7 @@ "base-test-unit": "cross-env BABEL_DISABLE_CACHE=1 mocha --file test/unit/bootstrap.js --loader=babel-register-esm", "build": "cross-env NODE_ENV=production webpack", "build-dev": "cross-env NODE_ENV=development webpack", - "transpile": "cross-env BABEL_DISABLE_CACHE=1 babel src --out-dir lib", + "transpile": "cross-env BABEL_DISABLE_CACHE=1 babel src --extensions '.js,.ts' --out-dir lib", "start": "cross-env NODE_ENV=development webpack serve", "start-https": "cross-env NODE_ENV=development webpack serve --https", "debug": "cross-env noInline=true npm start", @@ -75,8 +76,11 @@ "@babel/cli": "^7.22.5", "@babel/plugin-transform-runtime": "^7.22.5", "@babel/preset-env": "^7.22.5", + "@babel/preset-typescript": "^7.24.1", "@babel/register": "^7.22.5", "@types/three": "^0.159.0", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", "@xmldom/xmldom": "^0.8.10", "babel-inline-import-loader": "^1.0.1", "babel-loader": "^9.1.3", @@ -96,8 +100,9 @@ "cross-env": "^7.0.3", "eslint": "^8.55.0", "eslint-config-airbnb-base": "^15.0.0", + "eslint-import-resolver-babel-module": "^5.3.2", "eslint-import-resolver-webpack": "^0.13.8", - "eslint-plugin-import": "^2.29.0", + "eslint-plugin-import": "^2.29.1", "eslint-webpack-plugin": "^4.0.1", "github-url-from-git": "^1.5.0", "grunt": "^1.6.1", diff --git a/src/Core/3DTiles/C3DTFeature.js b/src/Core/3DTiles/C3DTFeature.js index 1e58cd777c..98b79cd0d9 100644 --- a/src/Core/3DTiles/C3DTFeature.js +++ b/src/Core/3DTiles/C3DTFeature.js @@ -1,4 +1,4 @@ -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars import { Object3D, Box3 } from 'three'; /** diff --git a/src/Core/AnimationPlayer.js b/src/Core/AnimationPlayer.js index 3575a10c22..1d03574f8f 100644 --- a/src/Core/AnimationPlayer.js +++ b/src/Core/AnimationPlayer.js @@ -55,7 +55,7 @@ class AnimationPlayer extends THREE.EventDispatcher { this.duration = 0; this.state = PLAYER_STATE.STOP; this.waitTimer = null; - this.callback = () => {}; + this.callback = () => { }; } isPlaying() { diff --git a/src/Core/Prefab/Globe/Atmosphere.js b/src/Core/Prefab/Globe/Atmosphere.js index 347b3a494d..87f77cebc0 100644 --- a/src/Core/Prefab/Globe/Atmosphere.js +++ b/src/Core/Prefab/Globe/Atmosphere.js @@ -145,7 +145,7 @@ class Atmosphere extends GeometryLayer { node.material.lightPosition = this.realisticLightingPosition; } - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars preUpdate(context, srcs) { const cameraPosition = context.view.camera3D.position; if (this.fog.enable) { diff --git a/src/Graph/Graph.ts b/src/Graph/Graph.ts new file mode 100644 index 0000000000..aa709de663 --- /dev/null +++ b/src/Graph/Graph.ts @@ -0,0 +1,467 @@ +import { + GraphNode, + DumpDotGlobalStyle, + Type, + Mappings, + JunctionNode, + SubGraphNode, + InputNode, + BuiltinType, + Dependency, + Optimizer, + GraphInputNode, +} from './Prelude'; + +/** Represents a directed graph that guarantees the absence of cycles on use. */ +export default class Graph { + public nodes: Map; + public types: Set; + protected _state: unknown; + protected _valid: boolean; + + public constructor(state: unknown) { + this.nodes = new Map(); + this.types = new Set(); + this._valid = false; + this._state = state; + } + + public get isValid(): boolean { + return this._valid; + } + + // Encapsulated to prevent setting the state to a new reference + public get state(): unknown { + return this._state; + } + + /** + * Get the output of a node at a given frame. + * @throws If the graph is invalid. + * @throws If the node does not exist. + * @returns The output of the node at the given frame. + */ + public getOutput(frame: number, out: Dependency | [GraphNode, string] | [string, string] | GraphNode | string): unknown { + this.validate(); + + if (out instanceof GraphNode) { + out = { node: out, output: GraphNode.defaultIoName }; + } else if (typeof out == 'string') { + const node = this.nodes.get(out); + if (node == undefined) { + throw new Error(`Node "${out}" does not exist in the graph`); + } + out = { node, output: GraphNode.defaultIoName }; + } else if (Array.isArray(out)) { + const [nodeName, output] = out; + const node = nodeName instanceof GraphNode ? nodeName : this.nodes.get(nodeName); + if (node == undefined) { + throw new Error(`Node "${nodeName}" does not exist in the graph`); + } + out = { node, output }; + } + + return out.node.getOutput(out.output, this, frame); + } + + public run(frame: number, end: GraphNode | string): void { + this.validate(); + + let node: GraphNode; + + if (typeof end == 'string') { + const opt = this.nodes.get(end); + if (opt == undefined) { + throw new Error(`Node "${end}" does not exist in the graph`); + } + node = opt; + } else { + node = end; + } + + node.run(this, frame); + } + + /** + * Get a node by name. + * @returns The node with the given name. + */ + public get(name: string): GraphNode | undefined { + return this.nodes.get(name); + } + + /** + * Add or update a node. If the node already exists, it will be updated. + * @throws If the node is orphaned and the graph has at least one node already. + * @returns True if the node was added or updated, false otherwise. + */ + private setSingle(name: string, node: GraphNode): boolean { + if (!(node instanceof InputNode) && this.nodes.size > 0 + && (node instanceof SubGraphNode ? node.graph.inputs.size == 0 : node.inputs.size == 0) + ) { + throw new Error('Orphaned node'); + } + + this._valid = false; + + this.nodes.set(name, node); + for (const [_name, [_, ty]] of node.inputs) { + this.types.add(ty); + } + for (const [_name, output] of node.outputs) { + this.types.add(output.type); + } + + return true; + } + + /** + * Add or update multiple nodes at once. Check the documentation of {@link set} + * for more details. + * Using numerical object keys is not recommended, as they will be automatically sorted, + * possibly leading to unexpected behavior. + * @throws If any of the nodes are orphaned and the graph has at least one node already. + * @returns A map of the results of the set operation. + */ + public set( + nodes: { [name: string]: GraphNode; }, + ): Map { + const results = new Map(); + for (const [name, node] of Object.entries(nodes)) { + results.set(name, this.setSingle(name, node)); + } + return results; + } + + /** + * Determine if the graph is valid. A graph is considered valid if it does + * not contain cycles nor dangling dependencies. + * @throws If the graph is invalid. + */ + public validate(): void { + if (this._valid) { + return; + } + + const visited = new Set(); + for (const [name, node] of this.nodes) { + if (visited.has(name)) { + continue; + } + + this._validationDfs(node, new Set(), visited); + } + + this._valid = true; + } + + /** + * Depth-first search for cycles and dangling dependencies. + * Node argument is assumed to exist within the graph. + * @throws If a cycle is detected or a dangling dependency is found. + */ + private _validationDfs( + node: GraphNode, + path: Set, + visited: Set, + ): void { + // GraphInputNodes are only used as entry points to subgraphs + if (node instanceof GraphInputNode) { + return; + } + + const nodeName = this.findNode(node)?.name; + if (nodeName == undefined) { + console.error(node); + throw new Error(`Node not found in graph after following this path: ${Array.from(path)}`); + } + + if (visited.has(nodeName)) { + return; + } + + // Cycle detection + if (path.has(nodeName)) { + throw new Error( + `Cycle detected: ${Array.from(path).join(' -> ')} -> ${nodeName}`, + ); + } + + // Type checking + for (const [name, [dep, type]] of node.inputs ?? []) { + if (dep != null) { + const output = dep.node.outputs.get(dep.output); + if (output == undefined) { + throw new Error(`Dangling dependency: '${nodeName}.${name}'; ` + + `${dep.node.nodeType} dependency does not have an output named '${dep.output}'`); + } + const outputType = output.type; + + if (outputType != type && type != BuiltinType.Any) { + throw new Error(`Invalid type for dependency ${nodeName}.${name}` + + `, got '${outputType}' expected '${type}'`); + } + } + } + + // DFS + path.add(nodeName); + for (const [_name, [dep, _type]] of node.inputs ?? []) { + if (dep != null) { + this._validationDfs(dep.node, path, visited); + } + } + path.delete(nodeName); + } + + /** + * Remove a node from the graph. + * @throws If the node does not exist. + * @returns true if the node was removed, false otherwise. + */ + public remove(name: string): void { + const node = this.nodes.get(name); + if (node == undefined) { + throw new Error(`Node "${name}" does not exist in the graph`); + } + + this._valid = false; + + Array.from(node.inputs).filter(([_input, [dep, _ty]]) => dep != null).forEach(([input, [dependency, _ty]]) => { + const { node, output } = dependency!; + const out = node.outputs.get(output); + if (out == undefined) { + throw new Error(`Dependency ${node.nodeType} (id: ${node.id}) does not have an output named '${output}'`); + } + out.dependants.delete({ node, input }); + }); + + this.nodes.delete(name); + } + + public findDependants(node: GraphNode): GraphNode[] { + const dependants: GraphNode[] = []; + for (const [_name, n] of this.nodes) { + for (const [_name, [dep, _ty]] of n.inputs) { + if (dep?.node == node) { + dependants.push(n); + } + } + } + return dependants; + } + + public findBacklinks(dependency: Dependency): [string, [GraphNode, string]][] { + const backlinks: [string, [GraphNode, string]][] = []; + for (const [name, node] of this.nodes) { + const inputs = node instanceof SubGraphNode ? new Map(Array.from(node.graph.inputs.entries()).map(([name, input]) => [name, input.input])) : node.inputs; + + for (const [depName, [dep, _depTy]] of inputs.entries()) { + if (dep == null) { + continue; + } + + if (dep.node == dependency.node && dep.output == dependency.output) { + backlinks.push([name, [node, depName]]); + } + } + } + return backlinks; + } + + public optimize(start: GraphNode | string, debug: boolean = false): void { + Optimizer.optimize(this, start, debug); + } + + /** Find a node's entry in the graph. O(n) time complexity. */ + public findGraphNode(node: GraphNode): { name: string, node: GraphNode } | null { + for (const [name, oNode] of this.nodes.entries()) { + if (node == oNode) { + return { name, node: oNode }; + } + } + return null; + } + + /** Find a node in all available node storages. */ + public findNode(node: GraphNode): { name: string, node: GraphNode } | null { + return this.findGraphNode(node); + } + + public get dumpDotStyle(): DumpDotGlobalStyle { + return { + rankdir: 'LR', + node: { + shape: 'mbox', + fontname: 'Arial', + style: 'filled', + fillcolor: 'whitesmoke', + }, + edge: { + fontname: 'Arial', + arrowhead: 'dot', + arrowtail: 'dot', + dir: 'both', + }, + }; + } + + /** + * Dump the graph in the DOT format. + * @throws If a node input is not part of the graph. + * @returns The graph in the DOT format. + */ + public dumpDot(graphName: string = 'G'): string { + const dump: string[] = []; + dump.push(`digraph ${graphName} {`); + + if (this.nodes.size > 0) { + // Global style defaults + Object.entries(this.dumpDotStyle).forEach(([attr, value]) => { + if (typeof value == 'object') { + const formattedAttrs = Object.entries(value) + .map(([k, v]) => `${k}=${v}`) + .join(' '); + dump.push(`\t${attr} [${formattedAttrs}]`); + } else { + dump.push(`\t${attr} = ${value}`); + } + }); + + // Declare nodes + dump.push('\t{'); + for (const [name, node] of this.nodes) { + dump.push(`\t\t${node.dumpDot(name)}`); + } + dump.push('\t}'); + + // Delicious spaghetti :( + // Declare edges + for (const [nodeName, node] of this.nodes) { + for (const [depName, [dep, depTy]] of node.inputs) { + if (dep == null) { continue; } + + // Lookup the dependency node's name + const nodeEntry = this.findNode(dep.node); + if (nodeEntry == undefined) { + throw new Error( + `Input "${depName}" of node "${nodeName}" is not part of the graph`, + ); + } + + const { name: entryName, node: entryNode } = nodeEntry; + const colorStyle = Mappings.colorize(null, depTy); + const attrs = nodeEntry.node.dumpDotEdgeAttr(depTy, { + ...(entryNode instanceof SubGraphNode ? { arrowtail: 'none' } : {}), + ...(node instanceof JunctionNode ? { arrowhead: 'none' } : {}), + ...colorStyle, + }); + const port = node instanceof JunctionNode ? '' : `:"${depName}"`; + + const sourcePort = `:"${dep.output}"`; + if (dep.node instanceof SubGraphNode) { + dump.push(`\t"${entryName}->${dep.output}":e -> "${nodeName}"${port}:w ${attrs};`); + } else { + dump.push(`\t"${entryName}"${sourcePort}:e -> "${nodeName}"${port}:w ${attrs};`); + } + } + + // Link the subgraph inputs while outside of it to force proper positioning of nodes with dot. + if (node instanceof SubGraphNode) { + for (const [iName, iNode] of node.graph.inputs) { + const [dep, depTy] = iNode.input; + if (dep != undefined) { + const nodeEntry = this.findGraphNode(dep.node); + if (nodeEntry == undefined) { + throw new Error( + `Input "${iName}" of subgraph "${node.label}" is not part of the graph`, + ); + } + const { name: entryName, node: _entryNode } = nodeEntry; + const colorStyle = Mappings.colorize(null, depTy); + const attrs = nodeEntry.node.dumpDotEdgeAttr(depTy, { + arrowhead: 'none', + ...colorStyle, + }); + dump.push(`\t"${entryName}":"${dep.output}":e -> "${nodeName}.${iName}":w ${attrs}`); + } + } + } + } + } + + dump.push('}'); + + return dump.join('\n'); + } + + /** + * Dump the graph in the DOT format and convert it to a graphviz link. + * @throws If a node input is not part of the graph. + * @returns The GraphvizOnline URL to view the graph. + */ + public dumpDotGraphvizLink(): string { + const dot = this.dumpDot(); + const escaped = dot + .replaceAll('\n', '%0A') + .replaceAll('\t', '%20%20') + .replaceAll(':', '%3A') + .replaceAll(';', '%3B') + .replaceAll('=', '%3D'); + return `https://dreampuf.github.io/GraphvizOnline/#${escaped}`; + } + + public dumpAdjacencyMatrix(dependants: boolean = false): string { + const nodeCount = GraphNode.totalNodesCreated; + + type Cell = { dependencies: number, dependants: number }; + + const matrix: Cell[][] = Array.from({ length: nodeCount }, () => Array.from({ length: nodeCount }, () => ({ dependencies: 0, dependants: 0 }))); + for (const node of this.nodes.values()) { + const nodeIndex = node.id; + + // Dependencies + for (const [dep, _ty] of node.inputs.values()) { + if (dep != null) { + const depIndex = dep.node.id; + matrix[nodeIndex][depIndex].dependencies += 1; + } + } + + // Dependants + for (const output of node.outputs.values()) { + for (const dep of output.dependants) { + const depIndex = dep.node.id; + matrix[nodeIndex][depIndex].dependants += 1; + } + } + } + + const dump: string[] = []; + const padding = nodeCount > 0 ? Math.floor(Math.log10(nodeCount)) + 1 : 1; + const cellPadding = nodeCount > 0 ? Math.floor(Math.log10(Math.max(nodeCount, ...matrix.map((row): number => + Math.max(...row.map((v): number => + Math.max(v.dependencies, v.dependants))))))) + 1 : 1; + + // Header + dump.push(`${' '.repeat(padding + 2)}${Array.from({ length: nodeCount }, (_, i) => i.toString().padStart(cellPadding)).join(' ')}`); + dump.push(`${' '.repeat(padding + 1)}┌${'─'.repeat((cellPadding + 1) * nodeCount - 1)}`); + + // Rows + for (const [index, row] of matrix.entries()) { + const provider = (v: Cell): number => (dependants ? v.dependants : v.dependencies); + const strRow = row.map(provider).map(v => (v > 0 ? v.toString() : ' ').padStart(padding)).join(' '); + + dump.push(`${index.toString().padStart(padding)} │${strRow}`); + } + + for (const [name, node] of this.nodes.entries()) { + dump.push(`${node.id.toString().padStart(padding)}: ${name}`); + if (node instanceof SubGraphNode) { + dump.push(node.graph.dumpAdjacencyMatrix()); + } + } + + return dump.join('\n'); + } +} diff --git a/src/Graph/LinearSet.ts b/src/Graph/LinearSet.ts new file mode 100644 index 0000000000..eb8c5cd2ac --- /dev/null +++ b/src/Graph/LinearSet.ts @@ -0,0 +1,43 @@ +export default class LinearSet { + private content: T[]; + private comparison: (a: T, b: T) => boolean; + + public constructor(init?: Iterable, comparison?: (a: T, b: T) => boolean) { + this.content = init != undefined ? Array.from(init) : []; + this.comparison = comparison ?? ((a, b) => a == b); + } + + public get size(): number { + return this.content.length; + } + + public add(value: T): void { + if (this.content.find(v => this.comparison(v, value)) == undefined) { + this.content.push(value); + } + } + + public has(value: T): boolean { + return this.get(value) != undefined; + } + + public get(value: T): T | undefined { + return this.content.find(v => this.comparison(v, value)); + } + + public delete(value: T): void { + this.content = this.content.filter(v => !this.comparison(v, value)); + } + + public clear(): void { + this.content = []; + } + + public [Symbol.iterator](): IterableIterator { + return this.values(); + } + + public values(): IterableIterator { + return this.content[Symbol.iterator](); + } +} diff --git a/src/Graph/Mappings.ts b/src/Graph/Mappings.ts new file mode 100644 index 0000000000..4f4dccd742 --- /dev/null +++ b/src/Graph/Mappings.ts @@ -0,0 +1,90 @@ +import * as THREE from 'three'; + +import View from '../Core/View'; + +import { BuiltinType, Type, ColorStyle } from './Types'; + +export default class Mappings { + public static typeMapping: [(v: any) => boolean, BuiltinType][] = [ + // Primitives + [v => typeof v === 'number', BuiltinType.Number], + // iTowns types + [v => v instanceof View, BuiltinType.View], + // Three.js types + [v => v instanceof THREE.WebGLRenderer, BuiltinType.Renderer], + [v => v instanceof THREE.WebGLRenderTarget, BuiltinType.RenderTarget], + [v => v instanceof THREE.Vector2, BuiltinType.Vector2], + [v => v instanceof THREE.Vector3, BuiltinType.Vector3], + [v => v instanceof THREE.Vector4, BuiltinType.Vector4], + // DOM + [v => v instanceof HTMLDivElement, BuiltinType.HtmlDivElement], + ]; + + public static typeOf(value: any): BuiltinType | undefined { + for (const [check, ty] of Mappings.typeMapping) { + if (check(value)) { + return ty; + } + } + return undefined; + } + + + // eslint-disable-next-line no-spaced-func, func-call-spacing + public static stringMapping = new Map string>([ + [BuiltinType.Vector2, v => `(${v.x}, ${v.y})`], + ]); + + public static stringify(value: any, type?: Type): string { + const auto: string = value.toString(); + if (!auto.startsWith('[object ')) { + return auto; + } + + const ty = type ?? Mappings.typeOf(value); + if (ty != undefined) { + return Mappings.stringMapping.get(ty)?.(value) ?? `[${ty}]`; + } + + return '...'; + } + + + public static colorMapping = new Map([ + [BuiltinType.Number, { color: 'chocolate', fillcolor: 'orange' }], + [BuiltinType.Source, { color: 'cornflowerblue', fillcolor: 'deepskyblue' }], + [BuiltinType.View, { color: 'cornflowerblue', fillcolor: 'deepskyblue' }], + [BuiltinType.Placement, { color: 'cornflowerblue', fillcolor: 'deepskyblue' }], + [BuiltinType.Vector2, { color: 'indigo', fillcolor: 'violet' }], + [BuiltinType.Vector3, { color: 'indigo', fillcolor: 'violet' }], + [BuiltinType.Vector4, { color: 'indigo', fillcolor: 'violet' }], + [BuiltinType.RenderTarget, { color: 'darkolivegreen', fillcolor: 'limegreen' }], + [BuiltinType.Renderer, { color: 'darkolivegreen', fillcolor: 'limegreen' }], + [BuiltinType.HtmlDivElement, { color: 'indianred', fillcolor: 'lightcoral' }], + ]); + + public static colorize(value: any, type?: Type): ColorStyle { + const ty = type ?? Mappings.typeOf(value); + if (ty != undefined) { + return Mappings.colorMapping.get(ty) ?? {}; + } + return {}; + } + + public static openGlMapping = new Map([ + [BuiltinType.Number, 'float'], + [BuiltinType.Vector2, 'vec2'], + [BuiltinType.Vector3, 'vec3'], + [BuiltinType.Vector4, 'vec4'], + [BuiltinType.RenderTarget, 'sampler2D'], + [BuiltinType.Texture, 'sampler2D'], + ]); + + public static toOpenGL(type: Type): string { + const glTy = Mappings.openGlMapping.get(type); + if (glTy == undefined) { + throw new Error(`Type ${type} does not have a known OpenGL equivalent`); + } + return glTy; + } +} diff --git a/src/Graph/Nodes/CameraDataNode.ts b/src/Graph/Nodes/CameraDataNode.ts new file mode 100644 index 0000000000..5c45312277 --- /dev/null +++ b/src/Graph/Nodes/CameraDataNode.ts @@ -0,0 +1,21 @@ +import { OrthographicCamera, PerspectiveCamera } from 'three'; +import { BuiltinType, Dependency } from '../Prelude'; +import ProcessorNode from './ProcessorNode'; + +type CameraLike = PerspectiveCamera | OrthographicCamera; + +export default class CameraDataNode extends ProcessorNode { + constructor(camera: Dependency) { + super( + { camera: [camera, BuiltinType.Camera] }, + new Map(Object.entries({ + cameraNear: BuiltinType.Number, + cameraFar: BuiltinType.Number, + })), + (_frame, args) => { + const camera = args.camera as CameraLike; + this.updateOutputs({ cameraNear: camera.near, cameraFar: camera.far }); + }, + ); + } +} diff --git a/src/Graph/Nodes/DepthGetterNode.ts b/src/Graph/Nodes/DepthGetterNode.ts new file mode 100644 index 0000000000..9f1dd76c49 --- /dev/null +++ b/src/Graph/Nodes/DepthGetterNode.ts @@ -0,0 +1,13 @@ +import { WebGLRenderTarget } from 'three'; +import { BuiltinType, Dependency, ProcessorNode } from '../Prelude'; + +export default class DepthGetterNode extends ProcessorNode { + constructor(target: Dependency) { + super( + { target: [target, BuiltinType.RenderTarget] }, + BuiltinType.Texture, + (_frame, args) => + this.updateOutputs({ [DepthGetterNode.defaultIoName]: (args.target as WebGLRenderTarget).depthTexture }), + ); + } +} diff --git a/src/Graph/Nodes/FieldGetterNode.ts b/src/Graph/Nodes/FieldGetterNode.ts new file mode 100644 index 0000000000..bca797527a --- /dev/null +++ b/src/Graph/Nodes/FieldGetterNode.ts @@ -0,0 +1,25 @@ +import { BuiltinType, Dependency, DumpDotNodeStyle, Type } from '../Prelude'; +import ProcessorNode from './ProcessorNode'; + +export default class FieldGetterNode extends ProcessorNode { + public constructor(input: Dependency, path: string, outputType: Type) { + super( + { input: [input, BuiltinType.Any] }, + outputType, + (_frame, args) => + path.split('.').reduce((acc: any, pathSegment) => acc[pathSegment], args.input), + ); + } + + public override get nodeType(): string { + return FieldGetterNode.name; + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + const { label: _, attrs } = super.dumpDotStyle; + return { + label: name => name, + attrs, + }; + } +} diff --git a/src/Graph/Nodes/GlobeViewNode.ts b/src/Graph/Nodes/GlobeViewNode.ts new file mode 100644 index 0000000000..f54a125be9 --- /dev/null +++ b/src/Graph/Nodes/GlobeViewNode.ts @@ -0,0 +1,36 @@ +import { Extent, GlobeView } from '../../Main'; +import { BuiltinType, Dependency, DumpDotNodeStyle, ViewNode } from '../Prelude'; + +export default class GlobeViewNode extends ViewNode { + public constructor(viewerDiv: Dependency, placement: Dependency) { + super( + viewerDiv, + (_frame, args) => { + if (args.viewerDiv == undefined || args.placement == undefined) { + throw new Error('Missing view dependencies'); + } + + const view = new GlobeView(args.viewerDiv as HTMLDivElement, args.placement as Extent); + this.updateOutputs({ + view, + renderer: view.mainLoop.gfxEngine.renderer, + camera: view.camera, + }); + }, + { placement: [placement, BuiltinType.Placement] }, + ); + } + + public override get nodeType(): string { + return GlobeViewNode.name; + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + return { + label: name => name, + attrs: { + color: 'cornflowerblue', + }, + }; + } +} diff --git a/src/Graph/Nodes/GraphInputNode.ts b/src/Graph/Nodes/GraphInputNode.ts new file mode 100644 index 0000000000..7c8bd7fe5f --- /dev/null +++ b/src/Graph/Nodes/GraphInputNode.ts @@ -0,0 +1,24 @@ +import { Dependency, GraphNode, JunctionNode, Type } from '../Prelude'; + +export default class GraphInputNode extends JunctionNode { + private inputNodeName: string | undefined; + + public constructor([name, input]: [string, Dependency | Type]) { + if (typeof input != 'string') { + super(input); + this.inputNodeName = name; + } else { + super(input); + } + } + + public get graphInput(): [string | undefined, [Dependency | null, Type]] { + return [this.inputNodeName, this.inputs.get(GraphNode.defaultIoName)!]; + } + + public set graphInput([name, node]: [string, Dependency]) { + const input = this.inputs.get(GraphNode.defaultIoName)!; + input[0] = node; + this.inputNodeName = name; + } +} diff --git a/src/Graph/Nodes/GraphNode.ts b/src/Graph/Nodes/GraphNode.ts new file mode 100644 index 0000000000..7b6d0f08f6 --- /dev/null +++ b/src/Graph/Nodes/GraphNode.ts @@ -0,0 +1,353 @@ +import { Type, Dependency, Dependant, DumpDotNodeStyle, Graph } from '../Prelude'; +import LinearSet from '../LinearSet'; + +function lerp(a: number, b: number, t: number): number { + return a + (b - a) * t; +} + +export class Output { + public value: unknown; + public type: Type; + public dependants: LinearSet; + + constructor(value: unknown, type: Type) { + this.value = value; + this.type = type; + this.dependants = new LinearSet([], (a, b) => a.node == b.node && a.input == b.input); + } +} + +/** + * Represents a node in a directed graph. + * Base class for all other types of nodes. + */ +export default abstract class GraphNode { + public static defaultIoName = 'value'; + // protected _out: [number, any | undefined]; + protected _out: { + frame: number, + outputs: Map, + /** Stored in ms. */ + timeTaken?: number, + }; + + private static idCounter = 0; + private _id: number; + + public inputs: Map; + + private _isStatic: boolean; + private _needsUpdate: boolean; + + public constructor( + inputs: Map, + // Optional to allow for clean side-effect-only nodes + outputs: Map | Type | null, + isStatic: boolean = false, + ) { + this._id = GraphNode.idCounter++; + + this.inputs = new Map(); + const inputDependencies = Object.fromEntries(Array.from(inputs.entries()) + .filter(([_name, [dep, _ty]]) => dep != null) + .map(([name, [dep, ty]]): [string, Dependency | Type] => [ + name, + dep instanceof GraphNode + ? { node: dep, output: GraphNode.defaultIoName } + : dep ?? ty, + ])); + this.updateInputs(inputDependencies, true); + + for (const input of this.inputs) { + const [name, [dep, _ty]] = input; + if (dep != null && dep instanceof GraphNode) { + const has = dep.outputs.get(GraphNode.defaultIoName)!.dependants.has({ node: this, input: name }); + if (!has) { + throw new Error(`Dependency ${dep.nodeType} (id: ${dep.id}) does not have GraphNode (id: ${this.id}) as a dependant`); + } + } + } + + const normalizedOutputs: Map = new Map((() => { + if (outputs == undefined) { + return []; + } else if (outputs instanceof Map) { + return Array.from(outputs.entries()) + .map(([name, ty]) => [name, new Output(undefined, ty)]); + } else if (typeof outputs == 'string') { + return [[GraphNode.defaultIoName, new Output(undefined, outputs)]]; + } else { + throw new Error('Unrecognized type when constructing node outputs'); + } + })()); + + this._out = { frame: -1, outputs: normalizedOutputs }; + this._isStatic = isStatic; + this._needsUpdate = this._isStatic; + } + + protected abstract _apply(graph?: Graph, frame?: number): void; + + public abstract get nodeType(): string; + public get id(): number { + return this._id; + } + public static get totalNodesCreated(): number { + return GraphNode.idCounter; + } + + private addAsDependantTo(target: Dependency, input: string): void { + const { node, output: outputName } = target; + const output = node.outputs.get(outputName); + if (output == undefined) { + throw new Error(`Provided dependency does not exist: ${node.nodeType} (id: ${node.id}) node does not have an output named '${outputName}'`); + } + output.dependants.add({ node: this, input }); + } + + public deleteInput(name: string): void { + const input = this.inputs.get(name); + if (input == undefined) { + throw new Error(`Provided ${this.nodeType} (id: ${this.id}) node does not have an input named '${name}' to delete`); + } + + const dep = input[0]; + if (dep != null) { + dep.node.outputs.get(dep.output)!.dependants.delete({ node: this, input: name }); + } + + this.inputs.delete(name); + } + + private updateInput(name: string, dep: Dependency | Type): void { + const input = this.inputs.get(name); + if (input == undefined) { + throw new Error(`Provided ${this.nodeType} (id: ${this.id}) node does not have an input named '${name}' to update`); + } + + if (typeof dep != 'string') { + // Removing a link + if (input[0] != null) { + const oDep = input[0]; + oDep.node.outputs.get(oDep.output)!.dependants.delete({ node: this, input: name }); + } else { // Adding a link + this.addAsDependantTo(dep, name); + } + input[0] = dep; + } else { + input[0] = null; + input[1] = dep; + } + } + + private addInput(name: string, dep: Dependency | Type): void { + if (this.inputs.has(name)) { + throw new Error(`Provided ${this.nodeType} node already has an input named '${name}'`); + } + + if (typeof dep != 'string') { + this.addAsDependantTo(dep!, name); + this.inputs.set(name, [dep, dep.node.outputs.get(dep.output)!.type]); + } else { + this.inputs.set(name, [null, dep]); + } + } + + public updateInputs(updates: { [name: string]: Dependency | Type }, adding: boolean = false): void { + for (const [name, dep] of Object.entries(updates)) { + if (adding) { + this.addInput(name, dep); + } else { + this.updateInput(name, dep); + } + } + } + + /** + * Returns the map of all of the node's outputs. + */ + public get outputs(): Map { + return this._out.outputs; + } + + /** + * Returns a dependency object for the desired output. + */ + public toDep(output?: string): Dependency { + return { node: this, output: output ?? GraphNode.defaultIoName }; + } + + /** + * Updates desire node outputs with the new values, updating the frame if it is provided. + * + * @throws If an output does not exist. + */ + public updateOutputs(updates: { [name: string]: unknown }, frame?: number): void { + const errors = []; + + for (const [name, value] of Object.entries(updates)) { + const output = this._out.outputs.get(name); + if (output == undefined) { + errors.push(`Provided ${this.nodeType} node does not have an output named '${name}' to update`); + continue; + } + output.value = value; + } + + if (frame != undefined) { + this._out.frame = frame; + } + + if (errors.length > 0) { + throw new Error(errors.join('\n')); + } + } + + /** + * Marks the node as needing to be updated on the next frame and propagates the update to its dependant nodes + * recursively (depth-first). + */ + public needsUpdate(): void { + this._needsUpdate = true; + + for (const output of this.outputs.values()) { + for (const dep of output.dependants) { + if (!dep.node._needsUpdate) { + dep.node.needsUpdate(); + } + } + } + } + + /** + * Get the output of the node for a given frame. + * + * @param name The name of the output to get. + * @param graph The graph the node is a part of. + * @param frame The frame to get the output for. + * @returns The output of the node at the given frame. + */ + public getOutput(name: string = GraphNode.defaultIoName, graph?: Graph, frame: number = 0): unknown { + const getOutput = this._out.outputs.get(name); + if (getOutput == undefined) { + throw new Error(`Provided ${this.nodeType} node does not have an output named '${name}'`); + } + + this._needsUpdate ||= getOutput.value == undefined; + + this.run(graph, frame); + + return getOutput.value; + } + + /** + * Run the node for a given frame. + * + * @param graph The graph the node is a part of. + * @param frame The frame to get the output for. + */ + public run(graph?: Graph, frame: number = 0): void { + const oFrame = this._out.frame; + + if (!this._isStatic && oFrame !== frame || this._needsUpdate) { + this._apply(graph, frame); + this._out.frame = frame; + + if (this._needsUpdate) { + this.needsUpdate(); + } + } + + this._needsUpdate = false; + } + + /** + * Get the style for the node when dumping to DOT format. + */ + public abstract get dumpDotStyle(): DumpDotNodeStyle; + + /** + * Get the DOT attribute string for the node. + * @param name The name of the node. + */ + // TODO: refactor this into a sort of component pattern + // use library: https://github.com/prantlf/graphviz-builder (also removes the need for dreampuf's viewer) + public dumpDot(name: string): string { + const { label, attrs } = this.dumpDotStyle; + const formattedAttrs = Object.entries(attrs) + .map(([k, v]) => `${k} = ${v}`) + .join(' '); + + const colspan = Math.max(1, (this.inputs.size > 0 ? 1 : 0) + (this.outputs.size > 0 ? 1 : 0)); + + const hasTiming = this._out.timeTaken != undefined; + const generateTiming = (): string => { + const lerpChannel = (): number => Math.floor(lerp(0, 255, Math.min(1, this._out.timeTaken! / 20))); + const mapChannel = (op: (x: number) => number): string => op(lerpChannel()).toString(16).padStart(2, '0'); + const timingColor = this._isStatic + ? '#000000' + : `#${mapChannel(x => x)}${mapChannel(x => 255 - x)}00`; + + return `${this._out.timeTaken!}ms`; + }; + + const header = [ + '', + `\t${this.nodeType}`, + ...(hasTiming ? [`\t${generateTiming()}`] : []), + '', + ]; + + const labelName = label(name).trim(); + const formattedName = labelName.length == 0 + ? [] + : [ + `
${this._isStatic ? '
' : ''}`, + `\t[${this.id}] ${labelName}`, + '', + ]; + + const zip = (a: any[], b: any[]): any[] => Array.from(Array(Math.max(b.length, a.length)), (_, i) => [a[i], b[i]]); + const iPort = (name: string, dep?: GraphNode): string => + `${name}`; + const oPort = (name: string): string => `${name}`; + + const ports = zip(Array.from(this.inputs), Array.from(this.outputs)) + .map(([i, o]) => { + const input = (() => { + if (i == undefined) { + if (this.inputs.size > 0) { + return ''; + } return ''; + } return iPort(i[0], i[1][0]); + })(); + + const output = (() => { + if (o == undefined) { + if (this.outputs.size > 0) { + return ''; + } return ''; + } return oPort(o[0]); + })(); + + return ['', input, output, ''].join(''); + }); + + const lHtml = ['<', ...header, ...formattedName, ...ports, '
>'].join('\n'); + + return `"${name}" [label=${lHtml} ${formattedAttrs} margin=.05]`; + } + + /** + * Get the DOT attribute string for the outgoing edges. + * + * Example expected output: [label=" Renderer"] + */ + public dumpDotEdgeAttr(ty: Type, extra?: { [attr: string]: string }): string { + const attrs = Object.entries(extra ?? {}) + .map(([name, value]) => `${name}="${value}"`) + .join(' '); + + return `[label=" ${ty}" ${attrs}]`; + } +} diff --git a/src/Graph/Nodes/GraphOutputNode.ts b/src/Graph/Nodes/GraphOutputNode.ts new file mode 100644 index 0000000000..4e9950b055 --- /dev/null +++ b/src/Graph/Nodes/GraphOutputNode.ts @@ -0,0 +1,30 @@ +import { Dependency, GraphNode, JunctionNode, Type } from '../Prelude'; + +export default class GraphOutputNode extends JunctionNode { + public constructor(input: Dependency | [GraphNode, string]) { + if (Array.isArray(input)) { + const [node, output] = input; + super({ node, output }); + } else { + super(input); + } + } + + public get graphOutput(): [Dependency, Type] { + const [dep, ty] = this.inputs.get(GraphNode.defaultIoName)!; + return [dep!, ty]; + } + + public set graphOutput(dependency: Dependency | [GraphNode, string]) { + const [node, output] = Array.isArray(dependency) ? dependency : [dependency.node, dependency.output]; + const socket = node.outputs.get(output); + if (socket == undefined) { + throw new Error(`Provided ${node.nodeType} node does not have an output named '${output}'`); + } + this.inputs.set(GraphNode.defaultIoName, [{ node, output }, socket.type]); + } + + public dumpDot(name: string): string { + return super.dumpDot(`${name}`); + } +} diff --git a/src/Graph/Nodes/InputNode.ts b/src/Graph/Nodes/InputNode.ts new file mode 100644 index 0000000000..f4a63eb6f5 --- /dev/null +++ b/src/Graph/Nodes/InputNode.ts @@ -0,0 +1,45 @@ +import GraphNode from './GraphNode'; +import { Type, DumpDotNodeStyle, Mappings, Graph } from '../Prelude'; + +/** Represents a node that outputs a constant value. */ +export default class InputNode extends GraphNode { + private _type: Type; + private _value: unknown; + + public constructor(value: unknown, type?: Type) { + const ty = type ?? Mappings.typeOf(value); + if (ty == undefined) { + throw new Error('Input node type could not be inferred'); + } + + super(new Map(), ty, true); + this._value = value; + this._type = ty; + } + + public get outputType(): Type { + return this._type; + } + + protected override _apply(_graph?: Graph, frame: number = 0): void { + this.updateOutputs({ [GraphNode.defaultIoName]: this._value }, frame); + } + + public override get nodeType(): string { + return InputNode.name; + } + + public set value(value: unknown) { + this._value = value; + this.needsUpdate(); + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + return { + label: name => `${name}: ${Mappings.stringify(this._value)}`, + attrs: { + color: 'goldenrod', + }, + }; + } +} diff --git a/src/Graph/Nodes/JunctionNode.ts b/src/Graph/Nodes/JunctionNode.ts new file mode 100644 index 0000000000..464696b220 --- /dev/null +++ b/src/Graph/Nodes/JunctionNode.ts @@ -0,0 +1,77 @@ +import { Dependency, DumpDotNodeStyle, Graph, Type, Mappings } from '../Prelude'; +import GraphNode from './GraphNode'; + +export default class JunctionNode extends GraphNode { + public constructor(input: Dependency | Type) { + if (typeof input != 'string') { + const { node, output } = input; + const nodeOutput = node.outputs.get(output); + if (nodeOutput == undefined) { + throw new Error(`Provided ${node.nodeType} node does not have an output named '${output}'`); + } + const ty = nodeOutput.type; + + super(new Map([[GraphNode.defaultIoName, [input, ty]]]), ty); + } else { + super(new Map([[GraphNode.defaultIoName, [null, input]]]), input); + } + } + + protected override _apply(graph?: Graph, frame: number = 0): void { + const dep = this.inputs.get(GraphNode.defaultIoName)![0]; + + if (dep == null) { + this.updateOutputs({ [GraphNode.defaultIoName]: null }, frame); + return; + } + + const { node, output } = dep; + this.updateOutputs({ [GraphNode.defaultIoName]: node.getOutput(output, graph, frame) ?? null }, frame); + } + + public get input(): [Dependency | null, Type] { + return this.inputs.get(GraphNode.defaultIoName)!; + } + + public set input(node: Dependency) { + this.updateInputs({ [GraphNode.defaultIoName]: node }); + } + + public override get nodeType(): string { + return JunctionNode.name; + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + return { + label: name => `${name}`, + attrs: { + shape: 'doublecircle', + width: '.1', + height: '.1', + ...Mappings.colorize(null, this.outputs.get(GraphNode.defaultIoName)!.type), + }, + }; + } + + public override dumpDot(name: string): string { + const { label: _, attrs } = this.dumpDotStyle; + const formattedAttrs = Object.entries(attrs) + .map(([k, v]) => `${k}=${v}`) + .join(' '); + + return `"${name}" [label="" ${formattedAttrs} margin=.05]`; + } + + public override dumpDotEdgeAttr(ty: Type, extra?: { [attr: string]: string; } | undefined): string { + const attrs = Object.entries(extra ?? {}) + .map(([name, value]) => `${name}="${value}"`) + .join(' '); + + const input = this.inputs.get(GraphNode.defaultIoName)![0]; + if (input != undefined) { + return input.node.dumpDotEdgeAttr(ty, extra); + } + + return `[${attrs}]`; + } +} diff --git a/src/Graph/Nodes/PlanarViewNode.ts b/src/Graph/Nodes/PlanarViewNode.ts new file mode 100644 index 0000000000..b672355dc3 --- /dev/null +++ b/src/Graph/Nodes/PlanarViewNode.ts @@ -0,0 +1,36 @@ +import { Extent, PlanarView } from '../../Main'; +import { BuiltinType, Dependency, DumpDotNodeStyle, ViewNode } from '../Prelude'; + +export default class PlanarViewNode extends ViewNode { + public constructor(viewerDiv: Dependency, placement: Dependency) { + super( + viewerDiv, + (_frame, args) => { + if (args.viewerDiv == undefined || args.placement == undefined) { + throw new Error('Missing view dependencies'); + } + + const view = new PlanarView(args.viewerDiv as HTMLDivElement, args.placement as Extent); + this.updateOutputs({ + view, + renderer: view.mainLoop.gfxEngine.renderer, + camera: view.camera, + }); + }, + { placement: [placement, BuiltinType.Placement] }, + ); + } + + public override get nodeType(): string { + return PlanarViewNode.name; + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + return { + label: name => name, + attrs: { + color: 'cornflowerblue', + }, + }; + } +} diff --git a/src/Graph/Nodes/ProcessorNode.ts b/src/Graph/Nodes/ProcessorNode.ts new file mode 100644 index 0000000000..64152c2352 --- /dev/null +++ b/src/Graph/Nodes/ProcessorNode.ts @@ -0,0 +1,42 @@ +import GraphNode from './GraphNode'; +import { Type, Dependency, DumpDotNodeStyle, Graph } from '../Prelude'; + +/** Represents a mapping from a set of inputs to an output. */ +export default class ProcessorNode extends GraphNode { + public constructor( + inputs: { [name: string]: [Dependency, Type] }, + outputs: Map | Type | null, + public callback: (frame: number, args: { [arg: string]: unknown }) => void, + isStatic: boolean = false, + ) { + super(new Map(Object.entries(inputs)), outputs, isStatic); + } + + protected override _apply(graph?: Graph, frame: number = 0): void { + const inputs = Array.from(this.inputs); + const args = inputs.map(([name, [dep, _ty]]): [string, unknown] => [ + name, + dep?.node.getOutput(dep.output, graph, frame) ?? null, + ]); + const argObj = Object.fromEntries(args); + + this._out.frame = frame; + + const start = Date.now(); + this.callback(frame, argObj); + this._out.timeTaken = Date.now() - start; + } + + public override get nodeType(): string { + return ProcessorNode.name; + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + return { + label: name => `${name}`, + attrs: { + color: 'lightskyblue', + }, + }; + } +} diff --git a/src/Graph/Nodes/RenderViewNode.ts b/src/Graph/Nodes/RenderViewNode.ts new file mode 100644 index 0000000000..3be22020b5 --- /dev/null +++ b/src/Graph/Nodes/RenderViewNode.ts @@ -0,0 +1,56 @@ +import * as THREE from 'three'; + +import { BuiltinType, Dependency, DumpDotNodeStyle } from '../Prelude'; +import ProcessorNode from './ProcessorNode'; + +import View from '../../Core/View'; +import MainLoop from '../../Core/MainLoop'; +import c3DEngine from '../../Renderer/c3DEngine'; + +export default class RenderViewNode extends ProcessorNode { + private _target: THREE.WebGLRenderTarget | null = null; + + public constructor(view: Dependency, toScreen: boolean = false) { + super({ view: [view, BuiltinType.View] }, BuiltinType.RenderTarget, (_frame, args) => { + const view = args.view as View; + const engine = (view.mainLoop as MainLoop).gfxEngine as c3DEngine; + const renderer = engine.renderer as THREE.WebGLRenderer; + + if (!this._target) { + if (toScreen) { + this._target = null; + } else { + const fsrt = engine.fullSizeRenderTarget; + + this._target = new THREE.WebGLRenderTarget(fsrt.width, fsrt.height, { + minFilter: THREE.LinearFilter, + magFilter: THREE.NearestFilter, + format: THREE.RGBAFormat, + type: THREE.FloatType, + }); + this._target.depthBuffer = true; + this._target.depthTexture = new THREE.DepthTexture(fsrt.width, fsrt.height); + this._target.depthTexture.type = THREE.UnsignedShortType; + } + } + + renderer.setRenderTarget(this._target); + renderer.clear(); + renderer.render(view.scene, view.camera3D); + + this.updateOutputs({ [RenderViewNode.defaultIoName]: this._target }); + }); + } + + public override get nodeType(): string { + return RenderViewNode.name; + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + const { label: _, attrs } = super.dumpDotStyle; + return { + label: (name: string) => name, + attrs, + }; + } +} diff --git a/src/Graph/Nodes/ScreenShaderNode.ts b/src/Graph/Nodes/ScreenShaderNode.ts new file mode 100644 index 0000000000..b519141868 --- /dev/null +++ b/src/Graph/Nodes/ScreenShaderNode.ts @@ -0,0 +1,230 @@ +import * as THREE from 'three'; +import { Vector2 } from 'three'; +import { BuiltinType, Dependency, DumpDotNodeStyle, GraphNode, Type, Mappings, ProcessorNode } from '../Prelude'; +import { CameraLike } from '../Types'; + +type CallbackArgs = { + target: THREE.WebGLRenderTarget; + renderer: THREE.WebGLRenderer; +} & { [name: string]: any }; + +type FragmentShaderParts = { + includes?: string[], + defines?: { [name: string]: number | string }, + uniforms?: { [name: string]: Dependency | GraphNode | Type }; + auxCode?: string; + main: string; +}; + +/** + * Applies a shader to a render target. + * Some default bindings are provided (without need to redefine them in your shader): + * - `vUv`............(varying vec2): The UV coordinates of the current fragment in the input texture. + * - `tDiffuse`..(uniform sampler2D): The color texture of the render target. + * + * Other uniforms are available but need to be explicitly declared in your code: + * - `tDepth`............(sampler2D): The depth texture of the render target. + * - `resolution`.............(vec2): The width and height of the render target. + * - `cameraNear`............(float): The near plane of the camera. + * - `cameraFar`.............(float): The far plane of the camera. + */ +export default class ScreenShaderNode extends ProcessorNode { + protected static get vertexShader(): string { + return /* glsl */` +varying vec2 vUv; + +void main() { + vUv = uv; + gl_Position = vec4(position, 1.0); +} +`; + } + + protected static get defaultFragmentShader(): FragmentShaderParts { + return { + main: /* glsl */'return color;', + }; + } + + // HACK: Essentially a scuffed singleton pack. + // PERF: Evaluate the cost of having a scene per shader node instead. + protected static _scene: THREE.Scene; + protected static _quad: THREE.Mesh; + protected static _camera: CameraLike; + + // Kept for debug purposes + public material: THREE.ShaderMaterial; + + protected _fragmentShaderParts: FragmentShaderParts; + + private static _init(): void { + if (ScreenShaderNode._scene == undefined) { + ScreenShaderNode._scene = new THREE.Scene(); + + // Setup the quad used to render the effects + ScreenShaderNode._quad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2)); + ScreenShaderNode._quad.frustumCulled = false; + + ScreenShaderNode._scene.add(ScreenShaderNode._quad); + + ScreenShaderNode._camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); + } + } + + public constructor( + target: Dependency, + renderer: Dependency, + { fragmentShaderParts = ScreenShaderNode.defaultFragmentShader, toScreen = false }: { + fragmentShaderParts?: FragmentShaderParts, + toScreen?: boolean + }, + ) { + ScreenShaderNode._init(); + + const uniforms = fragmentShaderParts.uniforms ?? {}; + + const fullUniforms = Object.fromEntries( + Object.entries(uniforms) + .map(([name, uniform]): [string, [Dependency | null, Type]] => { + let val: [Dependency | null, Type]; + if (typeof uniform == 'string') { + val = [null, uniform]; + } else if (uniform instanceof GraphNode) { + val = [{ node: uniform, output: GraphNode.defaultIoName }, uniform.outputs.get(GraphNode.defaultIoName)!.type]; + } else { + val = [uniform, uniform.node.outputs.get(uniform.output)!.type]; + } + + return [name, val]; + }), + ); + + super( + { + // Unpacking the uniforms object first allows us to ignore + // potential 'target' and 'renderer' fields. + ...fullUniforms, + target: [target, BuiltinType.RenderTarget], + renderer: [renderer, BuiltinType.Renderer], + }, + toScreen ? null : BuiltinType.RenderTarget, + (_frame, args) => { + const { target: input, renderer, ...rest } = args as CallbackArgs; + + const camera = ScreenShaderNode._camera; + + const uniforms = { + tDiffuse: input.texture, + tDepth: input.depthTexture, + resolution: new Vector2(input.width, input.height), + cameraNear: camera.near, + cameraFar: camera.far, + ...rest, + }; + + // Set uniforms + for (const [name, value] of Object.entries(uniforms ?? {})) { + this.material.uniforms[name] = { value }; + } + + ScreenShaderNode._quad.material = this.material; + + const target: THREE.WebGLRenderTarget | null = toScreen + ? null + : ((this.outputs.get(GraphNode.defaultIoName)!.value as THREE.WebGLRenderTarget | null) ?? createRenderTarget(input)); + + renderer.setRenderTarget(target); + renderer.clear(); + renderer.render(ScreenShaderNode._scene, ScreenShaderNode._camera); + + if (target != null) { + this.updateOutputs({ [ScreenShaderNode.defaultIoName]: target }); + } + }); + + this._fragmentShaderParts = fragmentShaderParts; + const frag = ScreenShaderNode.buildFragmentShader(this._fragmentShaderParts); + this.material = ScreenShaderNode.buildMaterial(frag); + } + + public get fragmentShaderParts(): FragmentShaderParts { + return this._fragmentShaderParts; + } + + // TODO: group this and similar operations in their own class + public static buildFragmentShader({ includes, defines, uniforms, auxCode, main }: FragmentShaderParts): string { + const uniformDeclarations = Object.entries(uniforms ?? {}) + .map(([name, uniform]): string => { + let ty: Type; + + if (typeof uniform == 'string') { + ty = uniform; + } else if (uniform instanceof GraphNode) { + ty = uniform.outputs.get(GraphNode.defaultIoName)!.type; + } else { + ty = uniform.node.outputs.get(uniform.output)!.type; + } + + // TODO: Create a way to mark types as non-automatic uniforms + // Maybe completely remove automatic uniform creation and leave it up to the user + if (ty == BuiltinType.Float32Array) { + return ''; + } + + return `uniform ${Mappings.toOpenGL(ty)} ${name};`; + }) + .filter(s => s.length > 0); + + return [ + // highp by default for simplicity, will change if complaints arise + 'precision highp float;\n', + // Pre-processor statements + ...includes?.map(inc => `#include <${inc}>`) ?? [], + '', + ...Object.entries(defines ?? {}).map(([name, value]) => `#define ${name} ${value}`), + '', + // UVs + 'varying vec2 vUv;', + // Uniforms + 'uniform sampler2D tDiffuse;', + ...(uniformDeclarations.length > 0 ? [uniformDeclarations.join('\n')] : []), + // User code + ...(auxCode != undefined ? [auxCode] : []), + 'vec4 shader(in vec4 color) {', + ...main.split('\n').map(s => ` ${s}`), + '}', + '', + 'void main() {', + ' gl_FragColor = shader(texture2D(tDiffuse, vUv));', + '}', + ].join('\n'); + } + + public static buildMaterial(fragmentShader: string): THREE.ShaderMaterial { + return new THREE.ShaderMaterial({ + fragmentShader, + vertexShader: ScreenShaderNode.vertexShader, + }); + } + + public override get nodeType(): string { + return ScreenShaderNode.name; + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + const { label, attrs } = super.dumpDotStyle; + return { + label, + attrs, + }; + } +} + +function createRenderTarget(input: THREE.WebGLRenderTarget): THREE.WebGLRenderTarget | null { + const target = new THREE.WebGLRenderTarget(input.width, input.height); + target.depthBuffer = true; + target.depthTexture = new THREE.DepthTexture(input.width, input.height); + target.depthTexture.type = THREE.UnsignedShortType; + return target; +} + diff --git a/src/Graph/Nodes/Source/OrientedImageSourceNode.ts b/src/Graph/Nodes/Source/OrientedImageSourceNode.ts new file mode 100644 index 0000000000..c91d2fceb4 --- /dev/null +++ b/src/Graph/Nodes/Source/OrientedImageSourceNode.ts @@ -0,0 +1,140 @@ +import { Matrix3Tuple, Vector2Tuple, Vector3Tuple } from 'three'; +import { BuiltinType, Dependency, type Source } from 'Graph/Types'; +import { Extent, Fetcher } from 'Main'; +import Cache from 'Core/Scheduler/Cache'; +import SourceNode from './SourceNode'; + +type OISDescriptor = { + url: string, + crs: string, + orientationsUrl?: string, + calibrationUrl?: string, +} + +type CameraCalibrationData = { + /** Camera ID */ + id: number, + // TODO: Add documentation + mask?: string, + /** Rotation matrix */ + rotation: Matrix3Tuple, + /** Translation vector */ + position: Vector3Tuple, + /** Camera intrinsic matrix */ + projection: Matrix3Tuple, + /** Width and height */ + size: Vector2Tuple, + distortion: { + /** Principal Point of Symmetry */ + pps: Vector2Tuple, + // TODO: Add documentation + poly357: Vector3Tuple, + limit: number, + }, + // TODO: Find spec for calibration data and remove this catch-all +} & { [name: string]: unknown }; + +type OrientationsData = { + type: string, + features: { + type: string, + geometry: { + type: string, + coordinates: Vector3Tuple, + }, + properties: { + id: number, + easting: number, + northing: number, + altitude: number, + heading: number, + roll: number, + pitch: number, + date: Date, + } + }[], + properties: CameraCalibrationData[], + crs: { + type: string, + properties: { + code: number, + }, + }, +}; + +type OISConfig = { calibration: CameraCalibrationData[], orientations: OrientationsData }; + +type OISDataLoadInput = { crs: string }; + +// FIXME: replace unknown with the actual types +export class OrientedImageSource implements Source { + private url: string; + private crs: string; + private whenReady: Promise; + private cache: { [crs: string]: Cache }; + + constructor(descriptor: OISDescriptor) { + this.url = descriptor.url; + this.crs = descriptor.crs; + + const promises = [ + descriptor.calibrationUrl ? Fetcher.json(descriptor.calibrationUrl) : Promise.resolve(), + descriptor.orientationsUrl ? Fetcher.json(descriptor.orientationsUrl) : Promise.resolve(), + ]; + + this.whenReady = Promise.all(promises).then(([calibration, orientations]) => ({ + calibration: calibration as CameraCalibrationData[], + orientations: orientations as OrientationsData, + })); + + this.cache = {}; + } + + public loadData(extent: Extent, input: OISDataLoadInput): Promise { + const cache = this.getCache(input.crs); + + const key = this.requestToKey(extent); + + // TODO: + throw new Error('Method not implemented.'); + } + + private imageUrl(cameraId: string, panoId: string): string { + return this.url.replace('{cameraId}', cameraId).replace('{panoId}', panoId); + } + + // TODO: + private requestToKey(extent: Extent): unknown { + throw new Error('Method not implemented.'); + } + + private getCache(crs: string): Cache { + if (this.cache[crs] === undefined) { + this.cache[crs] = new Cache(); + } + return this.cache[crs]; + } + + public removeCache(crs: string): void { + delete this.cache[crs]; + } +} + +export default class OrientedImageSourceNode extends SourceNode { + constructor( + url: Dependency, + crs: Dependency, + orientationsUrl?: Dependency, + calibrationUrl?: Dependency, + ) { + super( + args => new OrientedImageSource(args as OISDescriptor), + url, + crs, + { + ...(orientationsUrl ? { orientationsUrl: [orientationsUrl, BuiltinType.String] } : {}), + ...(calibrationUrl ? { calibrationUrl: [calibrationUrl, BuiltinType.String] } : {}), + }, + ); + } +} diff --git a/src/Graph/Nodes/Source/Prelude.ts b/src/Graph/Nodes/Source/Prelude.ts new file mode 100644 index 0000000000..de04498892 --- /dev/null +++ b/src/Graph/Nodes/Source/Prelude.ts @@ -0,0 +1,7 @@ +import SourceNode from './SourceNode'; +import { TMSSourceNode } from './TMSSourceNode'; + +export default { + SourceNode, + TMSSourceNode, +}; diff --git a/src/Graph/Nodes/Source/SourceNode.ts b/src/Graph/Nodes/Source/SourceNode.ts new file mode 100644 index 0000000000..03dfacea55 --- /dev/null +++ b/src/Graph/Nodes/Source/SourceNode.ts @@ -0,0 +1,30 @@ +import { BuiltinType, Dependency, Graph, GraphNode, ProcessorNode, Source, Type } from 'Graph/Prelude'; + +export type BaseConstructorArgs = { url: string, crs: string }; + +export default abstract class SourceNode extends GraphNode { + private _constructor: (args: ConstructorArgs) => Source; + private _config: ConstructorArgs; + + public constructor( + constructor: (args: ConstructorArgs) => Source, + constructorArgs: ConstructorArgs, + ) { + super( + new Map(), + BuiltinType.Source, + true, + ); + + this._constructor = constructor; + this._config = constructorArgs; + } + + protected _apply(_graph?: Graph, frame: number = 0): void { + this._out.frame = frame; + + const start = Date.now(); + this.updateOutputs({ [SourceNode.defaultIoName]: this._constructor(this._config) }) + this._out.timeTaken = Date.now() - start; + } +} diff --git a/src/Graph/Nodes/Source/TMSSourceNode.ts b/src/Graph/Nodes/Source/TMSSourceNode.ts new file mode 100644 index 0000000000..b50bbb14f8 --- /dev/null +++ b/src/Graph/Nodes/Source/TMSSourceNode.ts @@ -0,0 +1,55 @@ +import Extent from 'Core/Geographic/Extent'; +import { BuiltinType, DumpDotNodeStyle, Source } from 'Graph/Types'; +import SourceNode from './SourceNode'; + +export type TMSSourceDescriptor = { + url: string, + crs: string, + format?: string, + extent?: Extent, + zoom?: { + min: number, + max: number, + }, + tileMatrixSetLimits?: Record, + tileMatrixCallback?: (zoomLevel: number) => string, +}; + +export class TMSSourceNode extends SourceNode { + protected extentSetLimits?: Record>; + + constructor(public descriptor: TMSSourceDescriptor) { + super((args) => new TMSSource(args), descriptor) + } + + public get nodeType(): string { + return "TMSSource"; + } + + public get dumpDotStyle(): DumpDotNodeStyle { + return { + label: name => name, + attrs: { + color: 'lightskyblue', + } + } + } +} + +export type TMSSourceInput = { + crs: string, + +} + +export class TMSSource implements Source { + public constructor(config: TMSSourceDescriptor) { } + + public loadData(extent: Extent, input: TMSSourceDescriptor): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/src/Graph/Nodes/SubGraphNode.ts b/src/Graph/Nodes/SubGraphNode.ts new file mode 100644 index 0000000000..22c7f03a82 --- /dev/null +++ b/src/Graph/Nodes/SubGraphNode.ts @@ -0,0 +1,123 @@ +import { Dependency, DumpDotNodeStyle, Graph, GraphInputNode, GraphNode, GraphOutputNode, SubGraph, Type } from '../Prelude'; + +export default class SubGraphNode extends GraphNode { + public graph: SubGraph; + private graphOutputs: Map; + + public constructor( + outerGraph: Graph, + graph: SubGraph, + out: { [name: string]: Dependency | [GraphNode, string] | [string, string] | GraphNode | string }, + ) { + const outputs = Object.entries(out) + .map(([name, dep]): [string, Dependency] => { + if (Array.isArray(dep)) { + const [nodeName, output] = dep; + const node = nodeName instanceof GraphNode ? nodeName : graph.get(nodeName); + if (node == undefined) { + throw new Error(`Node "${nodeName}" selected as output does not exist within the subgraph`); + } + + return [name, { node, output }]; + } else if (dep instanceof GraphNode) { + return [name, { node: dep, output: GraphNode.defaultIoName }]; + } else if (typeof dep == 'string') { + const node = graph.get(dep); + if (node == undefined) { + throw new Error(`Node "${dep}" selected as output does not exist within the subgraph`); + } + return [name, { node, output: GraphNode.defaultIoName }]; + } else { + return [name, dep]; + } + }) + .map(([name, dep]): [string, [Dependency, Type]] => [name, [dep, dep.node.outputs.get(dep.output)!.type]]); + + const missingOutputDeps = outputs + .filter(([_name, [dep, _ty]]) => graph.findGraphNode(dep.node) == undefined) + .map(([name, [_dep, ty]]) => `Output "${name}" (type: ${ty}) points to a node not within the subgraph`); + + if (missingOutputDeps.length > 0) { + throw new Error(missingOutputDeps.join('\n')); + } + + const outputTypeMap = new Map(outputs.map(([name, [_, ty]]) => [name, ty])); + super(new Map(), outputTypeMap); + + this.graph = graph; + this.graphOutputs = new Map(outputs.map(([name, [dep, ty]]) => { + const node = new GraphOutputNode(dep); + this.graph.outputs.set(name, node); + return [name, [{ node, output: SubGraphNode.defaultIoName }, ty]]; + })); + + for (const [_nodeName, node] of this.graph.nodes) { + // Replace dependencies outside the graph with graph inputs + for (const [depName, [dep, depType]] of node.inputs) { + if (dep != undefined) { + dep.node.outputs.get(dep.output)!.dependants.delete({ node, input: depName }); + if (Array.from(this.graph.nodes.values()).find(oNode => oNode == dep.node) == undefined) { + // Try to find an already created graph input for this dependency + const inputs = Array.from(this.graph.inputs); + const findInput = inputs.find(([name, _input]) => name == depName); + if (findInput != undefined) { + const [_name, input] = findInput; + node.inputs.set(depName, [{ node: input, output: GraphNode.defaultIoName }, depType]); + continue; + } + + // NOTE: only works for one level of nesting, we might need a resolve function but + // I'm not sure the case where it'd be needed will ever occur. + const newInput = new GraphInputNode([outerGraph.findGraphNode(dep.node)!.name, dep]); + const addedInput = this.graph.inputs.set(depName, newInput).get(depName)!; + node.inputs.set(depName, [{ node: addedInput, output: GraphNode.defaultIoName }, depType]); + } + } + } + } + } + + protected override _apply(_graph?: Graph, frame: number = 0): void { + const updates = Array.from(this.graphOutputs.entries()) + .map(([name, [dep, _ty]]) => [name, dep.node.getOutput(dep.output, this.graph, frame)]); + this.updateOutputs(Object.fromEntries(updates), frame); + } + + public get label(): string | undefined { + return this.graph.name; + } + + public override get nodeType(): string { + return SubGraphNode.name; + } + + public override get dumpDotStyle(): DumpDotNodeStyle { + return { + label: name => `${name}`, + attrs: { + style: 'filled', + fillcolor: 'whitesmoke', + color: 'lightgrey', + }, + }; + } + + public override dumpDot(name: string): string { + const { label, attrs } = this.dumpDotStyle; + const formattedAttrs = Object.entries(attrs).map(([name, value]: [string, string | object]) => { + if (typeof value == 'string') { + return `${name}=${value}`; + } else { + return `${name} [${Object.entries(value).map(([name, value]) => `${name}=${value}`).join(' ')}]`; + } + }); + const graphLabel = `\t\tlabel="${label(this.graph.name)}"`; + return [ + `subgraph "cluster_${label(name)}" {`, + graphLabel, + ...formattedAttrs, + this.graph.dumpDot(name), + '}', + ].join('\n'); + } +} diff --git a/src/Graph/Nodes/ViewNode.ts b/src/Graph/Nodes/ViewNode.ts new file mode 100644 index 0000000000..8e7d3d8e49 --- /dev/null +++ b/src/Graph/Nodes/ViewNode.ts @@ -0,0 +1,20 @@ +import { BuiltinType, Dependency, ProcessorNode, Type } from '../Prelude'; + +export default abstract class ViewNode extends ProcessorNode { + public constructor( + viewerDiv: Dependency, + generator: (frame: number, args: any) => void, + extraDependencies?: { [name: string]: [Dependency, Type] }, + ) { + super( + { viewerDiv: [viewerDiv, BuiltinType.HtmlDivElement], ...extraDependencies }, + new Map(Object.entries({ + view: BuiltinType.View, + renderer: BuiltinType.Renderer, + camera: BuiltinType.Camera, + })), + generator, + true, + ); + } +} diff --git a/src/Graph/Optimizations/Prelude.ts b/src/Graph/Optimizations/Prelude.ts new file mode 100644 index 0000000000..55fac59a38 --- /dev/null +++ b/src/Graph/Optimizations/Prelude.ts @@ -0,0 +1,5 @@ +import screenShaderMerge from './ScreenShaderMerge'; + +export default { + screenShaderMerge, +}; diff --git a/src/Graph/Optimizations/ScreenShaderMerge.ts b/src/Graph/Optimizations/ScreenShaderMerge.ts new file mode 100644 index 0000000000..2f0d87200f --- /dev/null +++ b/src/Graph/Optimizations/ScreenShaderMerge.ts @@ -0,0 +1,151 @@ +import { Graph, GraphNode, ScreenShaderNode, SubGraph } from '../Prelude'; + +const merged: Map = new Map(); + +// TODO: refactor into multiple functions for maintainability +export default { + pattern: Array(2).fill(ScreenShaderNode.name), + operation: (nodes: GraphNode[], graph: Graph) => { + const [child, parent] = nodes as ScreenShaderNode[]; + + const [cName, pName] = [graph.findNode(child), graph.findNode(parent)] + .map(n => n?.name ?? 'null'); + + const [cRenderer, pRenderer] = [child.inputs, parent.inputs] + .map(inputs => inputs.get('renderer')![0]!); + + if (cRenderer.node != pRenderer.node || cRenderer.output != pRenderer.output) { + throw new Error(`Different renderer for ${cName} and ${pName}`); + } + + if (graph.findDependants(parent).length != 1) { + throw new Error(`Parent ${pName} has multiple dependants`); + } + + const [cParts, pParts] = [child.fragmentShaderParts, parent.fragmentShaderParts]; + + const detectedOffsetSampling = /texture2D\((?!uTexture,vUv)[^)]*\)/.exec( + ((cParts.auxCode ?? '') + cParts.main).replaceAll(/\s/gm, ''), + ); + + if (detectedOffsetSampling != null) { + throw new Error( + `Child ${cName} samples input with an offset` + + `(only allowed for parent shaders when merging): ${detectedOffsetSampling[0]}`, + ); + } + + // Fail if parent is linked to a subgraph output + if (graph instanceof SubGraph) { + for (const [outputName, output] of graph.outputs.entries()) { + if (output.input[0]?.node == parent as GraphNode ?? false) { + throw new Error(`Parent ${pName} is linked to subgraph output ${outputName}`); + } + } + } + + const includes = new Set(); + for (const include of [...cParts.includes ?? [], ...pParts.includes ?? []]) { + includes.add(include); + } + cParts.includes = [...includes.values()]; + + const defines = new Map(); + for (const [key, value] of Object.entries(cParts.defines ?? {})) { + const pValue = pParts.defines?.[key]; + if (pValue != undefined && pValue != value) { + throw new Error(`Child ${cName} and parent ${pName} both define ${key}`); + } + defines.set(key, value); + } + for (const [key, value] of Object.entries(pParts.defines ?? {})) { + if (!defines.has(key)) { + defines.set(key, value); + } + } + + // Find and mangle duplicate uniform names + const replacements: [string, string][] = []; + + pParts.uniforms ??= {}; + + for (const [name, _value] of Object.entries(pParts.uniforms)) { + if (cParts.uniforms?.[name] != undefined) { + const val = pParts.uniforms[name]; + delete pParts.uniforms[name]; + pParts.uniforms[`parent_${name}`] = val; + + replacements.push([name, `parent_${name}`]); + } + } + + cParts.uniforms = { ...cParts.uniforms, ...pParts.uniforms }; + + cParts.auxCode ??= ''; + pParts.auxCode ??= ''; + + // Replace duplicate uniform names in parent code + for (const [name, replacement] of replacements) { + const match = new RegExp(`\\b${name}\\b`); + pParts.auxCode = pParts.auxCode.replaceAll(match, replacement); + } + + const mergedIds = merged.get(child.id); + const childMergedId = mergedIds != undefined ? `_${child.id}_${mergedIds.join('_')}` : `_${child.id}`; + if (mergedIds == undefined) { + merged.set(child.id, [parent.id]); + } else { + mergedIds.push(parent.id); + } + + cParts.auxCode = [ + `// ${pName}`, pParts.auxCode, + `vec4 _${parent.id}_shader(vec4 color) {`, + `\t${pParts.main.replace('\n', '\n\t')}`, + '}', + `// ${cName}`, cParts.auxCode, + `vec4 ${childMergedId}_shader(vec4 color) {`, + `\t${cParts.main.replace('\n', '\n\t')}`, + '}', + ].join('\n'); + cParts.main = [ + `return ${childMergedId}_shader(_${parent.id}_shader(color));`, + ].join('\n'); + + const fragmentShader = ScreenShaderNode.buildFragmentShader(cParts); + + child.material = ScreenShaderNode.buildMaterial(fragmentShader); + + child.deleteInput('renderer'); + child.deleteInput('target'); + const newInputs = Object.fromEntries(Array.from(parent.inputs.entries()).map(([name, [dep, ty]]) => [name, dep ?? ty])); + child.updateInputs(newInputs, true); + + // Delete dependant entries for parent's inputs + for (const [name, [dep, _ty]] of parent.inputs.entries()) { + if (dep == null) { continue; } + + const output = dep.node.outputs.get(dep.output); + if (output == undefined) { + throw new Error(`Output ${dep.output} of ${dep.node.nodeType}(${dep.node.id}) does not exist`); + } + + output.dependants.delete({ node: parent, input: name }); + } + + graph.remove(pName); + graph.remove(cName); + // graph.nodes.delete(pName); + // graph.nodes.delete(cName); + + if (mergedIds != undefined) { + graph.nodes.set(`_${parent.id}_${pName}${cName}`, child); + } else { + graph.nodes.set(`_${parent.id}_${pName}_${child.id}_${cName}`, child); + } + + return child; + }, +}; + + diff --git a/src/Graph/Optimizer.ts b/src/Graph/Optimizer.ts new file mode 100644 index 0000000000..a52e656d57 --- /dev/null +++ b/src/Graph/Optimizer.ts @@ -0,0 +1,66 @@ +import { Graph, GraphInputNode, GraphNode, GraphOptimization, opti } from './Prelude'; + +export default class Optimizer { + public static patterns: Map = new Map(Object.entries(opti)); + + public static optimize(graph: Graph, start: GraphNode | string, debug: boolean = false): Graph { + graph.validate(); + + const node = start instanceof GraphNode ? start : graph.nodes.get(start); + if (node == undefined) { + throw new Error(`Node "${start as string}" does not exist in the graph`); + } + + const path: GraphNode[] = []; + + function dfs(node: GraphNode): number { + path.push(node); + + if (debug) { + // eslint-disable-next-line no-console + console.info(`[${Optimizer.name}] path:`, path.map(n => graph.findNode(n)?.name ?? n.nodeType)); + } + + for (const [name, { pattern, operation }] of Optimizer.patterns) { + // Compare the last n nodes in the path to the pattern + const isDifferent = path.length < pattern.length || path.slice(-pattern.length) + .map((v, i) => v.nodeType == pattern[i]) + .includes(false); + if (!isDifferent) { + try { + if (debug) { + // eslint-disable-next-line no-console + console.info(`[${Optimizer.name}] Trying optimization ${name}`); + } + const newStart = operation(path.slice(-pattern.length), graph); + path.splice(-pattern.length); + dfs(newStart); + return pattern.length; + } catch (e) { + if (debug) { + // eslint-disable-next-line no-console + console.info(`[${Optimizer.name}] ${name}:`, e); + } + } + } + } + + for (const [_name, [dep, _ty]] of node.inputs) { + if (dep != null && !(dep.node instanceof GraphInputNode)) { + const retCount = dfs(dep.node); + if (retCount > 0) { + return retCount - 1; + } + } + } + + path.pop(); + + return 0; + } + + dfs(node); + + return graph; + } +} diff --git a/src/Graph/Prelude.ts b/src/Graph/Prelude.ts new file mode 100644 index 0000000000..e595c8d46c --- /dev/null +++ b/src/Graph/Prelude.ts @@ -0,0 +1,84 @@ +import Mappings from './Mappings'; + +import Graph from './Graph'; +import SubGraph from './SubGraph'; +import GraphNode from './Nodes/GraphNode'; +import InputNode from './Nodes/InputNode'; +import ProcessorNode from './Nodes/ProcessorNode'; +import ScreenShaderNode from './Nodes/ScreenShaderNode'; +import RenderViewNode from './Nodes/RenderViewNode'; +import SubGraphNode from './Nodes/SubGraphNode'; +import JunctionNode from './Nodes/JunctionNode'; +import ViewNode from './Nodes/ViewNode'; +import GraphInputNode from './Nodes/GraphInputNode'; +import GraphOutputNode from './Nodes/GraphOutputNode'; +import GlobeViewNode from './Nodes/GlobeViewNode'; +import PlanarViewNode from './Nodes/PlanarViewNode'; +import FieldGetterNode from './Nodes/FieldGetterNode'; +import DepthGetterNode from './Nodes/DepthGetterNode'; +import CameraDataNode from './Nodes/CameraDataNode'; +import SourceNode from './Nodes/Source/SourceNode'; +import sources from './Nodes/Source/Prelude'; + +import { + KernelType, + BuiltinType, + Type, + Dependency, + Dependant, + ColorStyle, + DumpDotNodeStyle, + DumpDotGlobalStyle, + GraphOptimization, + Source, +} from './Types'; +import opti from './Optimizations/Prelude'; + +import Optimizer from './Optimizer'; + +export { + Graph, + SubGraph, + + // Graph + GraphNode, + InputNode, + SubGraphNode, + JunctionNode, + GraphInputNode, + GraphOutputNode, + FieldGetterNode, + + // View + ViewNode, + GlobeViewNode, + PlanarViewNode, + + // Sources + sources, + + // Processors + ProcessorNode, + ScreenShaderNode, + RenderViewNode, + CameraDataNode, + DepthGetterNode, + + // Utils + Mappings, + KernelType, + BuiltinType, + Optimizer, + opti, +}; + +export type { + Type, + Dependency, + Dependant, + ColorStyle, + DumpDotNodeStyle, + DumpDotGlobalStyle, + GraphOptimization, + Source, +}; diff --git a/src/Graph/SubGraph.ts b/src/Graph/SubGraph.ts new file mode 100644 index 0000000000..cf39075418 --- /dev/null +++ b/src/Graph/SubGraph.ts @@ -0,0 +1,149 @@ +import { Graph, GraphInputNode, GraphNode, GraphOutputNode, JunctionNode, SubGraphNode, Mappings } from './Prelude'; + +export default class SubGraph extends Graph { + public inputs: Map = new Map(); + public outputs: Map = new Map(); + public name: string; + + private constructor(name: string, state: unknown) { + super(state); + this.name = name; + } + + public static from(graph: Graph, name: string): SubGraph { + // State is shared between subgraphs and parent graphs + const subGraph = new SubGraph(name, graph.state); + subGraph.nodes = graph.nodes; + subGraph.types = graph.types; + return subGraph; + } + + /** Find a node's entry in the inputs. O(n) time complexity. */ + public findInputNode(node: GraphNode): { name: string, node: GraphInputNode } | null { + for (const [name, oNode] of this.inputs.entries()) { + if (node == oNode) { + return { name, node: oNode }; + } + } + return null; + } + + public findOutputNode(node: GraphNode): { name: string, node: GraphOutputNode } | null { + for (const [name, oNode] of this.outputs.entries()) { + if (node == oNode) { + return { name, node: oNode }; + } + } + return null; + } + + public override findNode(node: GraphNode): { name: string, node: GraphNode } | null { + return super.findNode(node) ?? this.findInputNode(node); + } + + public override dumpDot(subGraphName: string): string { + const dump: string[] = []; + + if (this.nodes.size > 0) { + // Declare nodes + dump.push('\t{'); + for (const [name, node] of this.nodes) { + dump.push(`\t\t${node.dumpDot(name)}`); + } + for (const [name, input] of this.inputs) { + dump.push(`\t\t${input.dumpDot(`${subGraphName}.${name}`)}`); + } + for (const [name, input] of this.outputs) { + dump.push(`\t\t${input.dumpDot(`${subGraphName}->${name}`)}`); + } + dump.push('\t}'); + + // Declare edges + for (const [nodeName, destNode] of this.nodes) { + for (const [depName, [dep, depTy]] of destNode.inputs) { + if (dep == null) { continue; } + + // Lookup the node in the graph nodes and inputs + const nodeEntry = this.findNode(dep.node); + if (nodeEntry == undefined) { + throw new Error( + `Input "${depName}" of node "${nodeName}" is not part of the subgraph "${this.name}"`, + ); + } + + const { name: srcName, node: srcNode } = nodeEntry; + const colorStyle = Mappings.colorize(null, depTy); + const attrs = nodeEntry.node.dumpDotEdgeAttr(depTy, { + ...(srcNode instanceof JunctionNode ? { arrowtail: 'none' } : {}), + ...(destNode instanceof JunctionNode ? { arrowhead: 'none' } : {}), + ...colorStyle, + }); + const port = destNode instanceof JunctionNode ? '' : `:${depName}`; + + const sourceName = srcNode instanceof GraphInputNode ? `${subGraphName}.${srcName}` : srcName; + const sourcePort = srcNode instanceof GraphInputNode ? '' : `:"${dep.output}"`; + + if (dep.node instanceof SubGraphNode) { + dump.push(`\t"${sourceName}->${dep.output}":e -> "${nodeName}"${port}:w ${attrs};`); + } else { + dump.push(`\t"${sourceName}"${sourcePort}:e -> "${nodeName}"${port}:w ${attrs};`); + } + } + + if (destNode instanceof SubGraphNode) { + for (const [iName, iNode] of destNode.graph.inputs) { + const [dep, depTy] = iNode.input; + if (dep != undefined) { + const nodeEntry = this.findGraphNode(dep.node); + if (nodeEntry == undefined) { + throw new Error( + `Input "${iName}" of subgraph "${destNode.label}" is not part of the graph`, + ); + } + const { name: entryName, node: _entryNode } = nodeEntry; + const colorStyle = Mappings.colorize(null, depTy); + const attrs = nodeEntry.node.dumpDotEdgeAttr(depTy, { + arrowhead: 'none', + ...colorStyle, + }); + dump.push(`\t"${entryName}":e -> "${nodeName}.${iName}":w ${attrs}`); + } + } + } + } + + for (const [name, node] of this.outputs) { + const dep = node.input[0]!; + const { name: gName, node: gNode } = this.findGraphNode(dep.node)!; + const ty = gNode.outputs.get(dep.output)!.type; + + const colorStyle = Mappings.colorize(null, ty); + const attrs = gNode.dumpDotEdgeAttr(ty, { + arrowhead: 'none', + ...colorStyle, + }); + + dump.push(`\t"${gName}":"${dep.output}":e -> "${subGraphName}->${name}":w ${attrs};`); + } + } + + return dump.join('\n'); + } + + override dumpAdjacencyMatrix(): string { + const dump = [super.dumpAdjacencyMatrix()]; + + const nodeCount = GraphNode.totalNodesCreated; + const padding = nodeCount > 0 ? Math.floor(Math.log10(nodeCount)) + 1 : 1; + + for (const [name, node] of this.inputs.entries()) { + dump.push(`${node.id.toString().padStart(padding)}: (in) ${name}`); + } + + for (const [name, node] of this.outputs.entries()) { + dump.push(`${node.id.toString().padStart(padding)}: (out) ${name}`); + } + + return dump.join('\n'); + } +} diff --git a/src/Graph/Types.ts b/src/Graph/Types.ts new file mode 100644 index 0000000000..5f03746f73 --- /dev/null +++ b/src/Graph/Types.ts @@ -0,0 +1,86 @@ +// Stored in a separate file to avoid circular dependencies + +import { OrthographicCamera, PerspectiveCamera } from 'three'; +import { Extent } from 'Main'; +import Graph from './Graph'; +import GraphNode from './Nodes/GraphNode'; + +export type Type = string; +export type Dependency = { + node: GraphNode, + output: string +}; +export type Dependant = { + node: GraphNode, + input: string, +}; + +// TODO: Refactor type enum variants into separate types and discriminated union types +// can still have a `Custom`-like variant + +export enum KernelType { + Gaussian = 'Gaussian', + Box = 'Box', + /** Eye-Dome Lighting, intended for SSAO effects */ + EDL = 'EDL', +} + +export enum BuiltinType { + Any = 'Any', + + // Primitives + Number = 'Number', + String = 'String', + Float32Array = 'Float32Array', + + // iTowns types + Source = 'Source', + View = 'View', + Placement = 'Placement', + CRS = 'CRS', + + // Three.js + /// Types + Renderer = 'Renderer', + RenderTarget = 'RenderTarget', + Texture = 'Texture', + Camera = 'Camera', + CameraData = 'CameraData', + /// Primitives + Vector2 = 'Vector2', + Vector3 = 'Vector3', + Vector4 = 'Vector4', + + // Variants + KernelType = 'KernelType', + + // DOM + HtmlDivElement = 'HtmlDivElement', +} + +export type CameraLike = OrthographicCamera | PerspectiveCamera; + +export type ColorStyle = { + color?: string, + fillcolor?: string, +}; + +export interface DumpDotNodeStyle { + label: (name: string) => string; + attrs: { [key: string]: string | { [key: string]: string } }; +} + +export interface DumpDotGlobalStyle { + rankdir: string; + node: { [key: string]: string }; + edge: { [key: string]: string }; +} + +export type GraphOptimization = { + pattern: string[], + operation: (nodes: GraphNode[], graph: Graph) => GraphNode +}; + +export interface Source { + loadData(extent: Extent, input: Input): Promise; +} diff --git a/src/Layer/C3DTilesLayer.js b/src/Layer/C3DTilesLayer.js index ee48c5d62d..147cad31a8 100644 --- a/src/Layer/C3DTilesLayer.js +++ b/src/Layer/C3DTilesLayer.js @@ -4,7 +4,7 @@ import { init3dTilesLayer, pre3dTilesUpdate, process3dTilesNode } from 'Process/ import C3DTileset from 'Core/3DTiles/C3DTileset'; import C3DTExtensions from 'Core/3DTiles/C3DTExtensions'; import { PNTS_MODE, PNTS_SHAPE, PNTS_SIZE_MODE } from 'Renderer/PointsMaterial'; -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars import Style from 'Core/Style'; import C3DTFeature from 'Core/3DTiles/C3DTFeature'; import { optimizeGeometryGroups } from 'Utils/ThreeUtils'; diff --git a/src/Main.js b/src/Main.js index 4969bbff5c..3c33f7995b 100644 --- a/src/Main.js +++ b/src/Main.js @@ -94,7 +94,7 @@ export { default as LASParser } from 'Parser/LASParser'; export { default as ISGParser } from 'Parser/ISGParser'; export { default as GDFParser } from 'Parser/GDFParser'; export { default as GTXParser } from 'Parser/GTXParser'; -export { default as GLTFParser, enableDracoLoader, enableKtx2Loader, glTFLoader, legacyGLTFLoader } from 'Parser/GLTFParser'; +export { default as GLTFParser, enableDracoLoader, enableKtx2Loader, glTFLoader, legacyGLTFLoader } from 'Parser/GLTFParser'; // 3D Tiles classes and extensions // Exported to allow one to implement its own 3D Tiles extension which needs to @@ -107,3 +107,5 @@ export { default as C3DTExtensions } from './Core/3DTiles/C3DTExtensions'; export { C3DTilesTypes, C3DTilesBoundingVolumeTypes } from './Core/3DTiles/C3DTilesEnums'; export { default as C3DTBatchTableHierarchyExtension } from './Core/3DTiles/C3DTBatchTableHierarchyExtension'; export { process3dTilesNode, $3dTilesCulling, $3dTilesSubdivisionControl } from 'Process/3dTilesProcessing'; + +export * as graph from './Graph/Prelude'; diff --git a/src/Parser/GeoJsonParser.js b/src/Parser/GeoJsonParser.js index 3c31645c5b..b3453362ce 100644 --- a/src/Parser/GeoJsonParser.js +++ b/src/Parser/GeoJsonParser.js @@ -206,6 +206,8 @@ export default { options = deprecatedParsingOptionsToNewOne(options); options.in = options.in || {}; + console.log(options) + const out = options.out; const _in = options.in; diff --git a/src/Source/Source.js b/src/Source/Source.js index feadfa8c5c..af84eca42f 100644 --- a/src/Source/Source.js +++ b/src/Source/Source.js @@ -21,7 +21,7 @@ export const supportedParsers = new Map([ ['application/gdf', GDFParser.parse], ]); -const noCache = { getByArray: () => {}, setByArray: a => a, clear: () => {} }; +const noCache = { getByArray: () => { }, setByArray: a => a, clear: () => { } }; /** * @property {string} crs - data crs projection. @@ -52,7 +52,7 @@ class InformationsData { * @property {FeatureBuildingOptions|Layer} out - options indicates how the features should be built. */ // eslint-disable-next-line -class /* istanbul ignore next */ ParsingOptions {} +class /* istanbul ignore next */ ParsingOptions { } let uid = 0; diff --git a/src/glsl.d.ts b/src/glsl.d.ts new file mode 100644 index 0000000000..235c87e9e4 --- /dev/null +++ b/src/glsl.d.ts @@ -0,0 +1,4 @@ +declare module '*.glsl' { + const content: string; + export default content; +} diff --git a/tsconfig.json b/tsconfig.json index 0035eca157..721425f2ca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,10 +9,10 @@ "paths": { "*": [ "src/*" ] }, - "moduleResolution": "nodenext", + "moduleResolution": "Node", /* Emit */ "declaration": true, - "emitDeclarationOnly": true, + "emitDeclarationOnly": false, "outDir": "types", /* JavaScript Support */ "allowJs": true, diff --git a/webpack.config.cjs b/webpack.config.cjs index 1331d777c1..6dd1e73ae6 100644 --- a/webpack.config.cjs +++ b/webpack.config.cjs @@ -1,50 +1,24 @@ -const fs = require('fs'); const path = require('path'); const ESLintPlugin = require('eslint-webpack-plugin'); const mode = process.env.NODE_ENV; -const noInline = process.env.noInline; -const debugBuild = mode === 'development'; - -/* - configuring babel: - - when babel runs alone (for `test-unit` for instance), we let him deal with - ES6 modules, because node doesn't support them yet (planned for v10 lts). - - however, webpack also has ES6 module support and these 2 don't play well - together. When running webpack (either `build` or `start` script), we prefer - to rely on webpack loaders (much more powerful and gives more possibilities), - so let's disable modules for babel here. - - we also dynamise the value of __DEBUG__ according to the env var -*/ -// Note that we don't support .babelrc in parent folders -const babelrc = fs.readFileSync(path.resolve(__dirname, '.babelrc')); -const babelConf = JSON.parse(babelrc); - -babelConf.babelrc = false; // disabel babelrc reading, as we've just done it -const replacementPluginConf = babelConf.plugins.find(plugin => Array.isArray(plugin) && plugin[0] === 'minify-replace'); -replacementPluginConf[1].replacements.find(decl => decl.identifierName === '__DEBUG__').replacement.value = debugBuild; - -const include = [ - path.resolve(__dirname, 'src'), - path.resolve(__dirname, 'test'), - path.resolve(__dirname, 'utils'), -]; module.exports = () => { - const babelLoaderOptions = []; - if (!noInline) { - babelLoaderOptions.push('babel-inline-import-loader'); - } - babelLoaderOptions.push({ - loader: 'babel-loader', - options: babelConf, - }); + const include = [ + path.resolve(__dirname, 'src'), + path.resolve(__dirname, 'test'), + path.resolve(__dirname, 'utils'), + ]; return { mode, context: path.resolve(__dirname), resolve: { modules: [path.resolve(__dirname, 'src'), 'node_modules'], + extensions: ['.ts', '.js'], + extensionAlias: { + '.js': ['.ts', '.js'], + }, }, entry: { itowns: [ @@ -71,16 +45,21 @@ module.exports = () => { module: { rules: [ { - test: /\.js$/, + test: /\.[jt]s$/, include, - use: babelLoaderOptions, + use: { + loader: 'babel-loader', + options: { + rootMode: 'upward', + }, + }, }, ], }, plugins: [ - new ESLintPlugin({ - files: include, - }), + // new ESLintPlugin({ + // files: include, + // }), ], devServer: { devMiddleware: {