diff --git a/package.json b/package.json index 49a2adc20..23733cb8a 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "@reatom/core": "^3.8.3", "@reatom/core-v2": "^3.1.4", "@reatom/logger": "^3.8.4", - "@reatom/npm-react": "^3.8.9", + "@reatom/npm-react": "^3.8.10", "@reatom/react-v2": "^3.1.2", "@sentry/react": "^7.113.0", "@slack/web-api": "^7.0.4", @@ -113,7 +113,7 @@ "@types/d3-interpolate": "^3.0.4", "@types/file-saver": "^2.0.7", "@types/geojson": "^7946.0.14", - "@types/node": "^20.14.8", + "@types/node": "^20.14.9", "@types/papaparse": "^5.3.14", "@types/react": "^18.2.65", "@types/react-dom": "^18.2.22", @@ -182,8 +182,8 @@ "react-lazily": "^0.9.2", "react-markdown": "^8.0.7", "react-promise-suspense": "^0.3.4", - "react-router": "^6.23.1", - "react-router-dom": "^6.23.1", + "react-router": "^6.24.1", + "react-router-dom": "^6.24.1", "react-transition-group": "^4.4.5", "react-virtuoso": "^4.7.11", "remark-gfm": "^3.0.1", @@ -200,7 +200,7 @@ "uhtml": "^4.5.9", "utility-types": "^3.11.0", "vi-fetch": "^0.8.0", - "vite": "~5.3.1", + "vite": "~5.3.3", "vite-plugin-html": "^3.2.2", "vite-plugin-mkcert": "^1.17.5", "vite-tsconfig-paths": "^4.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a8dd0cb9..50e6e4384 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,7 +66,7 @@ importers: version: 1.1.0-alpha.5 '@nebula.gl/layers': specifier: 1.1.0-alpha.5 - version: 1.1.0-alpha.5(@deck.gl/core@8.9.33)(@deck.gl/extensions@8.9.33(@deck.gl/core@8.9.33)(@luma.gl/constants@8.5.21)(@luma.gl/core@8.5.21)(@math.gl/core@3.6.3)(@math.gl/web-mercator@3.6.3)(gl-matrix@3.4.3))(@deck.gl/geo-layers@8.9.33(@deck.gl/core@8.9.33)(@deck.gl/extensions@8.9.33(@deck.gl/core@8.9.33)(@luma.gl/constants@8.5.21)(@luma.gl/core@8.5.21)(@math.gl/core@3.6.3)(@math.gl/web-mercator@3.6.3)(gl-matrix@3.4.3))(@deck.gl/layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/core@3.4.14)(@luma.gl/core@8.5.21))(@deck.gl/mesh-layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/images@3.4.15)(@luma.gl/core@8.5.21)(@luma.gl/engine@8.5.21)(@luma.gl/gltools@8.5.21)(@luma.gl/webgl@8.5.21))(@loaders.gl/core@3.4.14)(@loaders.gl/gltf@3.4.14)(@loaders.gl/images@3.4.15)(@luma.gl/core@8.5.21)(@luma.gl/engine@8.5.21)(@luma.gl/gltools@8.5.21)(@luma.gl/shadertools@8.5.21)(@luma.gl/webgl@8.5.21))(@deck.gl/layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/core@3.4.14)(@luma.gl/core@8.5.21))(@deck.gl/mesh-layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/images@3.4.15)(@luma.gl/core@8.5.21)(@luma.gl/engine@8.5.21)(@luma.gl/gltools@8.5.21)(@luma.gl/webgl@8.5.21))(@luma.gl/constants@8.5.21)(@luma.gl/core@8.5.21) + version: 1.1.0-alpha.5(fe5wxxh3m5buw4hujpm5qvmhei) '@paypal/react-paypal-js': specifier: ^8.5.0 version: 8.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -80,8 +80,8 @@ importers: specifier: ^3.8.4 version: 3.8.4 '@reatom/npm-react': - specifier: ^3.8.9 - version: 3.8.9(react@18.2.0) + specifier: ^3.8.10 + version: 3.8.10(react@18.2.0) '@reatom/react-v2': specifier: ^3.1.2 version: 3.1.2(@reatom/core-v2@3.1.4)(react@18.2.0) @@ -131,8 +131,8 @@ importers: specifier: ^7946.0.14 version: 7946.0.14 '@types/node': - specifier: ^20.14.8 - version: 20.14.8 + specifier: ^20.14.9 + version: 20.14.9 '@types/papaparse': specifier: ^5.3.14 version: 5.3.14 @@ -168,10 +168,10 @@ importers: version: 1.5.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) + version: 4.3.1(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) '@vitest/coverage-v8': specifier: ^1.6.0 - version: 1.6.0(vitest@1.6.0(@types/node@20.14.8)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) + version: 1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) ajv: specifier: ^8.16.0 version: 8.16.0 @@ -315,7 +315,7 @@ importers: version: 5.7.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(webpack@5.91.0) react-dnd: specifier: ^16.0.1 - version: 16.0.1(@types/node@20.14.8)(@types/react@18.2.65)(react@18.2.0) + version: 16.0.1(@types/node@20.14.9)(@types/react@18.2.65)(react@18.2.0) react-dnd-html5-backend: specifier: ^16.0.1 version: 16.0.1 @@ -338,11 +338,11 @@ importers: specifier: ^0.3.4 version: 0.3.4 react-router: - specifier: ^6.23.1 - version: 6.23.1(react@18.2.0) + specifier: ^6.24.1 + version: 6.24.1(react@18.2.0) react-router-dom: - specifier: ^6.23.1 - version: 6.23.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: ^6.24.1 + version: 6.24.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-transition-group: specifier: ^4.4.5 version: 4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -381,7 +381,7 @@ importers: version: 5.4.5 typescript-plugin-css-modules: specifier: ^5.1.0 - version: 5.1.0(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.4.5))(typescript@5.4.5) + version: 5.1.0(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.4.5))(typescript@5.4.5) uhtml: specifier: ^4.5.9 version: 4.5.9 @@ -392,20 +392,20 @@ importers: specifier: ^0.8.0 version: 0.8.0 vite: - specifier: ~5.3.1 - version: 5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + specifier: ~5.3.3 + version: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) vite-plugin-html: specifier: ^3.2.2 - version: 3.2.2(vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) + version: 3.2.2(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) vite-plugin-mkcert: specifier: ^1.17.5 - version: 1.17.5(vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) + version: 1.17.5(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.4.5)(vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) + version: 4.3.2(typescript@5.4.5)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)) vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@20.14.8)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + version: 1.6.0(@types/node@20.14.9)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) wretch: specifier: ^2.9.0 version: 2.9.0 @@ -433,7 +433,7 @@ importers: version: 7.2.0 playwright-slack-report: specifier: ^1.1.81 - version: 1.1.81(@types/node@20.14.8)(typescript@5.4.5) + version: 1.1.81(@types/node@20.14.9)(typescript@5.4.5) prompts: specifier: ^2.4.2 version: 2.4.2 @@ -1431,8 +1431,8 @@ packages: '@reatom/logger@3.8.4': resolution: {integrity: sha512-MOz8Td1eZV+kU4QpkZXAdO9qFtGjqpm40crIlMNweDtOH7GgUmV2oKgOXRORQzYbeGHVMlQHG4J5iPeEQdM7KA==} - '@reatom/npm-react@3.8.9': - resolution: {integrity: sha512-Z84tNybX23Orm8syPC5h3DrkMU/xmxBfphdb4MY3NMheE5zFOdcJyQCfWJ/1v0FM72nKSWD3YmE+WwFoTgabyA==} + '@reatom/npm-react@3.8.10': + resolution: {integrity: sha512-9a7SPuxSYZAAPR0kt5GkOvPID5nB3RC+wByQPo+io6RQ5htYbufM+kRLwsL6EqfOMJJeKy27BCC8MtowTIL+ng==} peerDependencies: react: '>=16.8.0' @@ -1445,8 +1445,8 @@ packages: '@reatom/utils@3.9.0': resolution: {integrity: sha512-TaRjNhqdM9DtuqCAG4K9ncAQJbRWfITEGp7gvcnR+TnNaLlFHeJ7PjkfWrJQHENXTN3vo47tAe7HhF8nb1XxGA==} - '@remix-run/router@1.16.1': - resolution: {integrity: sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==} + '@remix-run/router@1.17.1': + resolution: {integrity: sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==} engines: {node: '>=14.0.0'} '@rollup/pluginutils@4.2.1': @@ -1871,8 +1871,8 @@ packages: '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - '@types/node@20.14.8': - resolution: {integrity: sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==} + '@types/node@20.14.9': + resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -5361,6 +5361,9 @@ packages: picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -5507,6 +5510,10 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + potpack@2.0.0: resolution: {integrity: sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==} @@ -5741,15 +5748,15 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} - react-router-dom@6.23.1: - resolution: {integrity: sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==} + react-router-dom@6.24.1: + resolution: {integrity: sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' - react-router@6.23.1: - resolution: {integrity: sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==} + react-router@6.24.1: + resolution: {integrity: sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' @@ -6923,8 +6930,8 @@ packages: vite: optional: true - vite@5.3.1: - resolution: {integrity: sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==} + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -8126,8 +8133,8 @@ snapshots: lodash.throttle: 4.1.1 viewport-mercator-project: 7.0.4 - ? '@nebula.gl/layers@1.1.0-alpha.5(@deck.gl/core@8.9.33)(@deck.gl/extensions@8.9.33(@deck.gl/core@8.9.33)(@luma.gl/constants@8.5.21)(@luma.gl/core@8.5.21)(@math.gl/core@3.6.3)(@math.gl/web-mercator@3.6.3)(gl-matrix@3.4.3))(@deck.gl/geo-layers@8.9.33(@deck.gl/core@8.9.33)(@deck.gl/extensions@8.9.33(@deck.gl/core@8.9.33)(@luma.gl/constants@8.5.21)(@luma.gl/core@8.5.21)(@math.gl/core@3.6.3)(@math.gl/web-mercator@3.6.3)(gl-matrix@3.4.3))(@deck.gl/layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/core@3.4.14)(@luma.gl/core@8.5.21))(@deck.gl/mesh-layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/images@3.4.15)(@luma.gl/core@8.5.21)(@luma.gl/engine@8.5.21)(@luma.gl/gltools@8.5.21)(@luma.gl/webgl@8.5.21))(@loaders.gl/core@3.4.14)(@loaders.gl/gltf@3.4.14)(@loaders.gl/images@3.4.15)(@luma.gl/core@8.5.21)(@luma.gl/engine@8.5.21)(@luma.gl/gltools@8.5.21)(@luma.gl/shadertools@8.5.21)(@luma.gl/webgl@8.5.21))(@deck.gl/layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/core@3.4.14)(@luma.gl/core@8.5.21))(@deck.gl/mesh-layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/images@3.4.15)(@luma.gl/core@8.5.21)(@luma.gl/engine@8.5.21)(@luma.gl/gltools@8.5.21)(@luma.gl/webgl@8.5.21))(@luma.gl/constants@8.5.21)(@luma.gl/core@8.5.21)' - : dependencies: + '@nebula.gl/layers@1.1.0-alpha.5(fe5wxxh3m5buw4hujpm5qvmhei)': + dependencies: '@deck.gl/core': 8.9.33 '@deck.gl/extensions': 8.9.33(@deck.gl/core@8.9.33)(@luma.gl/constants@8.5.21)(@luma.gl/core@8.5.21)(@math.gl/core@3.6.3)(@math.gl/web-mercator@3.6.3)(gl-matrix@3.4.3) '@deck.gl/geo-layers': 8.9.33(@deck.gl/core@8.9.33)(@deck.gl/extensions@8.9.33(@deck.gl/core@8.9.33)(@luma.gl/constants@8.5.21)(@luma.gl/core@8.5.21)(@math.gl/core@3.6.3)(@math.gl/web-mercator@3.6.3)(gl-matrix@3.4.3))(@deck.gl/layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/core@3.4.14)(@luma.gl/core@8.5.21))(@deck.gl/mesh-layers@8.9.33(@deck.gl/core@8.9.33)(@loaders.gl/images@3.4.15)(@luma.gl/core@8.5.21)(@luma.gl/engine@8.5.21)(@luma.gl/gltools@8.5.21)(@luma.gl/webgl@8.5.21))(@loaders.gl/core@3.4.14)(@loaders.gl/gltf@3.4.14)(@loaders.gl/images@3.4.15)(@luma.gl/core@8.5.21)(@luma.gl/engine@8.5.21)(@luma.gl/gltools@8.5.21)(@luma.gl/shadertools@8.5.21)(@luma.gl/webgl@8.5.21) @@ -8389,7 +8396,7 @@ snapshots: '@reatom/core': 3.8.3 '@reatom/utils': 3.9.0 - '@reatom/npm-react@3.8.9(react@18.2.0)': + '@reatom/npm-react@3.8.10(react@18.2.0)': dependencies: '@reatom/core': 3.8.3 '@reatom/effects': 3.7.3 @@ -8405,7 +8412,7 @@ snapshots: '@reatom/utils@3.9.0': {} - '@remix-run/router@1.16.1': {} + '@remix-run/router@1.17.1': {} '@rollup/pluginutils@4.2.1': dependencies: @@ -8564,11 +8571,11 @@ snapshots: '@slack/logger@3.0.0': dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 '@slack/logger@4.0.0': dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 '@slack/types@2.12.0': {} @@ -8577,7 +8584,7 @@ snapshots: '@slack/logger': 3.0.0 '@slack/types': 2.12.0 '@types/is-stream': 1.1.0 - '@types/node': 20.14.8 + '@types/node': 20.14.9 axios: 1.6.8(debug@4.3.4) eventemitter3: 3.1.2 form-data: 2.5.1 @@ -8592,7 +8599,7 @@ snapshots: dependencies: '@slack/logger': 4.0.0 '@slack/types': 2.12.0 - '@types/node': 20.14.8 + '@types/node': 20.14.9 '@types/retry': 0.12.0 axios: 1.6.8(debug@4.3.4) eventemitter3: 5.0.1 @@ -8608,7 +8615,7 @@ snapshots: '@slack/webhook@7.0.2': dependencies: '@slack/types': 2.12.0 - '@types/node': 20.14.8 + '@types/node': 20.14.9 axios: 1.6.8(debug@4.3.4) transitivePeerDependencies: - debug @@ -8950,7 +8957,7 @@ snapshots: '@types/gettext-parser@4.0.4': dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 '@types/readable-stream': 4.0.14 '@types/hammerjs@2.0.45': {} @@ -8963,11 +8970,11 @@ snapshots: '@types/http-proxy@1.17.14': dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 '@types/is-stream@1.1.0': dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 '@types/istanbul-lib-coverage@2.0.6': {} @@ -8999,7 +9006,7 @@ snapshots: '@types/ms@0.7.34': {} - '@types/node@20.14.8': + '@types/node@20.14.9': dependencies: undici-types: 5.26.5 @@ -9009,17 +9016,17 @@ snapshots: '@types/papaparse@5.3.14': dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 '@types/pbf@3.0.5': {} '@types/postcss-modules-local-by-default@4.0.2': dependencies: - postcss: 8.4.35 + postcss: 8.4.38 '@types/postcss-modules-scope@3.0.4': dependencies: - postcss: 8.4.35 + postcss: 8.4.38 '@types/prop-types@15.7.11': {} @@ -9050,7 +9057,7 @@ snapshots: '@types/readable-stream@4.0.14': dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 safe-buffer: 5.1.2 '@types/retry@0.12.0': {} @@ -9173,18 +9180,18 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react@4.3.1(vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0))': + '@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0))': dependencies: '@babel/core': 7.24.7 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.7) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.8)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0))': + '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0))': dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 @@ -9199,7 +9206,7 @@ snapshots: std-env: 3.7.0 strip-literal: 2.0.0 test-exclude: 6.0.0 - vitest: 1.6.0(@types/node@20.14.8)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + vitest: 1.6.0(@types/node@20.14.9)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) transitivePeerDependencies: - supports-color @@ -10030,7 +10037,7 @@ snapshots: css-tree@2.3.1: dependencies: mdn-data: 2.0.30 - source-map-js: 1.0.2 + source-map-js: 1.2.0 css-what@6.1.0: {} @@ -11913,7 +11920,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -13232,6 +13239,8 @@ snapshots: picocolors@1.0.0: {} + picocolors@1.0.1: {} + picomatch@2.3.1: {} picomatch@3.0.1: {} @@ -13257,13 +13266,13 @@ snapshots: playwright-core@1.42.1: {} - playwright-slack-report@1.1.81(@types/node@20.14.8)(typescript@5.4.5): + playwright-slack-report@1.1.81(@types/node@20.14.9)(typescript@5.4.5): dependencies: '@slack/web-api': 6.12.0 '@slack/webhook': 7.0.2 commander: 11.1.0 https-proxy-agent: 7.0.4 - ts-node: 10.9.2(@types/node@20.14.8)(typescript@5.4.5) + ts-node: 10.9.2(@types/node@20.14.9)(typescript@5.4.5) zod: 3.23.8 transitivePeerDependencies: - '@swc/core' @@ -13300,13 +13309,13 @@ snapshots: postcss: 8.4.35 postcss-value-parser: 4.2.0 - postcss-load-config@3.1.4(postcss@8.4.35)(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.4.5)): + postcss-load-config@3.1.4(postcss@8.4.35)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.4.5)): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: postcss: 8.4.35 - ts-node: 10.9.2(@types/node@20.14.8)(typescript@5.4.5) + ts-node: 10.9.2(@types/node@20.14.9)(typescript@5.4.5) postcss-modules-extract-imports@3.0.0(postcss@8.4.35): dependencies: @@ -13359,13 +13368,13 @@ snapshots: dependencies: nanoid: 3.3.7 picocolors: 1.0.0 - source-map-js: 1.0.2 + source-map-js: 1.2.0 postcss@8.4.35: dependencies: nanoid: 3.3.7 picocolors: 1.0.0 - source-map-js: 1.0.2 + source-map-js: 1.2.0 postcss@8.4.38: dependencies: @@ -13373,6 +13382,12 @@ snapshots: picocolors: 1.0.0 source-map-js: 1.2.0 + postcss@8.4.39: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + potpack@2.0.0: {} prelude-ls@1.2.1: {} @@ -13495,7 +13510,7 @@ snapshots: dependencies: react: 18.2.0 react-dom: 18.0.0(react@18.2.0) - react-router-dom: 6.23.1(react-dom@18.0.0(react@18.2.0))(react@18.2.0) + react-router-dom: 6.24.1(react-dom@18.0.0(react@18.2.0))(react@18.2.0) react-cosmos-playground2@5.7.2: {} @@ -13557,7 +13572,7 @@ snapshots: dependencies: dnd-core: 16.0.1 - react-dnd@16.0.1(@types/node@20.14.8)(@types/react@18.2.65)(react@18.2.0): + react-dnd@16.0.1(@types/node@20.14.9)(@types/react@18.2.65)(react@18.2.0): dependencies: '@react-dnd/invariant': 4.0.2 '@react-dnd/shallowequal': 4.0.2 @@ -13566,7 +13581,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 18.2.0 optionalDependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 '@types/react': 18.2.65 react-dom@18.0.0(react@18.2.0): @@ -13642,23 +13657,23 @@ snapshots: react-refresh@0.14.2: {} - react-router-dom@6.23.1(react-dom@18.0.0(react@18.2.0))(react@18.2.0): + react-router-dom@6.24.1(react-dom@18.0.0(react@18.2.0))(react@18.2.0): dependencies: - '@remix-run/router': 1.16.1 + '@remix-run/router': 1.17.1 react: 18.2.0 react-dom: 18.0.0(react@18.2.0) - react-router: 6.23.1(react@18.2.0) + react-router: 6.24.1(react@18.2.0) - react-router-dom@6.23.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + react-router-dom@6.24.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: - '@remix-run/router': 1.16.1 + '@remix-run/router': 1.17.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-router: 6.23.1(react@18.2.0) + react-router: 6.24.1(react@18.2.0) - react-router@6.23.1(react@18.2.0): + react-router@6.24.1(react@18.2.0): dependencies: - '@remix-run/router': 1.16.1 + '@remix-run/router': 1.17.1 react: 18.2.0 react-transition-group@4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): @@ -13988,7 +14003,7 @@ snapshots: dependencies: chokidar: 3.5.3 immutable: 4.3.4 - source-map-js: 1.0.2 + source-map-js: 1.2.0 sax@1.3.0: {} @@ -14675,14 +14690,14 @@ snapshots: tslib: 2.6.2 typescript: 5.4.5 - ts-node@10.9.2(@types/node@20.14.8)(typescript@5.4.5): + ts-node@10.9.2(@types/node@20.14.9)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.14.8 + '@types/node': 20.14.9 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 @@ -14824,7 +14839,7 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typescript-plugin-css-modules@5.1.0(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.4.5))(typescript@5.4.5): + typescript-plugin-css-modules@5.1.0(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.4.5))(typescript@5.4.5): dependencies: '@types/postcss-modules-local-by-default': 4.0.2 '@types/postcss-modules-scope': 3.0.4 @@ -14833,7 +14848,7 @@ snapshots: less: 4.2.0 lodash.camelcase: 4.3.0 postcss: 8.4.35 - postcss-load-config: 3.1.4(postcss@8.4.35)(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.4.5)) + postcss-load-config: 3.1.4(postcss@8.4.35)(ts-node@10.9.2(@types/node@20.14.9)(typescript@5.4.5)) postcss-modules-extract-imports: 3.0.0(postcss@8.4.35) postcss-modules-local-by-default: 4.0.4(postcss@8.4.35) postcss-modules-scope: 3.1.1(postcss@8.4.35) @@ -15098,13 +15113,13 @@ snapshots: '@egjs/hammerjs': 2.0.17 component-emitter: 1.3.1 - vite-node@1.6.0(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0): + vite-node@1.6.0(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0): dependencies: cac: 6.7.14 debug: 4.3.4 pathe: 1.1.1 picocolors: 1.0.0 - vite: 5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) transitivePeerDependencies: - '@types/node' - less @@ -15115,7 +15130,7 @@ snapshots: - supports-color - terser - vite-plugin-html@3.2.2(vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)): + vite-plugin-html@3.2.2(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)): dependencies: '@rollup/pluginutils': 4.2.1 colorette: 2.0.20 @@ -15129,43 +15144,43 @@ snapshots: html-minifier-terser: 6.1.0 node-html-parser: 5.4.2 pathe: 0.2.0 - vite: 5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) - vite-plugin-mkcert@1.17.5(vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)): + vite-plugin-mkcert@1.17.5(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)): dependencies: '@octokit/rest': 20.0.2 axios: 1.6.8(debug@4.3.4) debug: 4.3.4 picocolors: 1.0.0 - vite: 5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) transitivePeerDependencies: - supports-color - vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)): + vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0)): dependencies: debug: 4.3.4 globrex: 0.1.2 tsconfck: 3.0.3(typescript@5.4.5) optionalDependencies: - vite: 5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) transitivePeerDependencies: - supports-color - typescript - vite@5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0): + vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0): dependencies: esbuild: 0.21.5 - postcss: 8.4.38 + postcss: 8.4.39 rollup: 4.17.2 optionalDependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 fsevents: 2.3.3 less: 4.2.0 sass: 1.71.1 stylus: 0.62.0 terser: 5.31.0 - vitest@1.6.0(@types/node@20.14.8)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0): + vitest@1.6.0(@types/node@20.14.9)(happy-dom@12.10.3)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -15184,11 +15199,11 @@ snapshots: strip-literal: 2.0.0 tinybench: 2.5.1 tinypool: 0.8.4 - vite: 5.3.1(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) - vite-node: 1.6.0(@types/node@20.14.8)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) + vite-node: 1.6.0(@types/node@20.14.9)(less@4.2.0)(sass@1.71.1)(stylus@0.62.0)(terser@5.31.0) why-is-node-running: 2.2.2 optionalDependencies: - '@types/node': 20.14.8 + '@types/node': 20.14.9 happy-dom: 12.10.3 transitivePeerDependencies: - less diff --git a/src/core/api_client/apiClient.ts b/src/core/api_client/apiClient.ts index 6c703a1d6..92205dab2 100644 --- a/src/core/api_client/apiClient.ts +++ b/src/core/api_client/apiClient.ts @@ -22,7 +22,7 @@ import type { } from './types'; export const LOCALSTORAGE_AUTH_KEY = 'auth_token'; - +const TIME_TO_REFRESH_MS = 1000 * 60 * 3; export class ApiClient { private listeners = new Map([['error', new Set<(e: ApiClientError) => void>()]]); private loginApiPath!: string; @@ -36,7 +36,7 @@ export class ApiClient { private tokenRefreshFlowPromise: Promise | undefined; private keycloakClientId!: string; private baseURL!: string; - timeToRefresh: number = 1000 * 60 * 3; // Should be less then Access Token Lifespan + timeToRefresh: number = TIME_TO_REFRESH_MS; // Should be less then Access Token Lifespan /** * The Singleton's constructor should always be private to prevent direct @@ -91,15 +91,15 @@ export class ApiClient { const decodedToken: JWTData = jwtDecode(token); const decodedRefreshToken: JWTData = jwtDecode(refreshToken); if (KONTUR_DEBUG) { - console.debug({ decodedToken, now: new Date().getTime() }); - console.debug({ decodedRefreshToken, now: new Date().getTime() }); + console.debug({ + decodedToken, + at_ttl: decodedToken.exp - decodedToken.iat, + decodedRefreshToken, + rt_ttl: decodedRefreshToken.exp - decodedRefreshToken.iat, + now: Date.now(), + }); } - if ( - decodedToken && - decodedToken.exp && - decodedRefreshToken && - decodedRefreshToken.exp - ) { + if (decodedToken?.exp && decodedRefreshToken?.exp) { const expiringDate = new Date(decodedToken.exp * 1000); const expiringRefreshDate = new Date(decodedRefreshToken.exp * 1000); if (expiringDate > new Date()) { @@ -133,6 +133,12 @@ export class ApiClient { const { token, refreshToken } = JSON.parse(storedTokensJson); if (token && refreshToken) { const decodedToken: JWTData = jwtDecode(token); + const tokenLifetime = decodedToken.exp - decodedToken.iat; + // ensure timeToRefresh is shorter than tokenLifetime + this.timeToRefresh = Math.min( + Math.trunc((tokenLifetime * 1000) / 5), + TIME_TO_REFRESH_MS, + ); const decodedRefreshToken: JWTData = jwtDecode(refreshToken); const expiringDate = new Date(decodedToken.exp * 1000); const expiringRefreshDate = new Date(decodedRefreshToken.exp * 1000); @@ -173,7 +179,7 @@ export class ApiClient { } const diffTime = this.tokenExpirationDate.getTime() - new Date().getTime(); if (diffTime < this.timeToRefresh) { - // token expires soon - in 5 minutes, refresh it + // token expires soon, refresh it try { await this.refreshAuthToken(); } catch (error) { @@ -257,7 +263,6 @@ export class ApiClient { } catch (err) { // unable to login or refresh token const error = createApiError(err); - this._emit('error', error); throw error; } } @@ -266,7 +271,7 @@ export class ApiClient { this.listeners.get(type)?.forEach((l) => l(payload)); } - async logout() { + logout() { this.resetAuth(); } diff --git a/src/core/api_client/errors.ts b/src/core/api_client/errors.ts index 96372a1bf..fcd8f66ad 100644 --- a/src/core/api_client/errors.ts +++ b/src/core/api_client/errors.ts @@ -25,6 +25,7 @@ export function getTranslatedApiErrors(i18n: { t: (arg0: string) => string }) { export function parseApiError(errorObj: WretchError): string { if (errorObj?.json) { const errorData = errorObj?.json; + if (errorData?.error_description) return errorData.error_description; if (errorData !== null) { if (Array.isArray(errorData)) { return errorData @@ -49,7 +50,15 @@ export function parseApiError(errorObj: WretchError): string { } return String(errorData); } - return errorObj?.text ?? errorObj?.message ?? 'Unknown Error'; + let res: string | undefined = + errorObj?.response?.statusText ?? errorObj?.message ?? errorObj?.text; + if (res?.startsWith('')) { + const parser = new DOMParser(); + const doc = parser.parseFromString(res, 'text/html'); + const title = doc.querySelector('title'); + res = title?.innerText; + } + return res ?? 'Unknown Error'; } export function createApiError(err: unknown) { @@ -66,9 +75,10 @@ export function createApiError(err: unknown) { } if (err instanceof wretch.WretchError) { status = err.status; - // In case of 401 error we need to parse error message from body and show it to user - if (status === 401) { - errorMessage = err.json?.error_description ?? err?.message ?? 'Auth error'; + // In case of 400/401 error we need to parse error message from body and show it to user + if (status === 400) { + problem = { kind: 'bad-request' }; + } else if (status === 401) { problem = { kind: 'unauthorized', data: err.json?.error }; } else if (status === 403) { problem = { kind: 'forbidden' }; diff --git a/src/core/app/authHooks.ts b/src/core/app/authHooks.ts index 75451f4bb..a06e6b6b7 100644 --- a/src/core/app/authHooks.ts +++ b/src/core/app/authHooks.ts @@ -1,7 +1,7 @@ import { configRepo } from '~core/config'; -import { currentUserAtom } from '~core/shared_state'; +import { currentUserAtom } from '~core/shared_state/currentUser'; +import { featureFlagsAtom } from '~core/shared_state/featureFlags'; import { yandexMetrics } from '~core/metrics'; -import { setFeatures } from './features'; import type { UserDto } from './user'; export async function onLogin() { @@ -10,7 +10,7 @@ export async function onLogin() { externalLoginTasks(config.user); currentUserAtom.setUser.dispatch({ ...config.user }); } - setFeatures(config.features); + featureFlagsAtom.set.dispatch(config.features); } function externalLoginTasks(user: UserDto) { diff --git a/src/core/app/features.ts b/src/core/app/features.ts deleted file mode 100644 index 71867fbb1..000000000 --- a/src/core/app/features.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { featureFlagsAtom } from '~core/shared_state'; -import { createBooleanAtom } from '~utils/atoms'; -import { store } from '~core/store/store'; -import type { AppFeatureType } from '~core/auth/types'; - -export const featuresWereSetAtom = createBooleanAtom(false, 'featuresWereSetAtom'); - -export function setFeatures(value: Record) { - store.dispatch([featureFlagsAtom.set(value), featuresWereSetAtom.setTrue()]); -} diff --git a/src/core/app/postAppInit.ts b/src/core/app/postAppInit.ts index d247ee99a..fb171d466 100644 --- a/src/core/app/postAppInit.ts +++ b/src/core/app/postAppInit.ts @@ -1,6 +1,5 @@ import { authClientInstance } from '~core/authClientInstance'; import { urlStoreAtom } from '~core/url_store'; -import { autoClearAtom } from '~core/logical_layers'; import { onLogin } from './authHooks'; import { runAtom } from './index'; import type { Config } from '~core/config/types'; @@ -15,7 +14,4 @@ export async function postAppInit(config: Config) { layers: config.activeLayers, }); runAtom(urlStoreAtom); - - // init LogicalLayers - runAtom(autoClearAtom); } diff --git a/src/core/auth/atoms/userState.ts b/src/core/auth/atoms/userState.ts deleted file mode 100644 index 0839ce41f..000000000 --- a/src/core/auth/atoms/userState.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { createAtom } from '~utils/atoms'; -import { UserStateStatus } from '~core/auth/types'; -import type { UserStateType } from '~core/auth/types'; - -export const userStateAtom = createAtom( - { - setState: (state: UserStateType) => state, - authorize: () => null, - reset: () => null, - login: () => null, - logout: () => null, - signup: () => null, - passwordReset: () => null, - }, - ( - { onAction, onInit, schedule, create }, - state: UserStateType = UserStateStatus.UNAUTHORIZED, - ) => { - onAction('setState', (st) => { - if (state !== st) { - state = st; - } - }); - - onAction('authorize', () => { - if (state !== UserStateStatus.AUTHORIZED) { - state = UserStateStatus.AUTHORIZED; - } - }); - - onAction('reset', () => { - if (state !== UserStateStatus.UNAUTHORIZED) { - state = UserStateStatus.UNAUTHORIZED; - } - }); - - onAction('login', () => { - if ( - state === UserStateStatus.UNAUTHORIZED || - state === UserStateStatus.SIGNING_UP - ) { - state = UserStateStatus.LOGGING_IN; - } - }); - - onAction('logout', () => { - if ( - state === UserStateStatus.AUTHORIZED || - state === UserStateStatus.PASSWORD_RESET - ) { - state = UserStateStatus.UNAUTHORIZED; - } - }); - - onAction('signup', () => { - if ( - state === UserStateStatus.UNAUTHORIZED || - state === UserStateStatus.LOGGING_IN - ) { - state = UserStateStatus.SIGNING_UP; - } - }); - - onAction('passwordReset', () => { - if (state === UserStateStatus.AUTHORIZED) { - state = UserStateStatus.PASSWORD_RESET; - } - }); - - return state; - }, - '[Shared state] userStateAtom', -); diff --git a/src/core/auth/client/AuthClient.ts b/src/core/auth/client/AuthClient.ts index 78f238061..a06252a6b 100644 --- a/src/core/auth/client/AuthClient.ts +++ b/src/core/auth/client/AuthClient.ts @@ -1,5 +1,4 @@ import { noop } from '@reatom/core-v2'; -import { userStateAtom } from '~core/auth/atoms/userState'; import type { ApiClient } from '~core/api_client'; interface AuthClientConfig { @@ -15,7 +14,6 @@ export class AuthClient { private readonly _apiClient: ApiClient; loginHook: AuthLoginHook = noop; - logoutHook: AuthLogoutHook = noop; private constructor({ apiClient }: AuthClientConfig) { this._apiClient = apiClient; } @@ -39,14 +37,11 @@ export class AuthClient { public logout() { this._apiClient.logout(); - this.logoutHook(); - userStateAtom.logout.dispatch(); // reload to init with public config and profile location.reload(); } private startAuthenticated() { this.loginHook(); - userStateAtom.authorize.dispatch(); } /** diff --git a/src/core/auth/index.ts b/src/core/auth/index.ts index 5e27d5d4d..2f1ebc208 100644 --- a/src/core/auth/index.ts +++ b/src/core/auth/index.ts @@ -1,3 +1,2 @@ export { AuthClient } from './client/AuthClient'; -export { userStateAtom } from './atoms/userState'; export { landUser } from './atoms/userWasLanded'; diff --git a/src/core/config/loaders/appConfigLoader.ts b/src/core/config/loaders/appConfigLoader.ts index 83a0c55ba..62a59394a 100644 --- a/src/core/config/loaders/appConfigLoader.ts +++ b/src/core/config/loaders/appConfigLoader.ts @@ -22,7 +22,10 @@ export async function getAppConfig(appId?: string): Promise { // In case appId absent in url - backend identifying it by domain const appCfg = await apiClient.get( '/apps/configuration', - { appId }, + { + appId, + ts: Date.now(), // bypass cache + }, true, ); if (appCfg === null) throw Error('App configuration unavailable'); diff --git a/src/core/logical_layers/atoms/autoClear.ts b/src/core/logical_layers/atoms/autoClear.ts deleted file mode 100644 index d625218b6..000000000 --- a/src/core/logical_layers/atoms/autoClear.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createAtom } from '~utils/atoms'; -import { currentMapAtom } from '../../shared_state/currentMap'; -import { hiddenLayersAtom } from './hiddenLayers'; -import { mountedLayersAtom } from './mountedLayers'; - -export const autoClearAtom = createAtom( - { - currentMapAtom, - }, - ({ onChange, schedule }) => { - onChange('currentMapAtom', (currentMap, prevMap) => { - if (!prevMap) return; - schedule((dispatch) => { - dispatch([hiddenLayersAtom.clear(), mountedLayersAtom.clear()]); - }); - }); - }, - 'autoClearAtom', -); diff --git a/src/core/logical_layers/index.ts b/src/core/logical_layers/index.ts index a4fa090c6..008feee91 100644 --- a/src/core/logical_layers/index.ts +++ b/src/core/logical_layers/index.ts @@ -1,2 +1 @@ -export { autoClearAtom } from './atoms/autoClear'; export { layerByOrder } from './utils/layersOrder/layerByOrder'; diff --git a/src/core/metrics/app-metrics.ts b/src/core/metrics/app-metrics.ts index 2da14a24f..39649058c 100644 --- a/src/core/metrics/app-metrics.ts +++ b/src/core/metrics/app-metrics.ts @@ -9,7 +9,6 @@ import { } from './constants'; import { Sequence } from './sequence'; import type { MetricsReportTemplate, MetricsEvent, Metric } from './types'; -import type { AppFeatureType } from '~core/auth/types'; class MetricMarker { readonly event: string; @@ -59,17 +58,16 @@ export class AppMetrics implements Metric { return this._instance; } - init(appId: string, route: string, hasFeature: (f: AppFeatureType) => boolean): void { + init(appId: string, routeId: string): void { // currently we support metrics only for map page - if (route !== '') { - // '' is route for map + if (routeId !== 'map') { return; } this.reportTemplate.appId = appId ?? ''; // add available features to metrics - this.watchList = buildWatchList(hasFeature); + this.watchList = buildWatchList(); globalThis.addEventListener(METRICS_EVENT, this.listener.bind(this) as EventListener); @@ -79,7 +77,11 @@ export class AppMetrics implements Metric { ); if (KONTUR_METRICS_DEBUG) { - console.info(`appMetrics.init route:${route}`, this.reportTemplate, this.watchList); + console.info( + `appMetrics.init route:${routeId}`, + this.reportTemplate, + this.watchList, + ); } this.exposeMetrics(); diff --git a/src/core/metrics/constants.ts b/src/core/metrics/constants.ts index 2b279051c..5dcdf6672 100644 --- a/src/core/metrics/constants.ts +++ b/src/core/metrics/constants.ts @@ -1,4 +1,5 @@ import { AppFeature } from '~core/auth/types'; +import { configRepo } from '~core/config'; import type { AppFeatureType } from '~core/auth/types'; import type { MetricsReportTemplate } from './types'; @@ -29,11 +30,11 @@ const APPEVENT_TO_FEATURE = { // ! Relevant only for map mode // WIP implementation // WatchList format {eventToWatch: null, ...} -export function buildWatchList(hasFeature: (f: AppFeatureType) => boolean) { +export function buildWatchList() { const effectiveWatchList = {}; Object.entries(APPEVENT_TO_FEATURE).forEach(([appEvent, value]) => { - if (value === null || value.every((f) => hasFeature(f))) { + if (value === null || value.every((f) => !!configRepo.get().features[f])) { effectiveWatchList[appEvent] = null; } }); diff --git a/src/core/metrics/index.ts b/src/core/metrics/index.ts index 25a504aad..28aa22235 100644 --- a/src/core/metrics/index.ts +++ b/src/core/metrics/index.ts @@ -4,7 +4,6 @@ import { AppMetrics } from './app-metrics'; import { GoogleMetrics } from './externalMetrics/googleMetrics'; import { YandexMetrics } from './externalMetrics/yandexMetrics'; import { addAllSequences } from './sequences'; -import type { AppFeatureType } from '~core/auth/types'; const appMetrics = AppMetrics.getInstance(); const googleMetrics = new GoogleMetrics(); @@ -12,19 +11,17 @@ export const yandexMetrics = new YandexMetrics(); addAllSequences(appMetrics); -export const initMetricsOnce = once( - async (appId: string, route: string, hasFeature: (f: AppFeatureType) => boolean) => { - appMetrics.init(appId, route, hasFeature); +export const initMetricsOnce = once(async (appId: string, routeId: string) => { + appMetrics.init(appId, routeId); - /* Enabling / Disabling GTM */ - if (import.meta.env.MODE !== 'development') { - const externalMetrics = [googleMetrics, yandexMetrics]; - const gtmPermission = cookieManagementService.requestPermission('GTM'); - gtmPermission.onStatusChange((status) => { - if (status === permissionStatuses.granted) { - externalMetrics.forEach((metric) => metric.init(appId, route)); - } - }); - } - }, -); + /* Enabling / Disabling GTM */ + if (import.meta.env.MODE !== 'development') { + const externalMetrics = [googleMetrics, yandexMetrics]; + const gtmPermission = cookieManagementService.requestPermission('GTM'); + gtmPermission.onStatusChange((status) => { + if (status === permissionStatuses.granted) { + externalMetrics.forEach((metric) => metric.init(appId, routeId)); + } + }); + } +}); diff --git a/src/core/postInit.ts b/src/core/postInit.ts index 3b52a411c..61222b9f0 100644 --- a/src/core/postInit.ts +++ b/src/core/postInit.ts @@ -1,25 +1,9 @@ -import { useAtom } from '@reatom/react-v2'; -import { useEffect } from 'react'; import { configRepo } from '~core/config'; import { initMetricsOnce } from '~core/metrics'; -import { currentRouteAtom } from '~core/router/atoms/currentRoute'; -import { featureFlagsAtom } from '~core/shared_state'; -import type { AppFeatureType } from '~core/auth/types'; // Temporary solution till we refactor init and move it to proper place // current goal is to isolate all that init tasks in one place -export function PostInit() { - const [route] = useAtom(currentRouteAtom); - const [featureFlags] = useAtom(featureFlagsAtom); - - useEffect(() => { - if (route && Object.keys(featureFlags).length) { - // at this point must be ready: appconfig, i18n, appId, current route - // TODO: use better approach for getEffectiveFeature from #13368 - const getEffectiveFeature = (f: AppFeatureType) => featureFlags[f]; - initMetricsOnce(configRepo.get().id, route?.slug, getEffectiveFeature); - } - }, [route, featureFlags]); - +export function postInit(routeId: string) { + initMetricsOnce(configRepo.get().id, routeId); return null; } diff --git a/src/core/router/atoms/availableRoutes.ts b/src/core/router/atoms/availableRoutes.ts index 3d75f8ff9..f763597f3 100644 --- a/src/core/router/atoms/availableRoutes.ts +++ b/src/core/router/atoms/availableRoutes.ts @@ -26,7 +26,7 @@ export function getAvailableRoutes() { const defaultRoute = available.routes.find((r) => r.slug === available.defaultRoute); if (!defaultRoute || defaultRoute?.disabled) { - const newDefault = available.routes.find((r) => !r.disabled && !r.parentRoute); + const newDefault = available.routes.find((r) => !r.disabled && !r.parentRouteId); if (newDefault) { available.defaultRoute = newDefault.slug; } else { diff --git a/src/core/router/atoms/currentRoute.ts b/src/core/router/atoms/currentRoute.ts index c6e4e0fd5..74e2b8886 100644 --- a/src/core/router/atoms/currentRoute.ts +++ b/src/core/router/atoms/currentRoute.ts @@ -1,4 +1,5 @@ import { matchPath } from 'react-router'; +import { configRepo } from '~core/config'; import { createAtom } from '~utils/atoms'; import { getAbsoluteRoute } from '../getAbsoluteRoute'; import { availableRoutesAtom } from './availableRoutes'; @@ -16,18 +17,24 @@ export const currentRouteAtom = createAtom( if (routesConfig === null) return null; const location = get('currentLocationAtom'); return ( - routesConfig.routes.find((route) => - matchPath( - { - path: getAbsoluteRoute(route), - exact: true, - }, + routesConfig.routes.find((route) => { + const path = getAbsoluteRoute(route); + const normalizedLocation = stripBasename( location.pathname, - ), - ) ?? null + configRepo.get().baseUrl, + ); + return matchPath({ path, exact: true }, normalizedLocation); + }) ?? null ); }, 'currentRouteAtom', ); export type CurrentRouteAtom = typeof currentRouteAtom; + +export function stripBasename(pathname: string, basename: string): string { + if (basename === '/') return pathname; + // leave trailing slash behavior in the user's control + const startIndex = basename.endsWith('/') ? basename.length - 1 : basename.length; + return pathname.slice(startIndex) || '/'; +} diff --git a/src/core/router/components/Router.tsx b/src/core/router/components/Router.tsx index 56c8675a7..2f2dd6ac7 100644 --- a/src/core/router/components/Router.tsx +++ b/src/core/router/components/Router.tsx @@ -6,7 +6,7 @@ import { } from 'react-router-dom'; import { KeepAliveProvider } from 'react-component-keepalive-ts'; import { Suspense } from 'react'; -import { PostInit } from '~core/postInit'; +import { postInit } from '~core/postInit'; import { CommonView } from '~views/CommonView'; import { configRepo } from '~core/config'; import { LoadingSpinner } from '~components/LoadingSpinner/LoadingSpinner'; @@ -37,7 +37,6 @@ export function Router() { function Layout() { return ( <> - ({ - id: r.slug, - path: getAbsoluteRoute(r.parentRoute ? `${r.parentRoute}/${r.slug}` : r.slug), + id: r.id, + path: getAbsoluteRoute(r.parentRouteId ? `${r.parentRouteId}/${r.slug}` : r.slug), element: }>{r.view}, })); @@ -94,6 +93,9 @@ export function initRouter() { router.navigate(getAbsoluteRoute(initialRedirect)); } + // Run last parts of app init requiring router + postInit(router?.state?.matches?.at(1)?.route.id ?? ''); + return router; } diff --git a/src/core/router/getAbsoluteRoute.ts b/src/core/router/getAbsoluteRoute.ts index 26091a665..9f3bfc0ca 100644 --- a/src/core/router/getAbsoluteRoute.ts +++ b/src/core/router/getAbsoluteRoute.ts @@ -16,8 +16,8 @@ export const getAbsoluteRoute = (slugOrRoute: string | AppRoute, base = '') => { if (typeof slugOrRoute === 'string') return '/' + pathJoin(base, slugOrRoute); return getAbsoluteRoute( - slugOrRoute.parentRoute - ? `${slugOrRoute.parentRoute}/${slugOrRoute.slug}` + slugOrRoute.parentRouteId + ? `${slugOrRoute.parentRouteId}/${slugOrRoute.slug}` : slugOrRoute.slug, base, ); diff --git a/src/core/router/routes.tsx b/src/core/router/routes.tsx index 31da8d774..48a3c8103 100644 --- a/src/core/router/routes.tsx +++ b/src/core/router/routes.tsx @@ -30,6 +30,7 @@ export const routerConfig: AppRouterConfig = { defaultRoute: '', routes: [ { + id: 'map', slug: '', title: i18n.t('modes.map'), icon: , @@ -38,6 +39,7 @@ export const routerConfig: AppRouterConfig = { cached: true, }, { + id: 'reports', slug: 'reports', title: i18n.t('modes.reports'), icon: , @@ -45,15 +47,17 @@ export const routerConfig: AppRouterConfig = { requiredFeature: AppFeature.REPORTS, }, { + id: 'report', slug: ':reportId', title: 'modes.report', icon: , view: , requiredFeature: AppFeature.REPORTS, - parentRoute: 'reports', + parentRouteId: 'reports', visibilityInNavigation: 'never', }, { + id: 'bivariate-manager', slug: 'bivariate-manager', title: i18n.t('sidebar.biv_color_manager'), icon: , @@ -61,6 +65,7 @@ export const routerConfig: AppRouterConfig = { requiredFeature: AppFeature.BIVARIATE_COLOR_MANAGER, }, { + id: 'profile', slug: 'profile', title: isAuthenticated ? i18n.t('modes.profile') : i18n.t('login.login_button'), icon: , @@ -68,6 +73,7 @@ export const routerConfig: AppRouterConfig = { requiredFeature: AppFeature.APP_LOGIN, }, { + id: 'pricing', slug: 'pricing', title: i18n.t('subscription.title'), icon: , @@ -75,6 +81,7 @@ export const routerConfig: AppRouterConfig = { requiredFeature: AppFeature.SUBSCRIPTION, }, { + id: 'about', slug: 'about', title: i18n.t('modes.about'), icon: , @@ -83,20 +90,22 @@ export const routerConfig: AppRouterConfig = { requiredFeature: AppFeature.ABOUT_PAGE, }, { + id: 'privacy', slug: 'privacy', title: i18n.t('modes.privacy'), icon: , view: , - parentRoute: 'about', + parentRouteId: 'about', visibilityInNavigation: 'always', requiredFeature: AppFeature.ABOUT_PAGE, }, { + id: 'cookies', slug: 'cookies', title: 'modes.cookies', icon: , view: , - parentRoute: 'about', + parentRouteId: 'about', visibilityInNavigation: 'never', requiredFeature: AppFeature.ABOUT_PAGE, }, diff --git a/src/core/router/types.ts b/src/core/router/types.ts index a32363ef3..e53ac568c 100644 --- a/src/core/router/types.ts +++ b/src/core/router/types.ts @@ -1,6 +1,7 @@ import type { AppFeatureType } from '~core/auth/types'; export interface AppRoute { + id: string; slug: string; view: JSX.Element; icon: JSX.Element; @@ -13,7 +14,7 @@ export interface AppRoute { /** What features must be enabled to show this route */ requiredFeature?: AppFeatureType; /** Nest routes to each other */ - parentRoute?: string; + parentRouteId?: string; /** * Visibility in navigation sidebar * - auto (default) - show when route or it's parent is active diff --git a/src/core/store/store.ts b/src/core/store/store.ts index b43b1b355..5116ad4d0 100644 --- a/src/core/store/store.ts +++ b/src/core/store/store.ts @@ -36,7 +36,7 @@ function logger(logs: Logs) { .map((a) => String(a) .trim() - .replace('https://localhost:3000/src', '~') + .replace(`https://localhost:3000${import.meta.env.BASE_URL}src`, '~') .replace('at ', '') .replace(/\?t=\d+/, ''), ); @@ -71,7 +71,6 @@ function logger(logs: Logs) { console.trace('TRACE:', name, logs); } } - KONTUR_DEBUG && console.debug(name, payload); KONTUR_WARN && console.warn(name, payload); KONTUR_TRACE_PATCH && console.info(name, payload); }); diff --git a/src/features/layers_in_area/atoms/areaLayersDetailsResource/areaLayersDetailsResourceAtomCache.ts b/src/features/layers_in_area/atoms/areaLayersDetailsResource/areaLayersDetailsResourceAtomCache.ts index fb06ab944..d8af97970 100644 --- a/src/features/layers_in_area/atoms/areaLayersDetailsResource/areaLayersDetailsResourceAtomCache.ts +++ b/src/features/layers_in_area/atoms/areaLayersDetailsResource/areaLayersDetailsResourceAtomCache.ts @@ -1,5 +1,4 @@ import { createAtom } from '~utils/atoms/createPrimitives'; -import { currentUserAtom } from '~core/shared_state'; import type { LayerDetailsDto } from '~core/logical_layers/types/source'; import type { DetailsRequestParams } from './types'; @@ -27,17 +26,12 @@ Cache structure: export const areaLayersDetailsResourceAtomCache = createAtom( { - user: currentUserAtom, update: (request: DetailsRequestParams, response: LayerDetailsDto[]) => ({ request, response, }), }, - ({ onAction, onChange }, state = createDefaultCacheState()) => { - onChange('user', () => { - state = createDefaultCacheState(); - }); - + ({ onAction }, state = createDefaultCacheState()) => { onAction('update', ({ request, response }) => { state = createDefaultCacheState(state); const layersToRetrieveWithEventId = new Set(request.layersToRetrieveWithEventId); diff --git a/src/features/side_bar/components/SideBar/NavButton.tsx b/src/features/side_bar/components/SideBar/NavButton.tsx index b525950a9..f5781c26d 100644 --- a/src/features/side_bar/components/SideBar/NavButton.tsx +++ b/src/features/side_bar/components/SideBar/NavButton.tsx @@ -26,13 +26,13 @@ export function NavButton({ if (!isVisible) return null; - const navLinkClassName = clsx(route.parentRoute ? s.nestedRoute : s.topLevelRoute); + const navLinkClassName = clsx(route.parentRouteId ? s.nestedRoute : s.topLevelRoute); return ( { const foo = { ...defaults, slug: 'foo', + id: 'idfoo', }; const bar = { ...defaults, slug: 'bar', + id: 'idbar', }; const bar_child = { ...defaults, slug: 'bar-child', - parentRoute: 'bar', + id: 'idbar-child', + parentRouteId: 'idbar', }; const bar_child_neighbor = { ...defaults, slug: 'bar-child-neighbor', - parentRoute: 'bar', + id: 'idbar-child-neighbor', + parentRouteId: 'idbar', }; context.routes = { diff --git a/src/features/side_bar/components/SideBar/routeVisibilityChecker.ts b/src/features/side_bar/components/SideBar/routeVisibilityChecker.ts index dd86f51f1..83dac1ea0 100644 --- a/src/features/side_bar/components/SideBar/routeVisibilityChecker.ts +++ b/src/features/side_bar/components/SideBar/routeVisibilityChecker.ts @@ -3,12 +3,12 @@ import type { AppRoute } from '~core/router'; export function routeVisibilityChecker(routes: AppRoute[]) { type RoutesTree = { [key: string]: RoutesTree }; const routesTree = routes.reduce((tree, route) => { - if (route.parentRoute) { - if (!tree[route.parentRoute]) tree[route.parentRoute] = {}; - tree[route.parentRoute][route.slug] = {}; + if (route.parentRouteId) { + if (!tree[route.parentRouteId]) tree[route.parentRouteId] = {}; + tree[route.parentRouteId][route.id] = {}; return tree; } - tree[route.slug] = {}; + tree[route.id] = {}; return tree; }, {} as RoutesTree); @@ -24,16 +24,16 @@ export function routeVisibilityChecker(routes: AppRoute[]) { default: // always show top level routes // hide nested routes if no selected routes in same branch - if (!route.parentRoute) return true; + if (!route.parentRouteId) return true; if (currentRoute === null) return false; - const isActive = route.slug === currentRoute.slug; - const haveActiveParentRoute = route.parentRoute - ? currentRoute?.slug === route.parentRoute + const isActive = route.id === currentRoute.id; + const haveActiveParentRoute = route.parentRouteId + ? currentRoute?.id === route.parentRouteId : false; - const neighbors = route.parentRoute - ? Object.keys(routesTree[route.parentRoute]) + const neighbors = route.parentRouteId + ? Object.keys(routesTree[route.parentRouteId]) : []; - const haveActiveNeighbor = neighbors.includes(currentRoute.slug); + const haveActiveNeighbor = neighbors.includes(currentRoute.id); return isActive || haveActiveParentRoute || haveActiveNeighbor; } diff --git a/src/utils/map/mapCSSToMapBoxPropertiesConverter/getRequirements.ts b/src/utils/map/mapCSSToMapBoxPropertiesConverter/getRequirements.ts index ecdda5966..560604100 100644 --- a/src/utils/map/mapCSSToMapBoxPropertiesConverter/getRequirements.ts +++ b/src/utils/map/mapCSSToMapBoxPropertiesConverter/getRequirements.ts @@ -1,3 +1,5 @@ +const DEBUG_MAPCSS = !!globalThis.localStorage?.getItem('DEBUG_MAPCSS'); + export function getRequirements(config, mapCSS) { const initialCSS = { // * Next important string need for generate casing offset when main line offset not set @@ -8,7 +10,7 @@ export function getRequirements(config, mapCSS) { Object.entries({ ...initialCSS, ...mapCSS }).forEach(([prop, value]) => { const req = config[prop]; if (req === undefined) { - console.debug(`Unsupported property: "${prop}"`); + DEBUG_MAPCSS && console.debug(`Unsupported property: "${prop}"`); } else { requirements.push([req, value]); } diff --git a/src/views/Profile/Profile.tsx b/src/views/Profile/Profile.tsx index 183e6bad3..e5bcdeaa5 100644 --- a/src/views/Profile/Profile.tsx +++ b/src/views/Profile/Profile.tsx @@ -1,12 +1,11 @@ -import { useAtom } from '@reatom/react-v2'; -import { userStateAtom } from '~core/auth'; +import { configRepo } from '~core/config'; import { LoginForm, SettingsForm } from '~features/user_profile'; import s from './Profile.module.css'; export function ProfilePage() { - const [userState] = useAtom(userStateAtom); + const user = configRepo.get().user; - if (userState === 'authorized') + if (user) return (