diff --git a/packages/@dcl/inspector/package-lock.json b/packages/@dcl/inspector/package-lock.json index e59fdb817..97e060c0c 100644 --- a/packages/@dcl/inspector/package-lock.json +++ b/packages/@dcl/inspector/package-lock.json @@ -17,11 +17,13 @@ "@dcl/ecs-math": "2.0.2", "@dcl/rpc": "^1.1.1", "@dcl/schemas": "^6.11.1", + "@reduxjs/toolkit": "^1.9.5", "@testing-library/react": "^14.0.0", "@types/node": "^18.11.18", "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@types/react-modal": "^3.16.0", + "@types/redux-saga": "^0.10.5", "@vscode/webview-ui-toolkit": "^1.2.2", "@well-known-components/pushable-channel": "^1.0.3", "classnames": "^2.3.2", @@ -37,7 +39,10 @@ "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-modal": "^3.16.1", + "react-redux": "^8.1.0", "react-resizable-panels": "^0.0.48", + "redux-saga": "^1.2.3", + "redux-saga-test-plan": "^4.0.6", "typescript": "^5.0.2" } }, @@ -510,6 +515,87 @@ "dev": true, "license": "MIT" }, + "node_modules/@redux-saga/core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", + "integrity": "sha512-U1JO6ncFBAklFTwoQ3mjAeQZ6QGutsJzwNBjgVLSWDpZTRhobUzuVDS1qH3SKGJD8fvqoaYOjp6XJ3gCmeZWgA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.6.3", + "@redux-saga/deferred": "^1.2.1", + "@redux-saga/delay-p": "^1.2.1", + "@redux-saga/is": "^1.1.3", + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1", + "redux": "^4.0.4", + "typescript-tuple": "^2.2.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/redux-saga" + } + }, + "node_modules/@redux-saga/deferred": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz", + "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==", + "dev": true + }, + "node_modules/@redux-saga/delay-p": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz", + "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==", + "dev": true, + "dependencies": { + "@redux-saga/symbols": "^1.1.3" + } + }, + "node_modules/@redux-saga/is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz", + "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==", + "dev": true, + "dependencies": { + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1" + } + }, + "node_modules/@redux-saga/symbols": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz", + "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==", + "dev": true + }, + "node_modules/@redux-saga/types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", + "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==", + "dev": true + }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "dev": true, + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@semantic-ui-react/event-stack": { "version": "3.1.3", "dev": true, @@ -646,6 +732,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "dev": true, @@ -723,6 +819,16 @@ "@types/react": "*" } }, + "node_modules/@types/redux-saga": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@types/redux-saga/-/redux-saga-0.10.5.tgz", + "integrity": "sha512-sqan7rs+PylBB5u2reL1hm+f0pDsZY1D1/o1vtgjrS4ErjxthK59zKCQyZhSLRcQDiOh3Pwp4fNQyn4A0CTdGA==", + "deprecated": "This is a stub types definition for redux-saga (https://github.com/redux-saga/redux-saga). redux-saga provides its own type definitions, so you don't need @types/redux-saga installed!", + "dev": true, + "dependencies": { + "redux-saga": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "dev": true, @@ -738,6 +844,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.24", "dev": true, @@ -1618,6 +1730,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fsm-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fsm-iterator/-/fsm-iterator-1.1.0.tgz", + "integrity": "sha512-hg47CNYdIGJ5m9WSKh617LHRdvJo4PiF0VkncFLwPVxKvBEQfSPd1qx/xLV/eSusewEu0C8eUFrsLsWlBgIcOg==", + "dev": true + }, "node_modules/function-bind": { "version": "1.1.1", "dev": true, @@ -1788,6 +1906,16 @@ "node": ">=0.10.0" } }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/impetus": { "version": "0.8.8", "dev": true, @@ -2225,6 +2353,18 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, "node_modules/long": { "version": "4.0.0", "dev": true, @@ -2668,6 +2808,55 @@ "react-dom": "^16.8.0 || ^17 || ^18" } }, + "node_modules/react-redux": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.0.tgz", + "integrity": "sha512-CtHZzAOxi7GQvTph4dVLWwZHAWUjV2kMEQtk50OrN8z3gKxpWg3Tz7JfDw32N3Rpd7fh02z73cF6yZkK467gbQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@reduxjs/toolkit": "^1 || ^2.0.0-beta.0", + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@reduxjs/toolkit": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/react-resizable-panels": { "version": "0.0.48", "dev": true, @@ -2825,6 +3014,40 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/redux-saga": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz", + "integrity": "sha512-HDe0wTR5nhd8Xr5xjGzoyTbdAw6rjy1GDplFt3JKtKN8/MnkQSRqK/n6aQQhpw5NI4ekDVOaW+w4sdxPBaCoTQ==", + "dev": true, + "dependencies": { + "@redux-saga/core": "^1.2.3" + } + }, + "node_modules/redux-saga-test-plan": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/redux-saga-test-plan/-/redux-saga-test-plan-4.0.6.tgz", + "integrity": "sha512-ESdbFoDWCeJ/EiFdUNSCGtA2CC9tnuvHDm6k06gVFa98EIeR2hpzFkGk9kJ1/hpMUnYFp+OOEEITIrZeDYBfFg==", + "dev": true, + "dependencies": { + "fsm-iterator": "^1.1.0", + "lodash.isequal": "^4.5.0", + "lodash.ismatch": "^4.4.0" + }, + "peerDependencies": { + "@redux-saga/is": "^1.0.1", + "@redux-saga/symbols": "^1.0.1", + "redux-saga": "^1.0.1" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "dev": true, + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "dev": true, @@ -2859,6 +3082,12 @@ "dev": true, "license": "MIT" }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "dev": true + }, "node_modules/right-now": { "version": "1.0.0", "dev": true, @@ -3166,6 +3395,30 @@ "node": ">=12.20" } }, + "node_modules/typescript-compare": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", + "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", + "dev": true, + "dependencies": { + "typescript-logic": "^0.0.0" + } + }, + "node_modules/typescript-logic": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", + "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==", + "dev": true + }, + "node_modules/typescript-tuple": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", + "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", + "dev": true, + "dependencies": { + "typescript-compare": "^0.0.2" + } + }, "node_modules/universalify": { "version": "0.2.0", "dev": true, @@ -3191,6 +3444,15 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "dev": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/victory-vendor": { "version": "36.6.8", "dev": true, @@ -3676,6 +3938,71 @@ "version": "4.0.2", "dev": true }, + "@redux-saga/core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", + "integrity": "sha512-U1JO6ncFBAklFTwoQ3mjAeQZ6QGutsJzwNBjgVLSWDpZTRhobUzuVDS1qH3SKGJD8fvqoaYOjp6XJ3gCmeZWgA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.6.3", + "@redux-saga/deferred": "^1.2.1", + "@redux-saga/delay-p": "^1.2.1", + "@redux-saga/is": "^1.1.3", + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1", + "redux": "^4.0.4", + "typescript-tuple": "^2.2.1" + } + }, + "@redux-saga/deferred": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz", + "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==", + "dev": true + }, + "@redux-saga/delay-p": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz", + "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==", + "dev": true, + "requires": { + "@redux-saga/symbols": "^1.1.3" + } + }, + "@redux-saga/is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz", + "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==", + "dev": true, + "requires": { + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1" + } + }, + "@redux-saga/symbols": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz", + "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==", + "dev": true + }, + "@redux-saga/types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", + "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==", + "dev": true + }, + "@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "dev": true, + "requires": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + } + }, "@semantic-ui-react/event-stack": { "version": "3.1.3", "dev": true, @@ -3778,6 +4105,16 @@ "version": "3.0.0", "dev": true }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "dev": true @@ -3844,6 +4181,15 @@ "@types/react": "*" } }, + "@types/redux-saga": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@types/redux-saga/-/redux-saga-0.10.5.tgz", + "integrity": "sha512-sqan7rs+PylBB5u2reL1hm+f0pDsZY1D1/o1vtgjrS4ErjxthK59zKCQyZhSLRcQDiOh3Pwp4fNQyn4A0CTdGA==", + "dev": true, + "requires": { + "redux-saga": "*" + } + }, "@types/scheduler": { "version": "0.16.3", "dev": true @@ -3856,6 +4202,12 @@ "version": "4.0.2", "dev": true }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "dev": true + }, "@types/yargs": { "version": "17.0.24", "dev": true, @@ -4426,6 +4778,12 @@ "version": "1.0.1", "dev": true }, + "fsm-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fsm-iterator/-/fsm-iterator-1.1.0.tgz", + "integrity": "sha512-hg47CNYdIGJ5m9WSKh617LHRdvJo4PiF0VkncFLwPVxKvBEQfSPd1qx/xLV/eSusewEu0C8eUFrsLsWlBgIcOg==", + "dev": true + }, "function-bind": { "version": "1.1.1", "dev": true @@ -4533,6 +4891,12 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, + "immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "dev": true + }, "impetus": { "version": "0.8.8", "dev": true @@ -4805,6 +5169,18 @@ "version": "4.17.21", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, "long": { "version": "4.0.0", "dev": true @@ -5093,6 +5469,28 @@ "warning": "^4.0.2" } }, + "react-redux": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.0.tgz", + "integrity": "sha512-CtHZzAOxi7GQvTph4dVLWwZHAWUjV2kMEQtk50OrN8z3gKxpWg3Tz7JfDw32N3Rpd7fh02z73cF6yZkK467gbQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, "react-resizable-panels": { "version": "0.0.48", "dev": true, @@ -5198,6 +5596,33 @@ "@babel/runtime": "^7.9.2" } }, + "redux-saga": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz", + "integrity": "sha512-HDe0wTR5nhd8Xr5xjGzoyTbdAw6rjy1GDplFt3JKtKN8/MnkQSRqK/n6aQQhpw5NI4ekDVOaW+w4sdxPBaCoTQ==", + "dev": true, + "requires": { + "@redux-saga/core": "^1.2.3" + } + }, + "redux-saga-test-plan": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/redux-saga-test-plan/-/redux-saga-test-plan-4.0.6.tgz", + "integrity": "sha512-ESdbFoDWCeJ/EiFdUNSCGtA2CC9tnuvHDm6k06gVFa98EIeR2hpzFkGk9kJ1/hpMUnYFp+OOEEITIrZeDYBfFg==", + "dev": true, + "requires": { + "fsm-iterator": "^1.1.0", + "lodash.isequal": "^4.5.0", + "lodash.ismatch": "^4.4.0" + } + }, + "redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "dev": true, + "requires": {} + }, "regenerator-runtime": { "version": "0.13.11", "dev": true @@ -5219,6 +5644,12 @@ "version": "1.0.0", "dev": true }, + "reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "dev": true + }, "right-now": { "version": "1.0.0", "dev": true @@ -5440,6 +5871,30 @@ "version": "5.0.3", "dev": true }, + "typescript-compare": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", + "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", + "dev": true, + "requires": { + "typescript-logic": "^0.0.0" + } + }, + "typescript-logic": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", + "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==", + "dev": true + }, + "typescript-tuple": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", + "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", + "dev": true, + "requires": { + "typescript-compare": "^0.0.2" + } + }, "universalify": { "version": "0.2.0", "dev": true @@ -5459,6 +5914,13 @@ "requires-port": "^1.0.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "dev": true, + "requires": {} + }, "victory-vendor": { "version": "36.6.8", "dev": true, diff --git a/packages/@dcl/inspector/package.json b/packages/@dcl/inspector/package.json index ab101e62d..9e65b49b6 100644 --- a/packages/@dcl/inspector/package.json +++ b/packages/@dcl/inspector/package.json @@ -11,11 +11,13 @@ "@dcl/ecs-math": "2.0.2", "@dcl/rpc": "^1.1.1", "@dcl/schemas": "^6.11.1", + "@reduxjs/toolkit": "^1.9.5", "@testing-library/react": "^14.0.0", "@types/node": "^18.11.18", "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@types/react-modal": "^3.16.0", + "@types/redux-saga": "^0.10.5", "@vscode/webview-ui-toolkit": "^1.2.2", "@well-known-components/pushable-channel": "^1.0.3", "classnames": "^2.3.2", @@ -31,7 +33,10 @@ "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-modal": "^3.16.1", + "react-redux": "^8.1.0", "react-resizable-panels": "^0.0.48", + "redux-saga": "^1.2.3", + "redux-saga-test-plan": "^4.0.6", "typescript": "^5.0.2" }, "files": [ diff --git a/packages/@dcl/inspector/src/components/App/App.tsx b/packages/@dcl/inspector/src/components/App/App.tsx index 308f75522..9d0260f8b 100644 --- a/packages/@dcl/inspector/src/components/App/App.tsx +++ b/packages/@dcl/inspector/src/components/App/App.tsx @@ -11,6 +11,8 @@ import './App.css' import Assets from '../Assets' import { useSelectedEntity } from '../../hooks/sdk/useSelectedEntity' import { useWindowSize } from '../../hooks/useWindowSize' +import { useAppSelector } from '../../redux/hooks' +import { getError } from '../../redux/data-layer' const App = () => { const selectedEntity = useSelectedEntity() @@ -19,9 +21,9 @@ const App = () => { // Footer's height is 48 pixels, so we need to calculate the percentage of the screen that it takes to pass as the minSize prop for the Panel const footerMin = (48 / height!) * 100 - + const disconnected = useAppSelector(getError) return ( -
+
diff --git a/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx b/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx index e0a3d4fe1..2b37e73a2 100644 --- a/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx +++ b/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx @@ -4,7 +4,6 @@ import { RxCross2 } from 'react-icons/rx' import { IoIosImage } from 'react-icons/io' import FileInput from '../FileInput' -import { withSdk } from '../../hoc/withSdk' import { Container } from '../Container' import { TextField } from '../EntityInspector/TextField' import { Block } from '../Block' @@ -16,6 +15,8 @@ import { GLTFValidation } from '@babylonjs/loaders' import './ImportAsset.css' import classNames from 'classnames' import { withAssetDir } from '../../lib/data-layer/host/fs-utils' +import { getDataLayer } from '../../redux/data-layer' +import { useAppSelector } from '../../redux/hooks' const ONE_MB_IN_BYTES = 1_048_576 const ONE_GB_IN_BYTES = ONE_MB_IN_BYTES * 1024 @@ -29,9 +30,9 @@ type ValidationError = string | null async function validateGltf(data: ArrayBuffer): Promise { let result try { - result = await GLTFValidation.ValidateAsync( - data, '', '', (uri) => { throw new Error('external references are not supported yet')} - ) + result = await GLTFValidation.ValidateAsync(data, '', '', (uri) => { + throw new Error('external references are not supported yet') + }) } catch (error) { return `Invalid GLTF: ${error}` } @@ -42,13 +43,13 @@ async function validateGltf(data: ArrayBuffer): Promise { Babylon's type declarations incorrectly state that result.issues.messages is an Array. In fact, it's an array of objects with useful properties. */ - type BabylonValidationIssue = {severity: number, code: string} + type BabylonValidationIssue = { severity: number; code: string } const severity = (issue as unknown as BabylonValidationIssue).severity /* Severity codes are Error (0), Warning (1), Information (2), Hint (3). https://github.com/KhronosGroup/glTF-Validator/blob/main/lib/src/errors.dart */ - if (severity == 0) { + if (severity === 0) { const message = (issue as unknown as BabylonValidationIssue).code return `Invalid GLTF: ${message}` } @@ -59,8 +60,10 @@ async function validateGltf(data: ArrayBuffer): Promise { } } -const ImportAsset = withSdk(({ sdk, onSave }) => { +const ImportAsset: React.FC = ({ onSave }) => { // TODO: multiple files + const dataLayer = useAppSelector(getDataLayer) + const [file, setFile] = useState() const [validationError, setValidationError] = useState(null) const [assetName, setAssetName] = useState('') @@ -92,7 +95,7 @@ const ImportAsset = withSdk(({ sdk, onSave }) => { } const gltfValidationError = await validateGltf(binary) - if (gltfValidationError != null) { + if (gltfValidationError !== null) { setValidationError(gltfValidationError) return } @@ -100,9 +103,9 @@ const ImportAsset = withSdk(({ sdk, onSave }) => { const content: Map = new Map() content.set(assetName + '.' + assetExtension, new Uint8Array(binary)) - const basePath = withAssetDir((await sdk!.dataLayer.getProjectData({})).path) + const basePath = withAssetDir((await dataLayer!.getProjectData({})).path) - await sdk!.dataLayer.importAsset({ + await dataLayer?.importAsset({ content, basePath, assetPackageName: '' @@ -124,10 +127,8 @@ const ImportAsset = withSdk(({ sdk, onSave }) => { const invalidName = !!assets.find((asset) => { const [packageName, otherAssetName] = removeBasePath(basePath, asset.path).split('/') - if (packageName === 'builder') - return false - else - return otherAssetName?.toLocaleLowerCase() === (assetName?.toLocaleLowerCase() + '.' + assetExtension) + if (packageName === 'builder') return false + else return otherAssetName?.toLocaleLowerCase() === assetName?.toLocaleLowerCase() + '.' + assetExtension }) return ( @@ -155,22 +156,18 @@ const ImportAsset = withSdk(({ sdk, onSave }) => {
- + - {validationError} + {validationError}
)}
) -}) +} export default ImportAsset diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectAssetExplorer.tsx b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectAssetExplorer.tsx index 7adbd93ec..a2e9d6cb5 100644 --- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectAssetExplorer.tsx +++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectAssetExplorer.tsx @@ -1,3 +1,5 @@ +import React from 'react' + import { useAssetTree } from '../../hooks/catalog/useAssetTree' import { useFileSystem } from '../../hooks/catalog/useFileSystem' import ProjectView from './ProjectView' @@ -6,10 +8,12 @@ import { AssetNodeFolder } from './types' import './ProjectAssetExplorer.css' -export function ProjectAssetExplorer() { +function ProjectAssetExplorer() { const [files] = useFileSystem() const { tree } = useAssetTree(files) const folders = tree.children.filter((item) => item.type === 'folder') as AssetNodeFolder[] return } + +export default React.memo(ProjectAssetExplorer) diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx index 4d85e066c..00ab258e9 100644 --- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx +++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx @@ -13,6 +13,8 @@ import { AssetNode, AssetNodeFolder } from './types' import { getFullNodePath } from './utils' import Search from '../Search' import { withAssetDir } from '../../lib/data-layer/host/fs-utils' +import { getDataLayer } from '../../redux/data-layer' +import { useAppSelector } from '../../redux/hooks' function noop() {} @@ -36,6 +38,8 @@ const FilesTree = Tree() function ProjectView({ folders }: Props) { const sdk = useSdk() + const dataLayer = useAppSelector(getDataLayer) + const [open, setOpen] = useState(new Set()) const [modal, setModal] = useState(undefined) const [lastSelected, setLastSelected] = useState() @@ -146,12 +150,11 @@ function ProjectView({ folders }: Props) { const removeAsset = useCallback( async (path: string, _: Entity[] = []) => { - if (!sdk) return - const { dataLayer } = sdk + if (!dataLayer) return await dataLayer.removeAsset({ path }) fileSystemEvent.emit('change') }, - [sdk] + [dataLayer] ) const handleConfirm = useCallback(async () => { @@ -251,7 +254,7 @@ function ProjectView({ folders }: Props) { - } - else - return ( - <> - - - - ) + return ( +
+ +
+ ) + } else + return ( + <> + + + + ) } export default React.memo(ProjectView) diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/index.ts b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/index.ts index 9386d5627..9912a840b 100644 --- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/index.ts +++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/index.ts @@ -1,2 +1,2 @@ -import { ProjectAssetExplorer } from './ProjectAssetExplorer' +import ProjectAssetExplorer from './ProjectAssetExplorer' export { ProjectAssetExplorer } diff --git a/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx b/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx index 302b233b5..d8c9bccbc 100644 --- a/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx +++ b/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx @@ -4,6 +4,9 @@ import { Vector3 } from '@babylonjs/core' import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' import { Dimmer } from 'decentraland-ui/dist/components/Dimmer/Dimmer' +import { withAssetDir } from '../../lib/data-layer/host/fs-utils' +import { useAppSelector } from '../../redux/hooks' +import { getDataLayer } from '../../redux/data-layer' import { BuilderAsset, DROP_TYPES, IDrop, ProjectAssetDrop, isDropType } from '../../lib/sdk/drag-drop' import { useRenderer } from '../../hooks/sdk/useRenderer' import { useSdk } from '../../hooks/sdk/useSdk' @@ -16,11 +19,10 @@ import { AssetNodeItem } from '../ProjectAssetExplorer/types' import { IAsset } from '../AssetsCatalog/types' import { getModel, isAsset } from '../EntityInspector/GltfInspector/utils' import { useIsMounted } from '../../hooks/useIsMounted' -import { Warnings } from './Warnings' +import { Warnings } from '../Warnings' import { CameraSpeed } from './CameraSpeed' import './Renderer.css' -import { withAssetDir } from '../../lib/data-layer/host/fs-utils' const fixedNumber = (val: number) => Math.round(val * 1e2) / 1e2 @@ -28,6 +30,8 @@ const Renderer: React.FC = () => { const canvasRef = React.useRef(null) useRenderer(() => canvasRef) const sdk = useSdk() + const dataLayer = useAppSelector(getDataLayer) + const [isLoading, setIsLoading] = useState(false) const isMounted = useIsMounted() const [files, init] = useFileSystem() @@ -84,7 +88,7 @@ const Renderer: React.FC = () => { }) ) - await sdk!.dataLayer.importAsset({ + await dataLayer?.importAsset({ content: new Map(Object.entries(fileContent)), basePath: withAssetDir(destFolder), assetPackageName diff --git a/packages/@dcl/inspector/src/components/Toolbar/Toolbar.tsx b/packages/@dcl/inspector/src/components/Toolbar/Toolbar.tsx index f7f4ba5e4..eb6b716c9 100644 --- a/packages/@dcl/inspector/src/components/Toolbar/Toolbar.tsx +++ b/packages/@dcl/inspector/src/components/Toolbar/Toolbar.tsx @@ -8,10 +8,13 @@ import { withSdk } from '../../hoc/withSdk' import { Gizmos } from './Gizmos' import { Preferences } from './Preferences' import { ToolbarButton } from './ToolbarButton' - import './Toolbar.css' +import { getDataLayer } from '../../redux/data-layer' +import { useAppSelector } from '../../redux/hooks' +import { DataLayerRpcClient } from '../../lib/data-layer/types' const Toolbar = withSdk(({ sdk }) => { + const dataLayer = useAppSelector(getDataLayer) const [save, isDirty] = useSave() const handleInspector = useCallback(() => { const { debugLayer } = sdk.scene @@ -23,14 +26,15 @@ const Toolbar = withSdk(({ sdk }) => { }, []) const handleUndoRedo = useCallback( - (fn: typeof sdk.dataLayer.undo) => async () => { + (fn?: DataLayerRpcClient['undo']) => async () => { + if (!fn) return const { type } = await fn({}) if (type === 'file') { fileSystemEvent.emit('change') } saveEvent.emit('change', true) }, - [] + [dataLayer] ) return ( @@ -38,10 +42,10 @@ const Toolbar = withSdk(({ sdk }) => { {isDirty ? : } - + - + diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx b/packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx similarity index 66% rename from packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx rename to packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx index 66ce35009..089de443e 100644 --- a/packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx +++ b/packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx @@ -1,11 +1,11 @@ import React from 'react' import { Warning } from '../Warning' -import { useGizmoAlignment } from '../../../../hooks/editor/useGizmoAlignment' -import { useSelectedEntity } from '../../../../hooks/sdk/useSelectedEntity' -import { ROOT } from '../../../../lib/sdk/tree' -import { withSdk } from '../../../../hoc/withSdk' -import { useComponentValue } from '../../../../hooks/sdk/useComponentValue' -import { GizmoType } from '../../../../lib/utils/gizmo' +import { useGizmoAlignment } from '../../../hooks/editor/useGizmoAlignment' +import { useSelectedEntity } from '../../../hooks/sdk/useSelectedEntity' +import { ROOT } from '../../../lib/sdk/tree' +import { withSdk } from '../../../hoc/withSdk' +import { useComponentValue } from '../../../hooks/sdk/useComponentValue' +import { GizmoType } from '../../../lib/utils/gizmo' const RotationGizmoLocalAlignmentDisabled: React.FC = withSdk(({ sdk }) => { const selectedEntity = useSelectedEntity() diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/index.ts b/packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/index.ts similarity index 100% rename from packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/index.ts rename to packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/index.ts diff --git a/packages/@dcl/inspector/src/components/Warnings/SocketConnection/SocketConnection.tsx b/packages/@dcl/inspector/src/components/Warnings/SocketConnection/SocketConnection.tsx new file mode 100644 index 000000000..6f4bd900d --- /dev/null +++ b/packages/@dcl/inspector/src/components/Warnings/SocketConnection/SocketConnection.tsx @@ -0,0 +1,18 @@ +import React from 'react' + +import { Warning } from '../Warning' +import { useAppSelector } from '../../../redux/hooks' +import { ErrorType, getError } from '../../../redux/data-layer' + +const mapError = { + [ErrorType.Disconnected]: 'Socket disconnected. Please refresh the page', + [ErrorType.Reconnecting]: 'Disconnected. Trying to reconnect...' +} + +const SocketConnection: React.FC = () => { + const error = useAppSelector(getError) + if (!error) return null + return +} + +export default React.memo(SocketConnection) diff --git a/packages/@dcl/inspector/src/components/Warnings/SocketConnection/index.ts b/packages/@dcl/inspector/src/components/Warnings/SocketConnection/index.ts new file mode 100644 index 000000000..6877a77c1 --- /dev/null +++ b/packages/@dcl/inspector/src/components/Warnings/SocketConnection/index.ts @@ -0,0 +1,2 @@ +import SocketConnection from './SocketConnection' +export { SocketConnection } diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/Warning.css b/packages/@dcl/inspector/src/components/Warnings/Warning/Warning.css similarity index 100% rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/Warning.css rename to packages/@dcl/inspector/src/components/Warnings/Warning/Warning.css diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/Warning.tsx b/packages/@dcl/inspector/src/components/Warnings/Warning/Warning.tsx similarity index 100% rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/Warning.tsx rename to packages/@dcl/inspector/src/components/Warnings/Warning/Warning.tsx diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/index.ts b/packages/@dcl/inspector/src/components/Warnings/Warning/index.ts similarity index 100% rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/index.ts rename to packages/@dcl/inspector/src/components/Warnings/Warning/index.ts diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.css b/packages/@dcl/inspector/src/components/Warnings/Warnings.css similarity index 100% rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.css rename to packages/@dcl/inspector/src/components/Warnings/Warnings.css diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.tsx b/packages/@dcl/inspector/src/components/Warnings/Warnings.tsx similarity index 79% rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.tsx rename to packages/@dcl/inspector/src/components/Warnings/Warnings.tsx index 86b57a481..0f6d157e0 100644 --- a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.tsx +++ b/packages/@dcl/inspector/src/components/Warnings/Warnings.tsx @@ -1,11 +1,15 @@ import React from 'react' + import { RotationGizmoLocalAlignmentDisabled } from './RotationGizmoLocalAlignmentDisabled' +import { SocketConnection } from './SocketConnection' + import './Warnings.css' const Warnings: React.FC = () => { return (
+
) } diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/index.ts b/packages/@dcl/inspector/src/components/Warnings/index.ts similarity index 100% rename from packages/@dcl/inspector/src/components/Renderer/Warnings/index.ts rename to packages/@dcl/inspector/src/components/Warnings/index.ts diff --git a/packages/@dcl/inspector/src/hooks/catalog/useFileSystem.ts b/packages/@dcl/inspector/src/hooks/catalog/useFileSystem.ts index 914ba9624..0d78addfd 100644 --- a/packages/@dcl/inspector/src/hooks/catalog/useFileSystem.ts +++ b/packages/@dcl/inspector/src/hooks/catalog/useFileSystem.ts @@ -1,8 +1,9 @@ import mitt from 'mitt' -import { useState } from 'react' +import { useEffect, useState } from 'react' -import { useSdk } from '../sdk/useSdk' import { AssetCatalogResponse } from '../../tooling-entrypoint' +import { useAppSelector } from '../../redux/hooks' +import { getDataLayer } from '../../redux/data-layer' type FileSystemEvent = { change: unknown } export const fileSystemEvent = mitt() @@ -14,15 +15,23 @@ export const removeBasePath = (basePath: string, path: string) => { /* istanbul ignore next */ export const useFileSystem = (): [AssetCatalogResponse, boolean] => { const [files, setFiles] = useState({ basePath: '', assets: [] }) + const dataLayer = useAppSelector(getDataLayer) + const [init, setInit] = useState(false) - useSdk(({ dataLayer }) => { - async function fetchFiles() { - const assets = await dataLayer.getAssetCatalog({}) - setFiles(assets) - } + + const fetchFiles = async () => { + if (!dataLayer) return + const assets = await dataLayer.getAssetCatalog({}) + setFiles(assets) + } + + useEffect(() => { fileSystemEvent.on('change', fetchFiles) + }, []) + + useEffect(() => { void fetchFiles().then(() => setInit(true)) - }) + }, [dataLayer]) return [files, init] } diff --git a/packages/@dcl/inspector/src/hooks/editor/useSave.ts b/packages/@dcl/inspector/src/hooks/editor/useSave.ts index 0b72b0e0b..93660dd67 100644 --- a/packages/@dcl/inspector/src/hooks/editor/useSave.ts +++ b/packages/@dcl/inspector/src/hooks/editor/useSave.ts @@ -1,19 +1,22 @@ import mitt from 'mitt' import { useCallback, useState } from 'react' +import { useAppSelector } from '../../redux/hooks' +import { getDataLayer } from '../../redux/data-layer' import { useSdk } from '../sdk/useSdk' type SaveEvent = { change: boolean } export const saveEvent = mitt() export const useSave = (): [() => Promise, boolean] => { + const dataLayer = useAppSelector(getDataLayer) const sdk = useSdk() const [isDirty, setIsDirty] = useState(false) const saveFn = useCallback(async () => { - await sdk?.dataLayer.save({}) + await dataLayer?.save({}) setIsDirty(false) - }, [sdk]) + }, [dataLayer]) saveEvent.on('change', (value: boolean) => { if (value && !sdk?.preferences.data.autosaveEnabled) setIsDirty(true) diff --git a/packages/@dcl/inspector/src/hooks/sdk/useSdkContext.ts b/packages/@dcl/inspector/src/hooks/sdk/useSdkContext.ts index f332d7208..0c47c24f7 100644 --- a/packages/@dcl/inspector/src/hooks/sdk/useSdkContext.ts +++ b/packages/@dcl/inspector/src/hooks/sdk/useSdkContext.ts @@ -1,22 +1,33 @@ import { useCallback, useEffect, useState } from 'react' import { createSdkContext, SdkContextValue } from '../../lib/sdk/context' import { useCatalog } from '../catalog/useCatalog' +import { useAppDispatch, useAppSelector } from '../../redux/hooks' +import { getDataLayer, connect as connectDataLayer } from '../../redux/data-layer' +import { addEngines } from '../../redux/sdk' /** * * @returns This hook is only meant to be used by the SdkProvider, use useSdk instead */ export const useSdkContext = () => { + const dataLayer = useAppSelector(getDataLayer) const [canvas, setCanvas] = useState(null) const [sdk, setSdk] = useState(null) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) const [catalog] = useCatalog() + const dispatch = useAppDispatch() + + useEffect(() => { + dispatch(connectDataLayer()) + }, [dispatch]) + useEffect(() => { - if (!catalog || !canvas || sdk || isLoading) return + if (!catalog || !canvas || sdk || isLoading || !dataLayer) return setIsLoading(true) - createSdkContext(canvas, catalog) + createSdkContext(dataLayer, canvas, catalog) .then((ctx) => { + dispatch(addEngines({ inspector: ctx.engine, babylon: ctx.sceneContext.engine })) setSdk(ctx) }) .catch((e) => { @@ -24,7 +35,7 @@ export const useSdkContext = () => { setError(e) }) .finally(() => setIsLoading(false)) - }, [catalog, canvas, sdk, isLoading]) + }, [catalog, canvas, sdk, isLoading, dispatch, dataLayer]) const renderer = useCallback( (ref: React.RefObject) => { diff --git a/packages/@dcl/inspector/src/index.tsx b/packages/@dcl/inspector/src/index.tsx index ec318cc44..df5343476 100644 --- a/packages/@dcl/inspector/src/index.tsx +++ b/packages/@dcl/inspector/src/index.tsx @@ -1,7 +1,9 @@ +import { Provider } from 'react-redux' import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' import ReactDOM from 'react-dom/client' +import { store } from './redux/store' import { App } from './components/App' import { SdkProvider } from './components/SdkProvider' @@ -17,8 +19,10 @@ globalThis.Buffer = MaybeBuffer const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( - - - + + + + + ) diff --git a/packages/@dcl/inspector/src/lib/babylon/decentraland/SceneContext.ts b/packages/@dcl/inspector/src/lib/babylon/decentraland/SceneContext.ts index 0d1f815c5..bba17cf42 100644 --- a/packages/@dcl/inspector/src/lib/babylon/decentraland/SceneContext.ts +++ b/packages/@dcl/inspector/src/lib/babylon/decentraland/SceneContext.ts @@ -16,10 +16,10 @@ import { putBillboardComponent } from './sdkComponents/billboard' import { putGltfContainerComponent } from './sdkComponents/gltf-container' import { putMeshRendererComponent } from './sdkComponents/mesh-renderer' import { putTransformComponent } from './sdkComponents/transform' -import { consumeAllMessagesInto } from '../../logic/consume-stream' import { putSceneComponent } from './editorComponents/scene' import { createOperations } from '../../sdk/operations' import { createGizmoManager } from './gizmo-manager' +import { store } from '../../../redux/store' export type LoadableScene = { readonly entity: Readonly> @@ -68,12 +68,7 @@ export class SceneContext { // this future is resolved when the scene is disposed readonly stopped = future() - constructor( - public babylon: BABYLON.Engine, - public scene: BABYLON.Scene, - public loadableScene: LoadableScene, - public dataLayer: DataLayerRpcClient - ) { + constructor(public babylon: BABYLON.Engine, public scene: BABYLON.Scene, public loadableScene: LoadableScene) { this.rootNode = new EcsEntity(0 as Entity, this.#weakThis, scene) Object.assign(globalThis, { babylon: this.engine }) } @@ -152,7 +147,9 @@ export class SceneContext { async getFile(src: string): Promise { if (!src) return null try { - const response = await this.dataLayer.getAssetData({ path: src }) + const { dataLayer } = store.getState().dataLayer + if (!dataLayer) return null + const response = await dataLayer.getAssetData({ path: src }) return response.data } catch (err) { console.error('Error fetching file ' + src, err) @@ -176,26 +173,6 @@ export class SceneContext { this.rootNode.parent = null this.rootNode.dispose() } - - async connectCrdtTransport(crdtStream: DataLayerRpcClient['crdtStream']) { - const outgoingMessages = new AsyncQueue((_, _action) => { - // console.log('SCENE QUEUE', action) - }) - const transport = this.addTransport(outgoingMessages) - const engine = this.engine - - async function onMessage(message: Uint8Array) { - if (message.byteLength) { - Array.from(serializeCrdtMessages('DataLayer>Babylon', message, engine)).forEach(($) => console.log($)) - } - transport.onmessage!(message) - await engine.update(1) - } - - consumeAllMessagesInto(crdtStream(outgoingMessages), onMessage, outgoingMessages.close).catch((e) => { - console.error('consumeAllMessagesInto failed: ', e) - }) - } } // an entity only exists if it has any component attached to it diff --git a/packages/@dcl/inspector/src/lib/data-layer/client/index.ts b/packages/@dcl/inspector/src/lib/data-layer/client/index.ts deleted file mode 100644 index 83cff66e3..000000000 --- a/packages/@dcl/inspector/src/lib/data-layer/client/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DataLayerRpcClient } from '../types' -import { feededFileSystem } from './feeded-local-fs' -import { createLocalDataLayerRpcClient } from './local-data-layer' -import { createWebSocketDataLayerRpcClient } from './ws-data-layer' - -const dataLayerWsByQueryParams = new URLSearchParams(window.location.search).get('ws') -const dataLayerWsByGlobalThis = ((globalThis as any).InspectorConfig?.dataLayerRpcWsUrl as string) || null - -const dataLayerWs: string | null = dataLayerWsByQueryParams || dataLayerWsByGlobalThis || null - -export async function createDataLayerClientRpc(): Promise { - if (!dataLayerWs) { - const fs = await feededFileSystem() - return createLocalDataLayerRpcClient(fs) - } else { - return createWebSocketDataLayerRpcClient(dataLayerWs) - } -} diff --git a/packages/@dcl/inspector/src/lib/data-layer/client/local-data-layer.ts b/packages/@dcl/inspector/src/lib/data-layer/client/local-data-layer.ts index 7c367d829..f1a9e5fa6 100644 --- a/packages/@dcl/inspector/src/lib/data-layer/client/local-data-layer.ts +++ b/packages/@dcl/inspector/src/lib/data-layer/client/local-data-layer.ts @@ -1,12 +1,14 @@ import { createDataLayerHost } from '../host' -import { DataLayerRpcClient, FileSystemInterface } from '../types' +import { DataLayerRpcClient } from '../types' +import { feededFileSystem } from './feeded-local-fs' /** * This RpcClient creates internally the DataLayer HOST, implementing its own file system interface and engine. * @param fs * @returns */ -export async function createLocalDataLayerRpcClient(fs: FileSystemInterface): Promise { +export async function createLocalDataLayerRpcClient(): Promise { + const fs = await feededFileSystem() const localDataLayerHost = await createDataLayerHost(fs) return localDataLayerHost.rpcMethods as DataLayerRpcClient } diff --git a/packages/@dcl/inspector/src/lib/data-layer/client/ws-data-layer.ts b/packages/@dcl/inspector/src/lib/data-layer/client/ws-data-layer.ts deleted file mode 100644 index 830316db1..000000000 --- a/packages/@dcl/inspector/src/lib/data-layer/client/ws-data-layer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createRpcClient } from '@dcl/rpc' -import { WebSocketTransport } from '@dcl/rpc/dist/transports/WebSocket' -import { DataLayerRpcClient } from '../types' -import * as codegen from '@dcl/rpc/dist/codegen' -import { DataServiceDefinition } from '../proto/gen/data-layer.gen' -import { IEngine } from '@dcl/ecs' - -export function createWebSocketDataLayerRpcClient(dataLayerWebSocketUrl: string): Promise { - // TODO: should be some persistent logic to reconnect in case of failure or disconnection? YES - return new Promise((resolve, _reject) => { - const ws = new WebSocket(dataLayerWebSocketUrl) - ws.onopen = async () => { - const clientTransport = WebSocketTransport(ws) - const client = await createRpcClient(clientTransport) - const clientPort = await client.createPort('scene-ctx') - const serviceClient: DataLayerRpcClient = codegen.loadService<{ engine: IEngine }, DataServiceDefinition>( - clientPort, - DataServiceDefinition - ) - resolve(serviceClient) - } - }) -} diff --git a/packages/@dcl/inspector/src/lib/data-layer/host/stream.ts b/packages/@dcl/inspector/src/lib/data-layer/host/stream.ts index 579491ae9..3590b71c5 100644 --- a/packages/@dcl/inspector/src/lib/data-layer/host/stream.ts +++ b/packages/@dcl/inspector/src/lib/data-layer/host/stream.ts @@ -40,8 +40,9 @@ export function stream( } // and lastly wire the new messages from the renderer engine - consumeAllMessagesInto(stream, processMessage, closeCallback).catch((err) => { - console.error('consumeAllMessagesInto failed: ', err) + consumeAllMessagesInto(stream, processMessage).catch((err) => { + console.error('x failed: ', err) + closeCallback() }) // Send initial message (engineSerialized) diff --git a/packages/@dcl/inspector/src/lib/logic/consume-stream.ts b/packages/@dcl/inspector/src/lib/logic/consume-stream.ts index 656798353..909734275 100644 --- a/packages/@dcl/inspector/src/lib/logic/consume-stream.ts +++ b/packages/@dcl/inspector/src/lib/logic/consume-stream.ts @@ -1,20 +1,9 @@ import { CrdtStreamMessage } from '../data-layer/proto/gen/data-layer.gen' -export async function consumeAllMessagesInto( - iter: AsyncIterable, - cb: (data: Uint8Array) => void, - onClose: () => void -) { - try { - for await (const it of iter) { - if (it.data.byteLength) { - cb(it.data) - } +export async function consumeAllMessagesInto(iter: AsyncIterable, cb: (data: Uint8Array) => void) { + for await (const it of iter) { + if (it.data.byteLength) { + cb(it.data) } - } catch (err) { - debugger - } finally { - onClose() } - debugger } diff --git a/packages/@dcl/inspector/src/lib/sdk/connect-stream.ts b/packages/@dcl/inspector/src/lib/sdk/connect-stream.ts new file mode 100644 index 000000000..51b045777 --- /dev/null +++ b/packages/@dcl/inspector/src/lib/sdk/connect-stream.ts @@ -0,0 +1,44 @@ +import { IEngine, Transport } from '@dcl/ecs' +import { AsyncQueue } from '@well-known-components/pushable-channel' + +import { CrdtStreamMessage } from '../data-layer/remote-data-layer' +import { DataLayerRpcClient } from '../data-layer/types' +import { consumeAllMessagesInto } from '../logic/consume-stream' +import { serializeCrdtMessages } from './crdt-logger' + +export function connectCrdtToEngine( + engine: IEngine, + dataLayerStream: DataLayerRpcClient['crdtStream'], + engineKey: string +) { + // + const outgoingMessagesStream = new AsyncQueue((_, _action) => {}) + + const transport: Transport = { + filter() { + return !outgoingMessagesStream.closed + }, + async send(message) { + if (outgoingMessagesStream.closed) return + outgoingMessagesStream.enqueue({ data: message }) + if (message.byteLength) { + Array.from(serializeCrdtMessages(`${engineKey}>Datalayer`, message, engine)).forEach(($) => console.log($)) + } + } + } + Object.assign(transport, { name: `${engineKey}TransportClient` }) + engine.addTransport(transport) + + function onMessage(message: Uint8Array) { + if (message.byteLength) { + Array.from(serializeCrdtMessages(`DataLayer>${engineKey}`, message, engine)).forEach(($) => console.log($)) + } + transport.onmessage!(message) + void engine.update(1) + } + + consumeAllMessagesInto(dataLayerStream(outgoingMessagesStream), onMessage).catch((e) => { + console.error(`${engineKey} consumeAllMessagesInto failed: `, e) + outgoingMessagesStream.close() + }) +} diff --git a/packages/@dcl/inspector/src/lib/sdk/context.ts b/packages/@dcl/inspector/src/lib/sdk/context.ts index ac7195ca3..e899f37e0 100644 --- a/packages/@dcl/inspector/src/lib/sdk/context.ts +++ b/packages/@dcl/inspector/src/lib/sdk/context.ts @@ -5,7 +5,6 @@ import { Emitter } from 'mitt' import { ITheme } from '../../components/AssetsCatalog' import { SceneContext } from '../babylon/decentraland/SceneContext' import { initRenderer } from '../babylon/setup/init' -import { createDataLayerClientRpc } from '../data-layer/client' import { EditorComponents, SdkComponents } from './components' import { getHardcodedLoadableScene } from './test-local-scene' import { createInspectorEngine } from './inspector-engine' @@ -27,17 +26,17 @@ export type SdkContextValue = { sceneContext: SceneContext events: Emitter dispose(): void - dataLayer: DataLayerRpcClient operations: ReturnType gizmos: Gizmos editorCamera: CameraManager preferences: InspectorPreferencesManager } -export async function createSdkContext(canvas: HTMLCanvasElement, catalog: ITheme[]): Promise { - // initialize DataLayer - const dataLayer = await createDataLayerClientRpc() - +export async function createSdkContext( + dataLayer: DataLayerRpcClient, + canvas: HTMLCanvasElement, + catalog: ITheme[] +): Promise { // fetch user preferences from the data layer const preferences = await dataLayer.getInspectorPreferences({}) @@ -51,16 +50,12 @@ export async function createSdkContext(canvas: HTMLCanvasElement, catalog: IThem getHardcodedLoadableScene( 'urn:decentraland:entity:bafkreid44xhavttoz4nznidmyj3rjnrgdza7v6l7kd46xdmleor5lmsxfm1', catalog - ), - dataLayer + ) ) ctx.rootNode.position.set(0, 0, 0) - // Connect babylon engine with dataLayer transport - void ctx.connectCrdtTransport(dataLayer.crdtStream) - // create inspector engine context and components - const { engine, components, events, dispose } = createInspectorEngine(dataLayer) + const { engine, components, events, dispose } = createInspectorEngine() // register some globals for debugging Object.assign(globalThis, { dataLayer, inspectorEngine: engine }) @@ -72,7 +67,6 @@ export async function createSdkContext(canvas: HTMLCanvasElement, catalog: IThem scene, sceneContext: ctx, dispose, - dataLayer, operations: createOperations(engine), gizmos: ctx.gizmos, editorCamera: renderer.editorCamera, diff --git a/packages/@dcl/inspector/src/lib/sdk/inspector-engine.ts b/packages/@dcl/inspector/src/lib/sdk/inspector-engine.ts index 679df1253..4f5f6ae03 100644 --- a/packages/@dcl/inspector/src/lib/sdk/inspector-engine.ts +++ b/packages/@dcl/inspector/src/lib/sdk/inspector-engine.ts @@ -1,17 +1,9 @@ -import { Transport } from '@dcl/ecs' -import { AsyncQueue } from '@well-known-components/pushable-channel' import mitt from 'mitt' -import { CrdtStreamMessage } from '../data-layer/proto/gen/data-layer.gen' -import { DataLayerRpcClient } from '../data-layer/types' -import { consumeAllMessagesInto } from '../logic/consume-stream' -import { serializeCrdtMessages } from './crdt-logger' import { SdkContextEvents, SdkContextValue } from './context' import { createEngineContext } from '../data-layer/host/utils/engine' -export function createInspectorEngine( - dataLayer: DataLayerRpcClient -): Omit< +export function createInspectorEngine(): Omit< SdkContextValue, 'scene' | 'sceneContext' | 'dataLayer' | 'operations' | 'gizmos' | 'editorCamera' | 'preferences' > { @@ -21,40 +13,8 @@ export function createInspectorEngine( events.emit('change', { entity, operation, component, value }) }) - // - const outgoingMessagesStream = new AsyncQueue((_, _action) => {}) - const transport: Transport = { - filter() { - return !outgoingMessagesStream.closed - }, - async send(message) { - if (outgoingMessagesStream.closed) return - outgoingMessagesStream.enqueue({ data: message }) - if (message.byteLength) { - Array.from(serializeCrdtMessages('Inspector>Datalayer', message, engine)).forEach(($) => console.log($)) - } - } - } - Object.assign(transport, { name: 'InspectorTransportClient' }) - engine.addTransport(transport) - - function onMessage(message: Uint8Array) { - if (message.byteLength) { - Array.from(serializeCrdtMessages('DataLayer>Inspector', message, engine)).forEach(($) => console.log($)) - } - transport.onmessage!(message) - void engine.update(1) - } - - consumeAllMessagesInto(dataLayer.crdtStream(outgoingMessagesStream), onMessage, outgoingMessagesStream.close).catch( - (e) => { - console.error('consumeAllMessagesInto failed: ', e) - } - ) - // - function dispose() { - outgoingMessagesStream.close() + // outgoingMessagesStream.close() events.emit('dispose') } return { diff --git a/packages/@dcl/inspector/src/redux/data-layer/index.ts b/packages/@dcl/inspector/src/redux/data-layer/index.ts new file mode 100644 index 000000000..e1b2cb71f --- /dev/null +++ b/packages/@dcl/inspector/src/redux/data-layer/index.ts @@ -0,0 +1,53 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { RootState } from '../../redux/store' +import { DataLayerRpcClient } from '../../lib/data-layer/types' + +export enum ErrorType { + Disconnected = 'disconnected', + Reconnecting = 'reconnecting' +} + +export interface DataLayerState { + dataLayer: DataLayerRpcClient | undefined + reconnectAttempts: number + error: ErrorType | undefined +} + +export const initialState: DataLayerState = { + dataLayer: undefined, + reconnectAttempts: 0, + error: undefined +} + +export const dataLayer = createSlice({ + name: 'data-layer', + initialState, + reducers: { + connect: (state) => { + state.reconnectAttempts++ + console.log('[WS] Connecting') + }, + reconnect: (state) => { + console.log('[WS] Reconnecting') + state.error = ErrorType.Reconnecting + state.dataLayer = undefined + }, + connected: (state, { payload }: PayloadAction<{ dataLayer: DataLayerState['dataLayer'] }>) => { + console.log('[WS] Connected') + state.dataLayer = payload.dataLayer + state.reconnectAttempts = 0 + state.error = undefined + }, + error: (state, { payload }: PayloadAction<{ error: ErrorType }>) => { + console.log('[WS] Error', payload.error) + state.error = payload.error + } + } +}) + +export const { connect, connected, reconnect, error } = dataLayer.actions +export const getError = (state: RootState) => state.dataLayer.error +export const getDataLayer = (state: RootState) => state.dataLayer.dataLayer +export const getDataLayerReconnectAttempts = (state: RootState) => state.dataLayer.reconnectAttempts + +export default dataLayer.reducer diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.spec.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.spec.ts new file mode 100644 index 000000000..df7324593 --- /dev/null +++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.spec.ts @@ -0,0 +1,119 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { expectSaga, testSaga } from 'redux-saga-test-plan' +import * as codegen from '@dcl/rpc/dist/codegen' + +import { connectSaga, createSocketChannel, createWebSocketConnection, getWsUrl } from './connect' +import { WebSocketTransport } from '@dcl/rpc/dist/transports/WebSocket' +import { RpcClient, RpcClientPort, Transport, createRpcClient } from '@dcl/rpc' +import { DataLayerRpcClient } from '../../../lib/data-layer/types' +import reducer, { connected, reconnect } from '..' +import { createLocalDataLayerRpcClient } from '../../../lib/data-layer/client/local-data-layer' +import { call } from 'redux-saga/effects' + +describe('WebSocket Connection Saga', () => { + it('Should create LOCAL data-layer if no ws url is provided', async () => { + const dataLayer = { boedo: 'casla' } as any as DataLayerRpcClient + + return expectSaga(connectSaga) + .withReducer(reducer) + .provide([ + [call(getWsUrl), undefined], + [call(createLocalDataLayerRpcClient), dataLayer] + ]) + .put(connected({ dataLayer })) + .hasFinalState({ + dataLayer, + error: undefined, + reconnectAttempts: 0 + }) + .run() + }) + + it('Should create remote data-layer with Ws', async () => { + const url = 'ws://boedo.com' + const ws = new MockWebSocket() + const channel = createSocketChannel(ws as any as WebSocket) + const clientTransport = {} as Transport + const client: RpcClient = { createPort: (_port: string) => {} } as RpcClient + const clientPort: RpcClientPort = {} as RpcClientPort + const dataLayer = { boedo: 'casla' } as any as DataLayerRpcClient + jest.spyOn(codegen, 'loadService').mockReturnValue(dataLayer as any) + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) + + testSaga(connectSaga) + .next() + .call(getWsUrl) + .next(url) + .call(createWebSocketConnection, url) + .next(ws) + .call(createSocketChannel, ws) + .next(channel) + .take(channel as any) + + // OPEN event. Connect data layer + .next({ type: 'WS_OPENED' }) + .call(WebSocketTransport, ws) + .next(clientTransport) + .call(createRpcClient, clientTransport) + .next(client) + .call(client.createPort, 'scene-ctx') + .next(clientPort) + .put(connected({ dataLayer })) + .next() + + // Error event. console.error (TODO: handle this) + .next({ type: 'WS_ERROR', error: 'some - error' }) + + // Break the connection. Should reconnect + .finish() + .put(reconnect()) + .next() + .isDone() + + // Error logic + expect(consoleSpy).toBeCalledWith('some - error') + }) +}) + +// Mock WebSocket + +class MockWebSocket { + listeners: { [key: string]: Function[] } = {} + + addEventListener(event: string, callback: Function) { + if (!this.listeners[event]) { + this.listeners[event] = [] + } + this.listeners[event].push(callback) + } + + removeEventListener(event: string, callback: Function) { + if (this.listeners[event]) { + this.listeners[event] = this.listeners[event].filter((cb) => cb !== callback) + } + } + + simulateOpen() { + if (this.listeners['open']) { + this.listeners['open'].forEach((callback) => callback()) + } + } + + simulateMessage(data: any) { + if (this.listeners['message']) { + this.listeners['message'].forEach((callback) => callback({ data })) + } + } + + simulateClose() { + if (this.listeners['close']) { + this.listeners['close'].forEach((callback) => callback()) + } + } + + simulateError() { + if (this.listeners['error']) { + this.listeners['error'].forEach((callback) => callback()) + } + } +} diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.ts new file mode 100644 index 000000000..d0c4cb719 --- /dev/null +++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.ts @@ -0,0 +1,80 @@ +import { IEngine } from '@dcl/ecs' +import { END, EventChannel, eventChannel } from 'redux-saga' +import * as codegen from '@dcl/rpc/dist/codegen' +import { call, put, take } from 'redux-saga/effects' +import { createRpcClient, RpcClient, RpcClientPort, Transport } from '@dcl/rpc' +import { WebSocketTransport } from '@dcl/rpc/dist/transports/WebSocket' + +import { connected, DataLayerState, reconnect } from '../' +import { createLocalDataLayerRpcClient } from '../../../lib/data-layer/client/local-data-layer' +import { DataServiceDefinition } from '../../..//lib/data-layer/proto/gen/data-layer.gen' +import { DataLayerRpcClient } from '../../../lib/data-layer/types' + +export function getWsUrl() { + const dataLayerWsByQueryParams = new URLSearchParams(window.location.search).get('ws') + const dataLayerWsByGlobalThis = ((globalThis as any).InspectorConfig?.dataLayerRpcWsUrl as string) || null + + return dataLayerWsByQueryParams || dataLayerWsByGlobalThis || null +} + +export function createWebSocketConnection(url: string): WebSocket { + return new WebSocket(url) +} + +export function createSocketChannel(socket: WebSocket): EventChannel { + return eventChannel((emit) => { + socket.addEventListener('close', () => { + emit(END) + }) + socket.addEventListener('error', (error) => { + emit({ type: 'WS_ERROR', error }) + }) + socket.addEventListener('open', () => { + emit({ type: 'WS_OPENED' }) + }) + return () => {} + }) +} + +export type WsActions = + | { + type: 'WS_OPENED' + } + | { + type: 'WS_ERROR' + error: unknown + } + +export function* connectSaga() { + const wsUrl: string | undefined = yield call(getWsUrl) + + if (!wsUrl) { + const dataLayer: DataLayerState['dataLayer'] = yield call(createLocalDataLayerRpcClient) + yield put(connected({ dataLayer })) + return + } + const ws: WebSocket = yield call(createWebSocketConnection, wsUrl) + const socketChannel: EventChannel = yield call(createSocketChannel, ws) + try { + while (true) { + const wsEvent: WsActions = yield take(socketChannel) + + if (wsEvent.type === 'WS_OPENED') { + const clientTransport: Transport = yield call(WebSocketTransport, ws) + const client: RpcClient = yield call(createRpcClient, clientTransport) + const clientPort: RpcClientPort = yield call(client.createPort, 'scene-ctx') + const dataLayer: DataLayerRpcClient = codegen.loadService<{ engine: IEngine }, DataServiceDefinition>( + clientPort, + DataServiceDefinition + ) + yield put(connected({ dataLayer })) + } else if (wsEvent.type === 'WS_ERROR') { + console.error(wsEvent.error) + } + } + } catch (error) { + console.log('[WS] Error', error) + } finally { + yield put(reconnect()) + } +} diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/index.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/index.ts new file mode 100644 index 000000000..96672d204 --- /dev/null +++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/index.ts @@ -0,0 +1,11 @@ +import { takeEvery } from 'redux-saga/effects' +import { connect, reconnect } from '..' +import { connectSaga } from './connect' +import { reconnectSaga } from './reconnect' + +export function* dataLayerSaga() { + yield takeEvery(connect.type, connectSaga) + yield takeEvery(reconnect.type, reconnectSaga) +} + +export default dataLayerSaga diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.spec.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.spec.ts new file mode 100644 index 000000000..852990059 --- /dev/null +++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.spec.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { expectSaga } from 'redux-saga-test-plan' +import reducer, { ErrorType, connect, error, initialState } from '..' + +import { MAX_RETRY_TIMES, reconnectSaga } from './reconnect' +import { combineReducers } from '@reduxjs/toolkit' + +describe('WebSocket Reconnection Saga', () => { + it('Should try to reconnect', async () => { + const provideDelay = ({ fn }, next) => (fn.name === 'delayP' ? null : next()) + + return expectSaga(reconnectSaga) + .withReducer(combineReducers({ dataLayer: reducer })) + .withState({ dataLayer: initialState }) + .provide([{ call: provideDelay }]) + .put(connect()) + .hasFinalState({ dataLayer: { ...initialState, reconnectAttempts: 1 } }) + .run() + }) + + it('Should set error if max attemps reached', async () => { + return expectSaga(reconnectSaga) + .withReducer(combineReducers({ dataLayer: reducer })) + .withState({ dataLayer: { ...initialState, reconnectAttempts: MAX_RETRY_TIMES } }) + .put(error({ error: ErrorType.Disconnected })) + .hasFinalState({ + dataLayer: { ...initialState, reconnectAttempts: MAX_RETRY_TIMES, error: ErrorType.Disconnected } + }) + .run() + }) +}) diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.ts new file mode 100644 index 000000000..4e2968b52 --- /dev/null +++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.ts @@ -0,0 +1,18 @@ +import { put, select, delay } from 'redux-saga/effects' + +import { ErrorType, connect, error, getDataLayerReconnectAttempts } from '../' + +const RECONNECT_TIMEOUT = 1000 +export const MAX_RETRY_TIMES = 6 + +export function* reconnectSaga() { + const reconnectAttempts: number = yield select(getDataLayerReconnectAttempts) + console.log(`[WS] Reconnecting for ${reconnectAttempts} time`) + + if (reconnectAttempts >= MAX_RETRY_TIMES) { + yield put(error({ error: ErrorType.Disconnected })) + return + } + yield delay(RECONNECT_TIMEOUT * reconnectAttempts) + yield put(connect()) +} diff --git a/packages/@dcl/inspector/src/redux/hooks.ts b/packages/@dcl/inspector/src/redux/hooks.ts new file mode 100644 index 000000000..4eabbe863 --- /dev/null +++ b/packages/@dcl/inspector/src/redux/hooks.ts @@ -0,0 +1,5 @@ +import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' +import type { RootState, AppDispatch } from './store' + +export const useAppDispatch: () => AppDispatch = useDispatch +export const useAppSelector: TypedUseSelectorHook = useSelector diff --git a/packages/@dcl/inspector/src/redux/root-saga.ts b/packages/@dcl/inspector/src/redux/root-saga.ts new file mode 100644 index 000000000..fe400e71c --- /dev/null +++ b/packages/@dcl/inspector/src/redux/root-saga.ts @@ -0,0 +1,7 @@ +import { all } from 'redux-saga/effects' +import { dataLayerSaga } from './data-layer/sagas' +import { sdkSagas } from './sdk/sagas' + +export default function* rootSaga() { + yield all([...dataLayerSaga(), ...sdkSagas()]) +} diff --git a/packages/@dcl/inspector/src/redux/sdk/index.ts b/packages/@dcl/inspector/src/redux/sdk/index.ts new file mode 100644 index 000000000..a1bcaffc4 --- /dev/null +++ b/packages/@dcl/inspector/src/redux/sdk/index.ts @@ -0,0 +1,32 @@ +import { IEngine } from '@dcl/ecs' +import { PayloadAction, createSlice } from '@reduxjs/toolkit' +import { RootState } from '../store' + +export interface EngineState { + inspectorEngine: IEngine | undefined + rendererEngine: IEngine | undefined +} + +export const initialState: EngineState = { + inspectorEngine: undefined, + rendererEngine: undefined +} + +export const sdkEngines = createSlice({ + name: 'sdk-engines', + initialState, + reducers: { + addEngines: (state, { payload }: PayloadAction<{ inspector: IEngine; babylon: IEngine }>) => { + state.inspectorEngine = payload.inspector + state.rendererEngine = payload.babylon + } + } +}) + +export const { addEngines } = sdkEngines.actions +export const getEngines = (state: RootState) => ({ + inspector: state.sdk.inspectorEngine, + renderer: state.sdk.rendererEngine +}) + +export default sdkEngines.reducer diff --git a/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.spec.ts b/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.spec.ts new file mode 100644 index 000000000..6db4218d4 --- /dev/null +++ b/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.spec.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { expectSaga } from 'redux-saga-test-plan' +import { combineReducers } from '@reduxjs/toolkit' +import { Engine } from '@dcl/ecs' + +import dataLayerReducer, { initialState as dataLayerState, getDataLayer } from '../../data-layer' +import sdkReducer, { getEngines, initialState as sdkState } from '../' +import { connectStream } from './connect-stream' +import * as connectStreamEngine from '../../../lib/sdk/connect-stream' + +describe('SDK Engines crdt stream', () => { + it('Should not connect crdt stream if there is no ws', async () => { + const spy = jest.spyOn(connectStreamEngine, 'connectCrdtToEngine') + await expectSaga(connectStream) + .withReducer(combineReducers({ dataLayer: dataLayerReducer, sdk: sdkReducer })) + .withState({ dataLayer: dataLayerState, sdk: sdkState }) + .select(getEngines) + .select(getDataLayer) + .run() + expect(spy).not.toBeCalled() + }) + + it('Should not connect crdt stream if there is no ws', async () => { + const spy = jest.spyOn(connectStreamEngine, 'connectCrdtToEngine').mockImplementation(() => {}) + const state = { + dataLayer: { + ...dataLayerState, + dataLayer: jest.fn() + }, + sdk: { + inspectorEngine: Engine(), + rendererEngine: Engine() + } + } + await expectSaga(connectStream) + .withReducer(combineReducers({ dataLayer: dataLayerReducer, sdk: sdkReducer })) + .withState(state) + .select(getEngines) + .select(getDataLayer) + .run() + expect(spy).toBeCalledTimes(2) + }) +}) diff --git a/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.ts b/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.ts new file mode 100644 index 000000000..974527f4d --- /dev/null +++ b/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.ts @@ -0,0 +1,15 @@ +import { call, select } from 'redux-saga/effects' + +import { getEngines } from '..' +import { connectCrdtToEngine } from '../../../lib/sdk/connect-stream' +import { getDataLayer } from '../../data-layer' + +export function* connectStream() { + const engines: ReturnType = yield select(getEngines) + const dataLayer: ReturnType = yield select(getDataLayer) + if (!dataLayer || !engines.inspector || !engines.renderer) return + + console.log('Data layer reconnected') + yield call(connectCrdtToEngine, engines.inspector, dataLayer.crdtStream, 'Inspector') + yield call(connectCrdtToEngine, engines.renderer, dataLayer.crdtStream, 'Renderer') +} diff --git a/packages/@dcl/inspector/src/redux/sdk/sagas/index.ts b/packages/@dcl/inspector/src/redux/sdk/sagas/index.ts new file mode 100644 index 000000000..6903737dc --- /dev/null +++ b/packages/@dcl/inspector/src/redux/sdk/sagas/index.ts @@ -0,0 +1,11 @@ +import { takeEvery } from 'redux-saga/effects' +import { addEngines } from '../' +import { connected } from '../../data-layer' +import { connectStream } from './connect-stream' + +export function* sdkSagas() { + yield takeEvery(connected.type, connectStream) + yield takeEvery(addEngines.type, connectStream) +} + +export default sdkSagas diff --git a/packages/@dcl/inspector/src/redux/store.ts b/packages/@dcl/inspector/src/redux/store.ts new file mode 100644 index 000000000..bb4077fce --- /dev/null +++ b/packages/@dcl/inspector/src/redux/store.ts @@ -0,0 +1,23 @@ +import { configureStore } from '@reduxjs/toolkit' +import createSagaMiddleware from 'redux-saga' + +import dataLayerReducer from './data-layer' +import sdkReducer from './sdk' +import sagas from './root-saga' + +const sagaMiddleware = createSagaMiddleware() + +export const store = configureStore({ + reducer: { + dataLayer: dataLayerReducer, + sdk: sdkReducer + }, + middleware: (getDefaultMiddleware) => { + return getDefaultMiddleware({ thunk: false, serializableCheck: false }).concat(sagaMiddleware) + } +}) + +sagaMiddleware.run(sagas) + +export type AppDispatch = typeof store.dispatch +export type RootState = ReturnType diff --git a/packages/@dcl/inspector/test/data-layer/utils.ts b/packages/@dcl/inspector/test/data-layer/utils.ts index 3732f6dfd..83c370dad 100644 --- a/packages/@dcl/inspector/test/data-layer/utils.ts +++ b/packages/@dcl/inspector/test/data-layer/utils.ts @@ -1,13 +1,14 @@ import * as BABYLON from '@babylonjs/core' import { IEngine } from '@dcl/ecs' + import { SceneContext, LoadableScene } from '../../src/lib/babylon/decentraland/SceneContext' import { SdkContextValue } from '../../src/lib/sdk/context' import { createInspectorEngine } from '../../src/lib/sdk/inspector-engine' import { createLocalDataLayerRpcClient } from '../../src/lib/data-layer/client/local-data-layer' -import { feededFileSystem } from '../../src/lib/data-layer/client/feeded-local-fs' import { DataLayerRpcClient } from '../../src/lib/data-layer/types' import { createOperations } from '../../src/lib/sdk/operations' import { getDefaultInspectorPreferences } from '../../src/lib/logic/preferences/types' +import { connectCrdtToEngine } from '../../src/lib/sdk/connect-stream' export function initTestEngine(loadableScene: Readonly) { let sceneCtx: SceneContext @@ -18,8 +19,7 @@ export function initTestEngine(loadableScene: Readonly) { > beforeAll(async () => { - const fs = await feededFileSystem({}) - dataLayer = await createLocalDataLayerRpcClient(fs) + dataLayer = await createLocalDataLayerRpcClient() // Enable autosave to improve test coverage await dataLayer.setInspectorPreferences({ ...getDefaultInspectorPreferences(), @@ -36,9 +36,9 @@ export function initTestEngine(loadableScene: Readonly) { const scene = new BABYLON.Scene(engine) sceneCtx = new SceneContext(engine, scene, loadableScene, dataLayer) - inspector = createInspectorEngine(dataLayer) - void sceneCtx.connectCrdtTransport(dataLayer.crdtStream) + connectCrdtToEngine(sceneCtx.engine, dataLayer.crdtStream, 'renderer') + connectCrdtToEngine(inspector.engine, dataLayer.crdtStream, 'inspector') }) afterAll(() => { diff --git a/packages/@dcl/sdk-commands/package-lock.json b/packages/@dcl/sdk-commands/package-lock.json index 016761fe8..59b80a515 100644 --- a/packages/@dcl/sdk-commands/package-lock.json +++ b/packages/@dcl/sdk-commands/package-lock.json @@ -63,11 +63,13 @@ "@dcl/ecs-math": "2.0.2", "@dcl/rpc": "^1.1.1", "@dcl/schemas": "^6.11.1", + "@reduxjs/toolkit": "^1.9.5", "@testing-library/react": "^14.0.0", "@types/node": "^18.11.18", "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@types/react-modal": "^3.16.0", + "@types/redux-saga": "^0.10.5", "@vscode/webview-ui-toolkit": "^1.2.2", "@well-known-components/pushable-channel": "^1.0.3", "classnames": "^2.3.2", @@ -83,7 +85,10 @@ "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-modal": "^3.16.1", + "react-redux": "^8.1.0", "react-resizable-panels": "^0.0.48", + "redux-saga": "^1.2.3", + "redux-saga-test-plan": "^4.0.6", "typescript": "^5.0.2" } }, @@ -2425,11 +2430,13 @@ "@dcl/ecs-math": "2.0.2", "@dcl/rpc": "^1.1.1", "@dcl/schemas": "^6.11.1", + "@reduxjs/toolkit": "^1.9.5", "@testing-library/react": "^14.0.0", "@types/node": "^18.11.18", "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@types/react-modal": "^3.16.0", + "@types/redux-saga": "^0.10.5", "@vscode/webview-ui-toolkit": "^1.2.2", "@well-known-components/pushable-channel": "^1.0.3", "classnames": "^2.3.2", @@ -2445,7 +2452,10 @@ "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-modal": "^3.16.1", + "react-redux": "^8.1.0", "react-resizable-panels": "^0.0.48", + "redux-saga": "^1.2.3", + "redux-saga-test-plan": "^4.0.6", "typescript": "^5.0.2" } }, diff --git a/test/snapshots/development-bundles/static-scene.test.ts.crdt b/test/snapshots/development-bundles/static-scene.test.ts.crdt index a93f9fa3c..bf7e08cc8 100644 --- a/test/snapshots/development-bundles/static-scene.test.ts.crdt +++ b/test/snapshots/development-bundles/static-scene.test.ts.crdt @@ -10,9 +10,9 @@ EVAL test/snapshots/development-bundles/static-scene.test.js REQUIRE: ~system/Testing REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 31k - MALLOC_COUNT = 9983 - ALIVE_OBJS_DELTA ~= 1.85k + OPCODES ~= 35k + MALLOC_COUNT = 10117 + ALIVE_OBJS_DELTA ~= 1.89k CALL onStart() main.crdt: PUT_COMPONENT e=0x200 c=1 t=0 data={"position":{"x":5.880000114440918,"y":2.7916901111602783,"z":7.380000114440918},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":1,"y":1,"z":1},"parent":0} main.crdt: PUT_COMPONENT e=0x202 c=1 t=0 data={"position":{"x":4,"y":0.800000011920929,"z":8},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":1,"y":1,"z":1},"parent":0} @@ -56,4 +56,4 @@ CALL onUpdate(0.1) OPCODES ~= 2k MALLOC_COUNT = -3 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 984.26k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 987.25k bytes \ No newline at end of file diff --git a/test/snapshots/development-bundles/testing-fw.test.ts.crdt b/test/snapshots/development-bundles/testing-fw.test.ts.crdt index 002b1e84d..127396c3f 100644 --- a/test/snapshots/development-bundles/testing-fw.test.ts.crdt +++ b/test/snapshots/development-bundles/testing-fw.test.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=375.4k bytes +SCENE_COMPILED_JS_SIZE_PROD=375.5k bytes THE BUNDLE HAS SOURCEMAPS (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k @@ -10,9 +10,9 @@ EVAL test/snapshots/development-bundles/testing-fw.test.js REQUIRE: ~system/Testing REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 32k - MALLOC_COUNT = 10096 - ALIVE_OBJS_DELTA ~= 1.89k + OPCODES ~= 36k + MALLOC_COUNT = 10230 + ALIVE_OBJS_DELTA ~= 1.93k CALL onStart() LOG: ["Adding one to position.y=0"] Renderer: PUT_COMPONENT e=0x0 c=1 t=1 data={"position":{"x":1,"y":0,"z":0},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":9,"y":9,"z":9},"parent":0} @@ -64,4 +64,4 @@ CALL onUpdate(0.1) OPCODES ~= 4k MALLOC_COUNT = -40 ALIVE_OBJS_DELTA ~= -0.01k - MEMORY_USAGE_COUNT ~= 984.29k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 987.27k bytes \ No newline at end of file diff --git a/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt b/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt index 736e113ef..b804f21f7 100644 --- a/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt +++ b/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=375.4k bytes +SCENE_COMPILED_JS_SIZE_PROD=375.5k bytes THE BUNDLE HAS SOURCEMAPS (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k @@ -10,9 +10,9 @@ EVAL test/snapshots/development-bundles/two-way-crdt.test.js REQUIRE: ~system/Testing REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 32k - MALLOC_COUNT = 10096 - ALIVE_OBJS_DELTA ~= 1.89k + OPCODES ~= 36k + MALLOC_COUNT = 10230 + ALIVE_OBJS_DELTA ~= 1.93k CALL onStart() LOG: ["Adding one to position.y=0"] Renderer: PUT_COMPONENT e=0x0 c=1 t=1 data={"position":{"x":1,"y":0,"z":0},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":9,"y":9,"z":9},"parent":0} @@ -64,4 +64,4 @@ CALL onUpdate(0.1) OPCODES ~= 4k MALLOC_COUNT = -40 ALIVE_OBJS_DELTA ~= -0.01k - MEMORY_USAGE_COUNT ~= 984.29k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 987.28k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/append-value-crdt.ts.crdt b/test/snapshots/production-bundles/append-value-crdt.ts.crdt index e8f667769..4c77d844d 100644 --- a/test/snapshots/production-bundles/append-value-crdt.ts.crdt +++ b/test/snapshots/production-bundles/append-value-crdt.ts.crdt @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/append-value-crdt.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 33k - MALLOC_COUNT = 8902 - ALIVE_OBJS_DELTA ~= 1.90k + OPCODES ~= 37k + MALLOC_COUNT = 9036 + ALIVE_OBJS_DELTA ~= 1.94k CALL onStart() Renderer: APPEND_VALUE e=0x200 c=1063 t=0 data={"button":0,"hit":{"position":{"x":1,"y":2,"z":3},"globalOrigin":{"x":1,"y":2,"z":3},"direction":{"x":1,"y":2,"z":3},"normalHit":{"x":1,"y":2,"z":3},"length":10,"meshName":"mesh","entityId":512},"state":1,"timestamp":1,"analog":5,"tickNumber":0} OPCODES ~= 8k @@ -55,4 +55,4 @@ CALL onUpdate(0.1) OPCODES ~= 13k MALLOC_COUNT = 31 ALIVE_OBJS_DELTA ~= 0.01k - MEMORY_USAGE_COUNT ~= 727.43k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 730.42k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/billboard.ts.crdt b/test/snapshots/production-bundles/billboard.ts.crdt index 535cf3227..17b882b90 100644 --- a/test/snapshots/production-bundles/billboard.ts.crdt +++ b/test/snapshots/production-bundles/billboard.ts.crdt @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/billboard.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 43k - MALLOC_COUNT = 11203 - ALIVE_OBJS_DELTA ~= 2.35k + OPCODES ~= 47k + MALLOC_COUNT = 11335 + ALIVE_OBJS_DELTA ~= 2.39k CALL onStart() OPCODES ~= 0k MALLOC_COUNT = 4 @@ -77,4 +77,4 @@ CALL onUpdate(0.1) OPCODES ~= 8k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 878.46k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 881.34k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/cube-deleted.ts.crdt b/test/snapshots/production-bundles/cube-deleted.ts.crdt index 90b882904..96268d864 100644 --- a/test/snapshots/production-bundles/cube-deleted.ts.crdt +++ b/test/snapshots/production-bundles/cube-deleted.ts.crdt @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/cube-deleted.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 32k - MALLOC_COUNT = 8435 - ALIVE_OBJS_DELTA ~= 1.78k + OPCODES ~= 36k + MALLOC_COUNT = 8569 + ALIVE_OBJS_DELTA ~= 1.81k CALL onStart() OPCODES ~= 0k MALLOC_COUNT = 6 @@ -42,4 +42,4 @@ CALL onUpdate(0.1) OPCODES ~= 3k MALLOC_COUNT = 1 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 700.84k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 703.82k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/cube.ts.crdt b/test/snapshots/production-bundles/cube.ts.crdt index f5e1482c8..e390d2ee5 100644 --- a/test/snapshots/production-bundles/cube.ts.crdt +++ b/test/snapshots/production-bundles/cube.ts.crdt @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/cube.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 32k - MALLOC_COUNT = 8274 - ALIVE_OBJS_DELTA ~= 1.74k + OPCODES ~= 36k + MALLOC_COUNT = 8408 + ALIVE_OBJS_DELTA ~= 1.78k CALL onStart() OPCODES ~= 0k MALLOC_COUNT = 8 @@ -32,4 +32,4 @@ CALL onUpdate(0.1) OPCODES ~= 1k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 685.34k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 688.32k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/cubes.ts.crdt b/test/snapshots/production-bundles/cubes.ts.crdt index dd22cd968..5ce7f7f5b 100644 --- a/test/snapshots/production-bundles/cubes.ts.crdt +++ b/test/snapshots/production-bundles/cubes.ts.crdt @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/cubes.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 82k - MALLOC_COUNT = 14653 - ALIVE_OBJS_DELTA ~= 3.67k + OPCODES ~= 86k + MALLOC_COUNT = 14787 + ALIVE_OBJS_DELTA ~= 3.70k CALL onStart() OPCODES ~= 0k MALLOC_COUNT = 6 @@ -1652,4 +1652,4 @@ CALL onUpdate(0.1) OPCODES ~= 636k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 1016.60k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1019.54k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/pointer-events.ts.crdt b/test/snapshots/production-bundles/pointer-events.ts.crdt index 292b53cad..c7a0536d0 100644 --- a/test/snapshots/production-bundles/pointer-events.ts.crdt +++ b/test/snapshots/production-bundles/pointer-events.ts.crdt @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/pointer-events.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 33k - MALLOC_COUNT = 8776 - ALIVE_OBJS_DELTA ~= 1.87k + OPCODES ~= 37k + MALLOC_COUNT = 8910 + ALIVE_OBJS_DELTA ~= 1.91k CALL onStart() OPCODES ~= 0k MALLOC_COUNT = 8 @@ -28,7 +28,7 @@ CALL onUpdate(0) Scene: PUT_COMPONENT e=0x200 c=1019 t=1 data={"mesh":{"$case":"box","box":{}}} Scene: PUT_COMPONENT e=0x201 c=1019 t=1 data={"mesh":{"$case":"box","box":{}}} Scene: PUT_COMPONENT e=0x202 c=1019 t=1 data={"mesh":{"$case":"box","box":{}}} - OPCODES ~= 20k + OPCODES ~= 21k MALLOC_COUNT = 35 ALIVE_OBJS_DELTA ~= 0.01k CALL onUpdate(0.1) @@ -43,4 +43,4 @@ CALL onUpdate(0.1) OPCODES ~= 1k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 709.87k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 712.87k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/schema-components.ts.crdt b/test/snapshots/production-bundles/schema-components.ts.crdt index 0021bb9d4..05a649762 100644 --- a/test/snapshots/production-bundles/schema-components.ts.crdt +++ b/test/snapshots/production-bundles/schema-components.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=162.5k bytes +SCENE_COMPILED_JS_SIZE_PROD=162.6k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/schema-components.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 36k - MALLOC_COUNT = 8393 - ALIVE_OBJS_DELTA ~= 1.77k + OPCODES ~= 40k + MALLOC_COUNT = 8524 + ALIVE_OBJS_DELTA ~= 1.81k CALL onStart() OPCODES ~= 0k MALLOC_COUNT = 8 @@ -31,4 +31,4 @@ CALL onUpdate(0.1) OPCODES ~= 1k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 687.70k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 690.54k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/ui.ts.crdt b/test/snapshots/production-bundles/ui.ts.crdt index e4f315b45..6360589a9 100644 --- a/test/snapshots/production-bundles/ui.ts.crdt +++ b/test/snapshots/production-bundles/ui.ts.crdt @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/ui.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 42k - MALLOC_COUNT = 16026 - ALIVE_OBJS_DELTA ~= 3.04k + OPCODES ~= 46k + MALLOC_COUNT = 16160 + ALIVE_OBJS_DELTA ~= 3.07k CALL onStart() OPCODES ~= 0k MALLOC_COUNT = 6 @@ -65,4 +65,4 @@ CALL onUpdate(0.1) OPCODES ~= 61k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 1508.39k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1511.33k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/with-main-function.ts.crdt b/test/snapshots/production-bundles/with-main-function.ts.crdt index 829a6c4c4..fa142c536 100644 --- a/test/snapshots/production-bundles/with-main-function.ts.crdt +++ b/test/snapshots/production-bundles/with-main-function.ts.crdt @@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/with-main-function.js REQUIRE: long REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi - OPCODES ~= 33k - MALLOC_COUNT = 8527 - ALIVE_OBJS_DELTA ~= 1.80k + OPCODES ~= 37k + MALLOC_COUNT = 8661 + ALIVE_OBJS_DELTA ~= 1.84k CALL onStart() OPCODES ~= 0k MALLOC_COUNT = 6 @@ -18,7 +18,7 @@ CALL onStart() CALL onUpdate(0) Scene: PUT_COMPONENT e=0x200 c=1 t=1 data={"position":{"x":0,"y":0,"z":0},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":1,"y":1,"z":1},"parent":0} Scene: PUT_COMPONENT e=0x200 c=1018 t=1 data={"mesh":{"$case":"box","box":{"uvs":[]}}} - OPCODES ~= 3k + OPCODES ~= 4k MALLOC_COUNT = 69 ALIVE_OBJS_DELTA ~= 0.03k CALL onUpdate(0.1) @@ -36,4 +36,4 @@ CALL onUpdate(0.1) OPCODES ~= 2k MALLOC_COUNT = 5 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 703.18k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 706.16k bytes \ No newline at end of file