From dd6ad7556e3c031dc076f9ca84d356fdddc138a2 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 23 Jul 2021 11:21:54 +0200 Subject: [PATCH 01/98] Include @spree/storefront-api-v2-sdk --- package.json | 1 + yarn.lock | 1786 ++++++++++++++++++++++++++------------------------ 2 files changed, 924 insertions(+), 863 deletions(-) diff --git a/package.json b/package.json index 95b46c4971..6451806ec2 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@react-spring/web": "^9.2.1", + "@spree/storefront-api-v2-sdk": "^4.5.4", "@vercel/fetch": "^6.1.0", "autoprefixer": "^10.2.6", "body-scroll-lock": "^3.1.5", diff --git a/yarn.lock b/yarn.lock index 8d1a534a53..eb580b5654 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,33 +4,33 @@ "@ardatan/aggregate-error@0.0.6": version "0.0.6" - resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" + resolved "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz" integrity sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ== dependencies: tslib "~2.0.1" "@babel/code-frame@7.12.11": version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz" integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: "@babel/highlight" "^7.10.4" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz" integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== dependencies: "@babel/highlight" "^7.14.5" "@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7": version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz" integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== "@babel/core@^7.0.0": version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.14.6.tgz" integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA== dependencies: "@babel/code-frame" "^7.14.5" @@ -51,7 +51,7 @@ "@babel/generator@^7.12.13", "@babel/generator@^7.14.5", "@babel/generator@^7.5.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.14.5.tgz" integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== dependencies: "@babel/types" "^7.14.5" @@ -60,14 +60,14 @@ "@babel/helper-annotate-as-pure@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz" integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA== dependencies: "@babel/types" "^7.14.5" "@babel/helper-compilation-targets@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz" integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== dependencies: "@babel/compat-data" "^7.14.5" @@ -77,7 +77,7 @@ "@babel/helper-create-class-features-plugin@^7.14.5": version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz" integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg== dependencies: "@babel/helper-annotate-as-pure" "^7.14.5" @@ -89,7 +89,7 @@ "@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz" integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== dependencies: "@babel/helper-get-function-arity" "^7.14.5" @@ -98,35 +98,35 @@ "@babel/helper-get-function-arity@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz" integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== dependencies: "@babel/types" "^7.14.5" "@babel/helper-hoist-variables@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz" integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== dependencies: "@babel/types" "^7.14.5" "@babel/helper-member-expression-to-functions@^7.14.5": version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz" integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA== dependencies: "@babel/types" "^7.14.5" "@babel/helper-module-imports@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz" integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== dependencies: "@babel/types" "^7.14.5" "@babel/helper-module-transforms@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz" integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== dependencies: "@babel/helper-module-imports" "^7.14.5" @@ -140,19 +140,19 @@ "@babel/helper-optimise-call-expression@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz" integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA== dependencies: "@babel/types" "^7.14.5" "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== "@babel/helper-replace-supers@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz" integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== dependencies: "@babel/helper-member-expression-to-functions" "^7.14.5" @@ -162,38 +162,38 @@ "@babel/helper-simple-access@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz" integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== dependencies: "@babel/types" "^7.14.5" "@babel/helper-skip-transparent-expression-wrappers@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz" integrity sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ== dependencies: "@babel/types" "^7.14.5" "@babel/helper-split-export-declaration@^7.12.13", "@babel/helper-split-export-declaration@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz" integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== dependencies: "@babel/types" "^7.14.5" "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz" integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== "@babel/helper-validator-option@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz" integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== "@babel/helpers@^7.14.6": version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.6.tgz" integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA== dependencies: "@babel/template" "^7.14.5" @@ -202,7 +202,7 @@ "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz" integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== dependencies: "@babel/helper-validator-identifier" "^7.14.5" @@ -211,17 +211,17 @@ "@babel/parser@7.12.16": version "7.12.16" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.16.tgz#cc31257419d2c3189d394081635703f549fc1ed4" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz" integrity sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw== "@babel/parser@^7.0.0", "@babel/parser@^7.12.13", "@babel/parser@^7.14.5", "@babel/parser@^7.14.6", "@babel/parser@^7.14.7": version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.14.7.tgz" integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== "@babel/plugin-proposal-class-properties@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz" integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== dependencies: "@babel/helper-create-class-features-plugin" "^7.14.5" @@ -229,7 +229,7 @@ "@babel/plugin-proposal-object-rest-spread@^7.0.0": version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz#5920a2b3df7f7901df0205974c0641b13fd9d363" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz" integrity sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g== dependencies: "@babel/compat-data" "^7.14.7" @@ -240,56 +240,56 @@ "@babel/plugin-syntax-class-properties@^7.0.0": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz#2ff654999497d7d7d142493260005263731da180" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.14.5.tgz" integrity sha512-9WK5ZwKCdWHxVuU13XNT6X73FGmutAXeor5lGFq6qhOFtMFUF4jkbijuyUdZZlpYq6E2hZeZf/u3959X9wsv0Q== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz" integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz" integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz" integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-block-scoping@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz#8cc63e61e50f42e078e6f09be775a75f23ef9939" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz" integrity sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-classes@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz#0e98e82097b38550b03b483f9b51a78de0acb2cf" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz" integrity sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA== dependencies: "@babel/helper-annotate-as-pure" "^7.14.5" @@ -302,21 +302,21 @@ "@babel/plugin-transform-computed-properties@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz" integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-destructuring@^7.0.0": version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz#0ad58ed37e23e22084d109f185260835e5557576" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz" integrity sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-flow-strip-types@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.14.5.tgz#0dc9c1d11dcdc873417903d6df4bed019ef0f85e" + resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.14.5.tgz" integrity sha512-KhcolBKfXbvjwI3TV7r7TkYm8oNXHNBqGOy6JDVwtecFaRoKYsUUqJdS10q0YDKW1c6aZQgO+Ys3LfGkox8pXA== dependencies: "@babel/helper-plugin-utils" "^7.14.5" @@ -324,14 +324,14 @@ "@babel/plugin-transform-for-of@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz#dae384613de8f77c196a8869cbf602a44f7fc0eb" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz" integrity sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-function-name@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz" integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ== dependencies: "@babel/helper-function-name" "^7.14.5" @@ -339,21 +339,21 @@ "@babel/plugin-transform-literals@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz" integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-member-expression-literals@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz" integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-modules-commonjs@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz" integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A== dependencies: "@babel/helper-module-transforms" "^7.14.5" @@ -363,7 +363,7 @@ "@babel/plugin-transform-object-super@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz" integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" @@ -371,28 +371,28 @@ "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz#49662e86a1f3ddccac6363a7dfb1ff0a158afeb3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz" integrity sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-property-literals@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz" integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-react-display-name@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.5.tgz#baa92d15c4570411301a85a74c13534873885b65" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.5.tgz" integrity sha512-07aqY1ChoPgIxsuDviptRpVkWCSbXWmzQqcgy65C6YSFOfPFvb/DX3bBRHh7pCd/PMEEYHYWUTSVkCbkVainYQ== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-react-jsx@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.14.5.tgz#39749f0ee1efd8a1bd729152cf5f78f1d247a44a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.14.5.tgz" integrity sha512-7RylxNeDnxc1OleDm0F5Q/BSL+whYRbOAR+bwgCxIr0L32v7UFh/pz1DLMZideAUxKT6eMoS2zQH6fyODLEi8Q== dependencies: "@babel/helper-annotate-as-pure" "^7.14.5" @@ -403,14 +403,14 @@ "@babel/plugin-transform-shorthand-properties@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz" integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-spread@^7.0.0": version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz" integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== dependencies: "@babel/helper-plugin-utils" "^7.14.5" @@ -418,35 +418,35 @@ "@babel/plugin-transform-template-literals@^7.0.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz" integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/runtime@7.12.5": version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" "@babel/runtime@7.4.5": version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz" integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== dependencies: regenerator-runtime "^0.13.2" "@babel/runtime@^7.0.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.14.0": version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz" integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== dependencies: regenerator-runtime "^0.13.4" "@babel/template@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz" integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== dependencies: "@babel/code-frame" "^7.14.5" @@ -455,7 +455,7 @@ "@babel/traverse@7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.13.tgz#689f0e4b4c08587ad26622832632735fb8c4e0c0" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz" integrity sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA== dependencies: "@babel/code-frame" "^7.12.13" @@ -470,7 +470,7 @@ "@babel/traverse@^7.0.0", "@babel/traverse@^7.14.5": version "7.14.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.7.tgz" integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== dependencies: "@babel/code-frame" "^7.14.5" @@ -485,7 +485,7 @@ "@babel/types@7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz" integrity sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ== dependencies: "@babel/helper-validator-identifier" "^7.12.11" @@ -494,7 +494,7 @@ "@babel/types@7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz" integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== dependencies: esutils "^2.0.2" @@ -503,7 +503,7 @@ "@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz" integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== dependencies: "@babel/helper-validator-identifier" "^7.14.5" @@ -511,12 +511,12 @@ "@csstools/convert-colors@^1.4.0": version "1.4.0" - resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" + resolved "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz" integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== "@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2": version "3.0.2" - resolved "https://registry.yarnpkg.com/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz#eea4635828dde372838b0909693ebd9aafeec22d" + resolved "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz" integrity sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA== dependencies: lodash.get "^4" @@ -526,14 +526,14 @@ "@fullhuman/postcss-purgecss@^4.0.3": version "4.0.3" - resolved "https://registry.yarnpkg.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-4.0.3.tgz#55d71712ec1c7a88e0d1ba5f10ce7fb6aa05beb4" + resolved "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-4.0.3.tgz" integrity sha512-/EnQ9UDWGGqHkn1UKAwSgh+gJHPKmD+Z+5dQ4gWT4qq2NUyez3zqAfZNwFH3eSgmgO+wjTXfhlLchx2M9/K+7Q== dependencies: purgecss "^4.0.3" "@graphql-codegen/cli@^1.21.5": version "1.21.5" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-1.21.5.tgz#b9041553747cfb2dee7c3473a2e2461ec3e7ada5" + resolved "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-1.21.5.tgz" integrity sha512-w3SovNJ9qtMhFLAdPZeCdGvHXDgfdb53mueWDTyncOt04m+tohVnY4qExvyKLTN5zlGxrA/5ubp2x8Az0xQarA== dependencies: "@graphql-codegen/core" "1.17.10" @@ -578,7 +578,7 @@ "@graphql-codegen/core@1.17.10": version "1.17.10" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-1.17.10.tgz#3b85b5bc2e84fcacbd25fced5af47a4bb2d7a8bd" + resolved "https://registry.npmjs.org/@graphql-codegen/core/-/core-1.17.10.tgz" integrity sha512-RA3umgVDs/RI/+ztHh+H4GfJxrJUfWJQqoAkMfX4qPTVO5qsy3R4vPudE0oP8w+kFbL8dFsRfAAPUZxI4jV/hQ== dependencies: "@graphql-codegen/plugin-helpers" "^1.18.7" @@ -588,7 +588,7 @@ "@graphql-codegen/plugin-helpers@^1.18.7": version "1.18.7" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.7.tgz#465af3e5b02de89e49ddc76ad2546b880fe240f2" + resolved "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.7.tgz" integrity sha512-8ICOrXlsvyL1dpVz8C9b7H31d4DJpDd75WfjMn6Xjqz81Ah8xDn1Bi+7YXRCCILCBmvI94k6fi8qpsIVhFBBjQ== dependencies: "@graphql-tools/utils" "^7.9.1" @@ -599,7 +599,7 @@ "@graphql-codegen/schema-ast@^1.18.3": version "1.18.3" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-1.18.3.tgz#7ba7422df716ff2038b1281c503e5751a8414ef2" + resolved "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-1.18.3.tgz" integrity sha512-D0uheH039ztSG3mboW5enmyaFwTcevLSR8yNrdN+NEKoQJJoDWsb9P/G6NTdFu5Bb03IvNhIFTpG1ttWtRP/aQ== dependencies: "@graphql-codegen/plugin-helpers" "^1.18.7" @@ -608,7 +608,7 @@ "@graphql-codegen/typescript-operations@^1.18.1": version "1.18.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-1.18.2.tgz#c1cba14eaf7584a875a63035f97b07fb232bcbae" + resolved "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-1.18.2.tgz" integrity sha512-AF9OCNBq0HuW3C5nsO11+53fgFGE40lNUtjSIJocvMcstEKvHx4GrzYO0XIpZsjRPrnyds00Y5xTSynLqB0XxA== dependencies: "@graphql-codegen/plugin-helpers" "^1.18.7" @@ -619,7 +619,7 @@ "@graphql-codegen/typescript@^1.22.2", "@graphql-codegen/typescript@^1.22.3": version "1.22.3" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.22.3.tgz#aaa85246974d74a9f544a950ae1611facabea7e6" + resolved "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-1.22.3.tgz" integrity sha512-qLSnVN2g/UxxzhRWHZcHw/Xkvx5wZh0RDzmg9MjAlPnDwAI89jg/ljKDwtTOfN+F6M8W4gQ9mjkWd6NxBQRgXw== dependencies: "@graphql-codegen/plugin-helpers" "^1.18.7" @@ -629,7 +629,7 @@ "@graphql-codegen/visitor-plugin-common@1.21.2": version "1.21.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.21.2.tgz#c72f1f47bee2ba03ceb48abf14e2cb82d9184b32" + resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.21.2.tgz" integrity sha512-Bb5P2Hw7f+lNWfazjcGwVcX434stNd7/EhgA+S2Ro0Bn1xVCK/WL0IHT2TGb+pf6/lWg3Y+J9wo2aOKkqDHT6A== dependencies: "@graphql-codegen/plugin-helpers" "^1.18.7" @@ -645,7 +645,7 @@ "@graphql-tools/apollo-engine-loader@^6.2.5": version "6.2.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-6.2.5.tgz#b9e65744f522bb9f6ca50651e5622820c4f059a8" + resolved "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-6.2.5.tgz" integrity sha512-CE4uef6PyxtSG+7OnLklIr2BZZDgjO89ZXK47EKdY7jQy/BQD/9o+8SxPsgiBc+2NsDJH2I6P/nqoaJMOEat6g== dependencies: "@graphql-tools/utils" "^7.0.0" @@ -654,7 +654,7 @@ "@graphql-tools/batch-execute@^7.1.2": version "7.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-7.1.2.tgz#35ba09a1e0f80f34f1ce111d23c40f039d4403a0" + resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-7.1.2.tgz" integrity sha512-IuR2SB2MnC2ztA/XeTMTfWcA0Wy7ZH5u+nDkDNLAdX+AaSyDnsQS35sCmHqG0VOGTl7rzoyBWLCKGwSJplgtwg== dependencies: "@graphql-tools/utils" "^7.7.0" @@ -664,7 +664,7 @@ "@graphql-tools/code-file-loader@^6.3.1": version "6.3.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-6.3.1.tgz#42dfd4db5b968acdb453382f172ec684fa0c34ed" + resolved "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-6.3.1.tgz" integrity sha512-ZJimcm2ig+avgsEOWWVvAaxZrXXhiiSZyYYOJi0hk9wh5BxZcLUNKkTp6EFnZE/jmGUwuos3pIjUD3Hwi3Bwhg== dependencies: "@graphql-tools/graphql-tag-pluck" "^6.5.1" @@ -673,7 +673,7 @@ "@graphql-tools/delegate@^7.0.1", "@graphql-tools/delegate@^7.1.5": version "7.1.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-7.1.5.tgz#0b027819b7047eff29bacbd5032e34a3d64bd093" + resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-7.1.5.tgz" integrity sha512-bQu+hDd37e+FZ0CQGEEczmRSfQRnnXeUxI/0miDV+NV/zCbEdIJj5tYFNrKT03W6wgdqx8U06d8L23LxvGri/g== dependencies: "@ardatan/aggregate-error" "0.0.6" @@ -686,7 +686,7 @@ "@graphql-tools/git-loader@^6.2.6": version "6.2.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-6.2.6.tgz#c2226f4b8f51f1c05c9ab2649ba32d49c68cd077" + resolved "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-6.2.6.tgz" integrity sha512-ooQTt2CaG47vEYPP3CPD+nbA0F+FYQXfzrB1Y1ABN9K3d3O2RK3g8qwslzZaI8VJQthvKwt0A95ZeE4XxteYfw== dependencies: "@graphql-tools/graphql-tag-pluck" "^6.2.6" @@ -695,7 +695,7 @@ "@graphql-tools/github-loader@^6.2.5": version "6.2.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-6.2.5.tgz#460dff6f5bbaa26957a5ea3be4f452b89cc6a44b" + resolved "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-6.2.5.tgz" integrity sha512-DLuQmYeNNdPo8oWus8EePxWCfCAyUXPZ/p1PWqjrX/NGPyH2ZObdqtDAfRHztljt0F/qkBHbGHCEk2TKbRZTRw== dependencies: "@graphql-tools/graphql-tag-pluck" "^6.2.6" @@ -705,7 +705,7 @@ "@graphql-tools/graphql-file-loader@^6.0.0", "@graphql-tools/graphql-file-loader@^6.2.7": version "6.2.7" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-6.2.7.tgz#d3720f2c4f4bb90eb2a03a7869a780c61945e143" + resolved "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-6.2.7.tgz" integrity sha512-5k2SNz0W87tDcymhEMZMkd6/vs6QawDyjQXWtqkuLTBF3vxjxPD1I4dwHoxgWPIjjANhXybvulD7E+St/7s9TQ== dependencies: "@graphql-tools/import" "^6.2.6" @@ -714,7 +714,7 @@ "@graphql-tools/graphql-tag-pluck@^6.2.6", "@graphql-tools/graphql-tag-pluck@^6.5.1": version "6.5.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-6.5.1.tgz#5fb227dbb1e19f4b037792b50f646f16a2d4c686" + resolved "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-6.5.1.tgz" integrity sha512-7qkm82iFmcpb8M6/yRgzjShtW6Qu2OlCSZp8uatA3J0eMl87TxyJoUmL3M3UMMOSundAK8GmoyNVFUrueueV5Q== dependencies: "@babel/parser" "7.12.16" @@ -725,7 +725,7 @@ "@graphql-tools/import@^6.2.6": version "6.3.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.3.1.tgz#731c47ab6c6ac9f7994d75c76b6c2fa127d2d483" + resolved "https://registry.npmjs.org/@graphql-tools/import/-/import-6.3.1.tgz" integrity sha512-1szR19JI6WPibjYurMLdadHKZoG9C//8I/FZ0Dt4vJSbrMdVNp8WFxg4QnZrDeMG4MzZc90etsyF5ofKjcC+jw== dependencies: resolve-from "5.0.0" @@ -733,7 +733,7 @@ "@graphql-tools/json-file-loader@^6.0.0", "@graphql-tools/json-file-loader@^6.2.6": version "6.2.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-6.2.6.tgz#830482cfd3721a0799cbf2fe5b09959d9332739a" + resolved "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-6.2.6.tgz" integrity sha512-CnfwBSY5926zyb6fkDBHnlTblHnHI4hoBALFYXnrg0Ev4yWU8B04DZl/pBRUc459VNgO2x8/mxGIZj2hPJG1EA== dependencies: "@graphql-tools/utils" "^7.0.0" @@ -741,7 +741,7 @@ "@graphql-tools/load@^6.0.0", "@graphql-tools/load@^6.2.8": version "6.2.8" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-6.2.8.tgz#16900fb6e75e1d075cad8f7ea439b334feb0b96a" + resolved "https://registry.npmjs.org/@graphql-tools/load/-/load-6.2.8.tgz" integrity sha512-JpbyXOXd8fJXdBh2ta0Q4w8ia6uK5FHzrTNmcvYBvflFuWly2LDTk2abbSl81zKkzswQMEd2UIYghXELRg8eTA== dependencies: "@graphql-tools/merge" "^6.2.12" @@ -756,7 +756,7 @@ "@graphql-tools/merge@^6.0.0", "@graphql-tools/merge@^6.2.12", "@graphql-tools/merge@^6.2.14": version "6.2.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-6.2.14.tgz#694e2a2785ba47558e5665687feddd2935e9d94e" + resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-6.2.14.tgz" integrity sha512-RWT4Td0ROJai2eR66NHejgf8UwnXJqZxXgDWDI+7hua5vNA2OW8Mf9K1Wav1ZkjWnuRp4ztNtkZGie5ISw55ow== dependencies: "@graphql-tools/schema" "^7.0.0" @@ -765,14 +765,14 @@ "@graphql-tools/optimize@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.0.1.tgz#9933fffc5a3c63f95102b1cb6076fb16ac7bb22d" + resolved "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-1.0.1.tgz" integrity sha512-cRlUNsbErYoBtzzS6zXahXeTBZGPVlPHXCpnEZ0XiK/KY/sQL96cyzak0fM/Gk6qEI9/l32MYEICjasiBQrl5w== dependencies: tslib "~2.0.1" "@graphql-tools/prisma-loader@^6.3.0": version "6.3.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-6.3.0.tgz#c907e17751ff2b26e7c2bc75d0913ebf03f970da" + resolved "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-6.3.0.tgz" integrity sha512-9V3W/kzsFBmUQqOsd96V4a4k7Didz66yh/IK89B1/rrvy9rYj+ULjEqR73x9BYZ+ww9FV8yP8LasWAJwWaqqJQ== dependencies: "@graphql-tools/url-loader" "^6.8.2" @@ -799,7 +799,7 @@ "@graphql-tools/relay-operation-optimizer@^6.3.0": version "6.3.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz#f8c7f6c8aa4a9cf50ab151fbc5db4f4282a79532" + resolved "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz" integrity sha512-Or3UgRvkY9Fq1AAx7q38oPqFmTepLz7kp6wDHKyR0ceG7AvHv5En22R12mAeISInbhff4Rpwgf6cE8zHRu6bCw== dependencies: "@graphql-tools/utils" "^7.1.0" @@ -808,7 +808,7 @@ "@graphql-tools/schema@^7.0.0", "@graphql-tools/schema@^7.1.5": version "7.1.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-7.1.5.tgz#07b24e52b182e736a6b77c829fc48b84d89aa711" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-7.1.5.tgz" integrity sha512-uyn3HSNSckf4mvQSq0Q07CPaVZMNFCYEVxroApOaw802m9DcZPgf9XVPy/gda5GWj9AhbijfRYVTZQgHnJ4CXA== dependencies: "@graphql-tools/utils" "^7.1.2" @@ -817,7 +817,7 @@ "@graphql-tools/url-loader@^6.0.0", "@graphql-tools/url-loader@^6.10.1", "@graphql-tools/url-loader@^6.8.2": version "6.10.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-6.10.1.tgz#dc741e4299e0e7ddf435eba50a1f713b3e763b33" + resolved "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-6.10.1.tgz" integrity sha512-DSDrbhQIv7fheQ60pfDpGD256ixUQIR6Hhf9Z5bRjVkXOCvO5XrkwoWLiU7iHL81GB1r0Ba31bf+sl+D4nyyfw== dependencies: "@graphql-tools/delegate" "^7.0.1" @@ -842,7 +842,7 @@ "@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.1.0", "@graphql-tools/utils@^7.1.2", "@graphql-tools/utils@^7.5.0", "@graphql-tools/utils@^7.7.0", "@graphql-tools/utils@^7.7.1", "@graphql-tools/utils@^7.8.1", "@graphql-tools/utils@^7.9.0", "@graphql-tools/utils@^7.9.1": version "7.10.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-7.10.0.tgz#07a4cb5d1bec1ff1dc1d47a935919ee6abd38699" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.10.0.tgz" integrity sha512-d334r6bo9mxdSqZW6zWboEnnOOFRrAPVQJ7LkU8/6grglrbcu6WhwCLzHb90E94JI3TD3ricC3YGbUqIi9Xg0w== dependencies: "@ardatan/aggregate-error" "0.0.6" @@ -851,7 +851,7 @@ "@graphql-tools/wrap@^7.0.4": version "7.0.8" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-7.0.8.tgz#ad41e487135ca3ea1ae0ea04bb3f596177fb4f50" + resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-7.0.8.tgz" integrity sha512-1NDUymworsOlb53Qfh7fonDi2STvqCtbeE68ntKY9K/Ju/be2ZNxrFSbrBHwnxWcN9PjISNnLcAyJ1L5tCUyhg== dependencies: "@graphql-tools/delegate" "^7.1.5" @@ -862,7 +862,7 @@ "@hapi/accept@5.0.2": version "5.0.2" - resolved "https://registry.yarnpkg.com/@hapi/accept/-/accept-5.0.2.tgz#ab7043b037e68b722f93f376afb05e85c0699523" + resolved "https://registry.npmjs.org/@hapi/accept/-/accept-5.0.2.tgz" integrity sha512-CmzBx/bXUR8451fnZRuZAJRlzgm0Jgu5dltTX/bszmR2lheb9BpyN47Q1RbaGTsvFzn0PXAEs+lXDKfshccYZw== dependencies: "@hapi/boom" "9.x.x" @@ -870,46 +870,46 @@ "@hapi/boom@9.x.x": version "9.1.2" - resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.2.tgz#48bd41d67437164a2d636e3b5bc954f8c8dc5e38" + resolved "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.2.tgz" integrity sha512-uJEJtiNHzKw80JpngDGBCGAmWjBtzxDCz17A9NO2zCi8LLBlb5Frpq4pXwyN+2JQMod4pKz5BALwyneCgDg89Q== dependencies: "@hapi/hoek" "9.x.x" "@hapi/hoek@9.x.x": version "9.2.0" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" + resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz" integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== "@iarna/toml@^2.2.5": version "2.2.5" - resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" + resolved "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== "@microsoft/fetch-event-source@2.0.1": version "2.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d" + resolved "https://registry.npmjs.org/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz" integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA== "@next/bundle-analyzer@^10.2.3": version "10.2.3" - resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-10.2.3.tgz#6526e31f46cd48145986dc3bf911ff693e2acdf7" + resolved "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-10.2.3.tgz" integrity sha512-vEfQhGWgJugZOlSUlj3DZWs/KsK0SO2SPKoHSZ7KkzpruKzc/e45G0oUh0rffzdhasMQZM1TuSBkxO+1UcnDNw== dependencies: webpack-bundle-analyzer "4.3.0" "@next/env@11.0.1": version "11.0.1" - resolved "https://registry.yarnpkg.com/@next/env/-/env-11.0.1.tgz#6dc96ac76f1663ab747340e907e8933f190cc8fd" + resolved "https://registry.npmjs.org/@next/env/-/env-11.0.1.tgz" integrity sha512-yZfKh2U6R9tEYyNUrs2V3SBvCMufkJ07xMH5uWy8wqcl5gAXoEw6A/1LDqwX3j7pUutF9d1ZxpdGDA3Uag+aQQ== "@next/polyfill-module@11.0.1": version "11.0.1" - resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-11.0.1.tgz#ca2a110c1c44672cbcff6c2b983f0c0549d87291" + resolved "https://registry.npmjs.org/@next/polyfill-module/-/polyfill-module-11.0.1.tgz" integrity sha512-Cjs7rrKCg4CF4Jhri8PCKlBXhszTfOQNl9AjzdNy4K5jXFyxyoSzuX2rK4IuoyE+yGp5A3XJCBEmOQ4xbUp9Mg== "@next/react-dev-overlay@11.0.1": version "11.0.1" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-11.0.1.tgz#3c481e83347255abd466dcf7e59ac8a79a0d7fd6" + resolved "https://registry.npmjs.org/@next/react-dev-overlay/-/react-dev-overlay-11.0.1.tgz" integrity sha512-lvUjMVpLsgzADs9Q8wtC5LNqvfdN+M0BDMSrqr04EDWAyyX0vURHC9hkvLbyEYWyh+WW32pwjKBXdkMnJhoqMg== dependencies: "@babel/code-frame" "7.12.11" @@ -926,12 +926,12 @@ "@next/react-refresh-utils@11.0.1": version "11.0.1" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-11.0.1.tgz#a7509f22b6f70c13101a26573afd295295f1c020" + resolved "https://registry.npmjs.org/@next/react-refresh-utils/-/react-refresh-utils-11.0.1.tgz" integrity sha512-K347DM6Z7gBSE+TfUaTTceWvbj0B6iNAsFZXbFZOlfg3uyz2sbKpzPYYFocCc27yjLaS8OfR8DEdS2mZXi8Saw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -939,12 +939,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.7" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz#94c23db18ee4653e129abd26fb06f870ac9e1ee2" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz" integrity sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -952,12 +952,12 @@ "@polka/url@^1.0.0-next.15": version "1.0.0-next.15" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.15.tgz#6a9d143f7f4f49db2d782f9e1c8839a29b43ae23" + resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.15.tgz" integrity sha512-15spi3V28QdevleWBNXE4pIls3nFZmBbUGrW9IVPwiQczuSb9n76TCB4bsk8TSel+I1OkHEdPhu5QKMfY6rQHA== "@react-spring/animated@~9.2.0": version "9.2.3" - resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.2.3.tgz#271226099d356b7b6d99240375dd8f62f450e997" + resolved "https://registry.npmjs.org/@react-spring/animated/-/animated-9.2.3.tgz" integrity sha512-xxYOxxrk4r+yy218lVnkR027GXGvHcadDnnXRW0l6atcL+1AGYwimMwIuvzlvnsVnyoK0YUABEeGjk9ENOrVMQ== dependencies: "@react-spring/shared" "~9.2.0" @@ -965,7 +965,7 @@ "@react-spring/core@~9.2.0": version "9.2.3" - resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.2.3.tgz#b3926ef8f2694bfb251318d7b44abac1fa5321d0" + resolved "https://registry.npmjs.org/@react-spring/core/-/core-9.2.3.tgz" integrity sha512-8qJbj0xjjoYGVqdmNd2bVaFzKSAwrMVLweHkaYJiTY6p0g7LhSdt5KSl1MjYA4Rj6S4wO1KgefAPK6mH6MtGtA== dependencies: "@react-spring/animated" "~9.2.0" @@ -974,12 +974,12 @@ "@react-spring/rafz@~9.2.0": version "9.2.3" - resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.2.3.tgz#ffde337240b6880a2488d3ca67e4b01688881793" + resolved "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.2.3.tgz" integrity sha512-ThBI3HWp1Y82uRSFVpoykwgM3M9s3SJD6ilKKp9C2OTPcGhWiRGt1IMjzKPwB+F1NX3psbPTt30Bev8WzA/DQQ== "@react-spring/shared@~9.2.0": version "9.2.3" - resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.2.3.tgz#eaa4a4e6bf4f43d10b4a5c920a917e84047ac50e" + resolved "https://registry.npmjs.org/@react-spring/shared/-/shared-9.2.3.tgz" integrity sha512-MehdmKao1oUAwSEJo8BXOz1OGgsSav/dimD1Vz920hirShj9s/I4zKnWtkdK92xQ4n35D/xD3hATHkXbt/WSvg== dependencies: "@react-spring/rafz" "~9.2.0" @@ -987,12 +987,12 @@ "@react-spring/types@~9.2.0": version "9.2.3" - resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.2.3.tgz#08b5ca1cb262328529ee558796395f073b6029b0" + resolved "https://registry.npmjs.org/@react-spring/types/-/types-9.2.3.tgz" integrity sha512-G7AWUJavwsvvvprnYmqUXE5N1XKINktc8V72ipvkFTE3DD3GApYpX/ORNmieJljKJYvBSBzpRSIzk2qELUs30Q== "@react-spring/web@^9.2.1": version "9.2.3" - resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.2.3.tgz#b9037719b489863ca2ad39caaacff6d315bbaea8" + resolved "https://registry.npmjs.org/@react-spring/web/-/web-9.2.3.tgz" integrity sha512-dWRcgVDbO2UI9I03n/HVmCx9tY++Na+RwRzkzXv3E53BcFsjvnWGArnpj+xE/XgXiaII3ep2RmUj5jyYoukqGg== dependencies: "@react-spring/animated" "~9.2.0" @@ -1002,133 +1002,142 @@ "@samverschueren/stream-to-observable@^0.3.0": version "0.3.1" - resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" + resolved "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz" integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ== dependencies: any-observable "^0.3.0" "@sindresorhus/is@^0.14.0": version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@spree/storefront-api-v2-sdk@^4.5.4": + version "4.5.4" + resolved "https://registry.yarnpkg.com/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-4.5.4.tgz#ad0a414139064df3c5ec7c85147597c8fa716f35" + integrity sha512-GlpCvOhOmM0X1Of8rQaBU/HuX9FKX0ZkO+EmTfucC//MDgwuxkUt9vHpv+JRPLdR8+88AA+CwwSVtLIGcLo9yQ== + dependencies: + axios "^0.21.1" + lodash "^4.17.21" + qs "^6.10.1" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== dependencies: defer-to-connect "^1.0.1" "@tootallnate/once@1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@types/async-retry@1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@types/async-retry/-/async-retry-1.2.1.tgz#fa9ac165907a8ee78f4924f4e393b656c65b5bb4" + resolved "https://registry.npmjs.org/@types/async-retry/-/async-retry-1.2.1.tgz" integrity sha512-yMQ6CVgICWtyFNBqJT3zqOc+TnqqEPLo4nKJNPFwcialiylil38Ie6q1ENeFTjvaLOkVim9K5LisHgAKJWidGQ== "@types/body-scroll-lock@^2.6.1": version "2.6.1" - resolved "https://registry.yarnpkg.com/@types/body-scroll-lock/-/body-scroll-lock-2.6.1.tgz#0dbd2b6ad2f4cfcece7102d6cf8630ce95508ee0" + resolved "https://registry.npmjs.org/@types/body-scroll-lock/-/body-scroll-lock-2.6.1.tgz" integrity sha512-PPFm/2A6LfKmSpvMg58gHtSqwwMChbcKKGhSCRIhY4MyFzhY8moAN6HrTCpOeZQUqkFdTFfMqr7njeqGLKt72Q== "@types/cookie@^0.4.0": version "0.4.0" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" + resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz" integrity sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg== "@types/http-proxy-agent@^2.0.2": version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/http-proxy-agent/-/http-proxy-agent-2.0.2.tgz#942c1f35c7e1f0edd1b6ffae5d0f9051cfb32be1" + resolved "https://registry.npmjs.org/@types/http-proxy-agent/-/http-proxy-agent-2.0.2.tgz" integrity sha512-2S6IuBRhqUnH1/AUx9k8KWtY3Esg4eqri946MnxTG5HwehF1S5mqLln8fcyMiuQkY72p2gH3W+rIPqp5li0LyQ== dependencies: "@types/node" "*" "@types/js-cookie@^2.2.6": version "2.2.6" - resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f" + resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz" integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw== "@types/js-yaml@^4.0.0": version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.1.tgz#5544730b65a480b18ace6b6ce914e519cec2d43b" + resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.1.tgz" integrity sha512-xdOvNmXmrZqqPy3kuCQ+fz6wA0xU5pji9cd1nDrflWaAWtYLLGk5ykW0H6yg5TVyehHP1pfmuuSaZkhP+kspVA== "@types/json-stable-stringify@^1.0.32": version "1.0.32" - resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz#121f6917c4389db3923640b2e68de5fa64dda88e" + resolved "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz" integrity sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw== "@types/jsonwebtoken@^8.5.0": version "8.5.2" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.2.tgz#eb71c717b3b8681bb85fbd2950c9c4c5d4506748" + resolved "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.2.tgz" integrity sha512-X8BOCkp+WJVNYCYIBugREtVZa4Y09Or9HDx6xqRZem5F8jJV8FuJgNessXyMuv9+U8pjnvdezASwU28uw+1scw== dependencies: "@types/node" "*" "@types/lodash.debounce@^4.0.6": version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz#c5a2326cd3efc46566c47e4c0aa248dc0ee57d60" + resolved "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz" integrity sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ== dependencies: "@types/lodash" "*" "@types/lodash.random@^3.2.6": version "3.2.6" - resolved "https://registry.yarnpkg.com/@types/lodash.random/-/lodash.random-3.2.6.tgz#64b08abad168dca39c778ed40cce75b2f9e168eb" + resolved "https://registry.npmjs.org/@types/lodash.random/-/lodash.random-3.2.6.tgz" integrity sha512-RRr0pKm+3USvG/HTkuRKA8v2EqXu19VXC09j4VL2UQec8Yx8Fn6wYTPGjYdmX4UFd23ykS7SLFkiULS/rv8kTA== dependencies: "@types/lodash" "*" "@types/lodash.throttle@^4.1.6": version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/lodash.throttle/-/lodash.throttle-4.1.6.tgz#f5ba2c22244ee42ff6c2c49e614401a870c1009c" + resolved "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.6.tgz" integrity sha512-/UIH96i/sIRYGC60NoY72jGkCJtFN5KVPhEMMMTjol65effe1gPn0tycJqV5tlSwMTzX8FqzB5yAj0rfGHTPNg== dependencies: "@types/lodash" "*" "@types/lodash@*": version "4.14.170" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz" integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q== "@types/lru-cache@4.1.1": version "4.1.1" - resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-4.1.1.tgz#b2d87a5e3df8d4b18ca426c5105cd701c2306d40" + resolved "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-4.1.1.tgz" integrity sha512-8mNEUG6diOrI6pMqOHrHPDBB1JsrpedeMK9AWGzVCQ7StRRribiT9BRvUmF8aUws9iBbVlgVekOT5Sgzc1MTKw== "@types/node-fetch@2.3.2": version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.3.2.tgz#e01893b176c6fa1367743726380d65bce5d6576b" + resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.3.2.tgz" integrity sha512-yW0EOebSsQme9yKu09XbdDfle4/SmWZMK4dfteWcSLCYNQQcF+YOv0kIrvm+9pO11/ghA4E6A+RNQqvYj4Nr3A== dependencies: "@types/node" "*" "@types/node@*", "@types/node@^15.12.4": version "15.12.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.4.tgz#e1cf817d70a1e118e81922c4ff6683ce9d422e26" + resolved "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz" integrity sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA== "@types/node@10.12.18": version "10.12.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" + resolved "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz" integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prop-types@*": version "15.7.3" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== "@types/react@^17.0.8": version "17.0.11" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.11.tgz#67fcd0ddbf5a0b083a0f94e926c7d63f3b836451" + resolved "https://registry.npmjs.org/@types/react/-/react-17.0.11.tgz" integrity sha512-yFRQbD+whVonItSk7ZzP/L+gPTJVBkL/7shLEF+i9GC/1cV3JmUxEQz6+9ylhUpWSDuqo1N9qEvqS6vTj4USUA== dependencies: "@types/prop-types" "*" @@ -1137,19 +1146,24 @@ "@types/scheduler@*": version "0.16.1" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" + resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz" integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== +"@types/uuid@8.3.1": + version "8.3.1" + resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz" + integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== + "@types/websocket@1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.2.tgz#d2855c6a312b7da73ed16ba6781815bf30c6187a" + resolved "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.2.tgz" integrity sha512-B5m9aq7cbbD/5/jThEr33nUY8WEfVi6A2YKCTOvw5Ldy7mtsOkqRvGjnzy6g7iMMDsgu7xREuCzqATLDLQVKcQ== dependencies: "@types/node" "*" "@vercel/fetch-cached-dns@^2.0.2": version "2.0.2" - resolved "https://registry.yarnpkg.com/@vercel/fetch-cached-dns/-/fetch-cached-dns-2.0.2.tgz#975c395cf53f188fa618fad57f27af6b5ffc5bab" + resolved "https://registry.npmjs.org/@vercel/fetch-cached-dns/-/fetch-cached-dns-2.0.2.tgz" integrity sha512-gDqKEV8CeY2YmCdZpP1rn3tFK1L07Vw2+HYkCK8zpRHOVGr/sP8yhBsW+C/yqGVj0i9z/rIvqIHe5emvRvxwgw== dependencies: "@types/node-fetch" "2.3.2" @@ -1157,7 +1171,7 @@ "@vercel/fetch-retry@^5.0.2": version "5.0.3" - resolved "https://registry.yarnpkg.com/@vercel/fetch-retry/-/fetch-retry-5.0.3.tgz#cce5d23f6e64f6f525c24e2ac7c78f65d6c5b1f4" + resolved "https://registry.npmjs.org/@vercel/fetch-retry/-/fetch-retry-5.0.3.tgz" integrity sha512-DIIoBY92r+sQ6iHSf5WjKiYvkdsDIMPWKYATlE0KcUAj2RV6SZK9UWpUzBRKsofXqedOqpVjrI0IE6AWL7JRtg== dependencies: async-retry "^1.3.1" @@ -1165,7 +1179,7 @@ "@vercel/fetch@^6.1.0": version "6.1.1" - resolved "https://registry.yarnpkg.com/@vercel/fetch/-/fetch-6.1.1.tgz#6564bfe9050f871842cf4834bb0117eaafd3915e" + resolved "https://registry.npmjs.org/@vercel/fetch/-/fetch-6.1.1.tgz" integrity sha512-nddCkgpA0aVIqOlzh+qVlzDNcQq0cSnqefM+x6SciGI4GCvVZeaZ7WEowgX8I/HwBAq8Uj5Bdnd+r0+sYsJsig== dependencies: "@types/async-retry" "1.2.1" @@ -1176,7 +1190,7 @@ "@zeit/dns-cached-resolve@2.1.2": version "2.1.2" - resolved "https://registry.yarnpkg.com/@zeit/dns-cached-resolve/-/dns-cached-resolve-2.1.2.tgz#2c2e33d682d67f94341c9a06ac0e2a8f14ff035f" + resolved "https://registry.npmjs.org/@zeit/dns-cached-resolve/-/dns-cached-resolve-2.1.2.tgz" integrity sha512-A/5gbBskKPETTBqHwvlaW1Ri2orO62yqoFoXdxna1SQ7A/lXjpWgpJ1wdY3IQEcz5LydpS4sJ8SzI2gFyyLEhg== dependencies: "@types/async-retry" "1.2.1" @@ -1187,14 +1201,14 @@ abort-controller@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: event-target-shim "^5.0.0" acorn-node@^1.6.1: version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" + resolved "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz" integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== dependencies: acorn "^7.0.0" @@ -1203,41 +1217,41 @@ acorn-node@^1.6.1: acorn-walk@^7.0.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== acorn-walk@^8.0.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.1.0.tgz#d3c6a9faf00987a5e2b9bdb506c2aa76cd707f83" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.0.tgz" integrity sha512-mjmzmv12YIG/G8JQdQuz2MUDShEJ6teYpT5bmWA4q7iwoGen8xtt3twF3OvzIUl+Q06aWIjvnwQUKvQ6TtMRjg== acorn@^7.0.0: version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.0.4: version "8.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.0.tgz#af53266e698d7cffa416714b503066a82221be60" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.4.0.tgz" integrity sha512-ULr0LDaEqQrMFGyQ3bhJkLsbtrQ8QibAseGZeaSUiT/6zb9IvIkomWHJIvgvwad+hinRAgsI51JcWk2yvwyL+w== agent-base@6: version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" agentkeepalive@3.4.1: version "3.4.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.4.1.tgz#aa95aebc3a749bca5ed53e3880a09f5235b48f0c" + resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.4.1.tgz" integrity sha512-MPIwsZU9PP9kOrZpyu2042kYA8Fdt/AedQYkYXucHgF9QoD9dXVp0ypuGnHXSR0hTstBxdt85Xkh4JolYfK5wg== dependencies: humanize-ms "^1.2.1" aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" @@ -1245,68 +1259,68 @@ aggregate-error@^3.0.0: anser@1.4.9: version "1.4.9" - resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760" + resolved "https://registry.npmjs.org/anser/-/anser-1.4.9.tgz" integrity sha512-AI+BjTeGt2+WFk4eWcqbQ7snZpDBt8SaLlj0RT2h5xfdWaiy51OjYvqwMrNzJLGy8iOAL6nKDITWO+rd4MkYEA== ansi-colors@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-regex@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-regex@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== ansi-styles@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" any-observable@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" + resolved "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz" integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== anymatch@~3.1.1, anymatch@~3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" @@ -1314,22 +1328,22 @@ anymatch@~3.1.1, anymatch@~3.1.2: arg@^4.1.0: version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== arg@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.0.tgz#a20e2bb5710e82950a516b3f933fee5ed478be90" + resolved "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz" integrity sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ== argparse@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-includes-with-glob@^3.0.6: version "3.1.0" - resolved "https://registry.yarnpkg.com/array-includes-with-glob/-/array-includes-with-glob-3.1.0.tgz#f04e8172f231ab8261e52bfe9756b65c08b87ab9" + resolved "https://registry.npmjs.org/array-includes-with-glob/-/array-includes-with-glob-3.1.0.tgz" integrity sha512-/PZEKASyXWmUTkNhuxnmqybv1CmIdY5rp3axLy3Dv6SYfaBb+EgS7Nl991mquHT1N2u0YAnE3IOafVNRM6Y9dw== dependencies: "@babel/runtime" "^7.14.0" @@ -1337,12 +1351,12 @@ array-includes-with-glob@^3.0.6: array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array.prototype.flatmap@^1.2.4: version "1.2.4" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz" integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q== dependencies: call-bind "^1.0.0" @@ -1352,12 +1366,12 @@ array.prototype.flatmap@^1.2.4: asap@~2.0.3: version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1.js@^5.2.0: version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + resolved "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz" integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== dependencies: bn.js "^4.0.0" @@ -1367,7 +1381,7 @@ asn1.js@^5.2.0: assert@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" + resolved "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz" integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== dependencies: es6-object-assign "^1.1.0" @@ -1377,7 +1391,7 @@ assert@2.0.0: assert@^1.1.1: version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + resolved "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz" integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== dependencies: object-assign "^4.1.1" @@ -1385,41 +1399,41 @@ assert@^1.1.1: ast-types@0.13.2: version "0.13.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" + resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz" integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-retry@1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0" + resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz" integrity sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q== dependencies: retry "0.12.0" async-retry@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" + resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz" integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA== dependencies: retry "0.12.0" asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= auto-bind@~4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" + resolved "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== autoprefixer@^10.2.6: version "10.2.6" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.2.6.tgz#aadd9ec34e1c98d403e01950038049f0eb252949" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.6.tgz" integrity sha512-8lChSmdU6dCNMCQopIf4Pe5kipkAGj/fvTMslCsih0uHpOrXOPUEVOmYMMqmw3cekQkSD7EhIeuYl5y0BLdKqg== dependencies: browserslist "^4.16.6" @@ -1431,7 +1445,7 @@ autoprefixer@^10.2.6: autoprefixer@^9.6.1: version "9.8.6" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz" integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== dependencies: browserslist "^4.12.0" @@ -1444,29 +1458,36 @@ autoprefixer@^9.6.1: available-typed-arrays@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz" integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA== +axios@^0.21.1: + version "0.21.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" + integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== + dependencies: + follow-redirects "^1.10.0" + babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + resolved "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz" integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== dependencies: object.assign "^4.1.0" babel-plugin-syntax-jsx@6.18.0: version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" + resolved "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== babel-preset-fbjs@^3.3.0: version "3.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" + resolved "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz" integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== dependencies: "@babel/plugin-proposal-class-properties" "^7.0.0" @@ -1499,47 +1520,47 @@ babel-preset-fbjs@^3.3.0: backo2@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz" integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== big.js@^5.2.2: version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== bn.js@^5.0.0, bn.js@^5.1.1: version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== body-scroll-lock@^3.1.5: version "3.1.5" - resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz#c1392d9217ed2c3e237fee1e910f6cdd80b7aaec" + resolved "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz" integrity sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg== brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -1547,19 +1568,19 @@ brace-expansion@^1.1.7: braces@^3.0.1, braces@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz" integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== dependencies: buffer-xor "^1.0.3" @@ -1571,7 +1592,7 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4: browserify-cipher@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + resolved "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz" integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== dependencies: browserify-aes "^1.0.4" @@ -1580,7 +1601,7 @@ browserify-cipher@^1.0.0: browserify-des@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + resolved "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz" integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== dependencies: cipher-base "^1.0.1" @@ -1590,7 +1611,7 @@ browserify-des@^1.0.0: browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + resolved "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz" integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== dependencies: bn.js "^5.0.0" @@ -1598,7 +1619,7 @@ browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: browserify-sign@^4.0.0: version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + resolved "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz" integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== dependencies: bn.js "^5.1.1" @@ -1613,14 +1634,14 @@ browserify-sign@^4.0.0: browserify-zlib@0.2.0, browserify-zlib@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + resolved "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz" integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== dependencies: pako "~1.0.5" browserslist@4.16.6, browserslist@^4.12.0, browserslist@^4.16.6, browserslist@^4.6.4: version "4.16.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz" integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== dependencies: caniuse-lite "^1.0.30001219" @@ -1631,29 +1652,29 @@ browserslist@4.16.6, browserslist@^4.12.0, browserslist@^4.16.6, browserslist@^4 bser@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-equal-constant-time@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= buffer-from@^1.0.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== buffer-xor@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= buffer@5.6.0: version "5.6.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz" integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== dependencies: base64-js "^1.0.2" @@ -1661,7 +1682,7 @@ buffer@5.6.0: buffer@^4.3.0: version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz" integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== dependencies: base64-js "^1.0.2" @@ -1670,7 +1691,7 @@ buffer@^4.3.0: buffer@^5.7.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -1678,17 +1699,17 @@ buffer@^5.7.0: builtin-status-codes@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= bytes@3.1.0, bytes@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cacheable-request@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== dependencies: clone-response "^1.0.2" @@ -1701,7 +1722,7 @@ cacheable-request@^6.0.0: call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -1709,12 +1730,12 @@ call-bind@^1.0.0, call-bind@^1.0.2: callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camel-case@4.1.2, camel-case@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== dependencies: pascal-case "^3.1.2" @@ -1722,22 +1743,22 @@ camel-case@4.1.2, camel-case@^4.1.2: camelcase-css@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== camelcase@^5.0.0: version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001230: version "1.0.30001239" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz#66e8669985bb2cb84ccb10f68c25ce6dd3e4d2b8" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz" integrity sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ== capital-case@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + resolved "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz" integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== dependencies: no-case "^3.0.4" @@ -1746,7 +1767,7 @@ capital-case@^1.0.4: chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -1755,7 +1776,7 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: chalk@4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz" integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== dependencies: ansi-styles "^4.1.0" @@ -1763,7 +1784,7 @@ chalk@4.0.0: chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= dependencies: ansi-styles "^2.2.1" @@ -1774,7 +1795,7 @@ chalk@^1.0.0, chalk@^1.1.3: chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz" integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== dependencies: ansi-styles "^4.1.0" @@ -1782,7 +1803,7 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: change-case-all@1.0.14: version "1.0.14" - resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.14.tgz#bac04da08ad143278d0ac3dda7eccd39280bfba1" + resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz" integrity sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA== dependencies: change-case "^4.1.2" @@ -1798,7 +1819,7 @@ change-case-all@1.0.14: change-case@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" + resolved "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz" integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== dependencies: camel-case "^4.1.2" @@ -1816,12 +1837,12 @@ change-case@^4.1.2: chardet@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== chokidar@3.5.1: version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== dependencies: anymatch "~3.1.1" @@ -1836,7 +1857,7 @@ chokidar@3.5.1: chokidar@^3.5.1: version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz" integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== dependencies: anymatch "~3.1.2" @@ -1851,7 +1872,7 @@ chokidar@^3.5.1: cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz" integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== dependencies: inherits "^2.0.1" @@ -1859,36 +1880,36 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: classnames@2.2.6: version "2.2.6" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== classnames@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz" integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= dependencies: restore-cursor "^2.0.0" cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-truncate@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz" integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= dependencies: slice-ansi "0.0.4" @@ -1896,7 +1917,7 @@ cli-truncate@^0.2.1: cli-truncate@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== dependencies: slice-ansi "^3.0.0" @@ -1904,12 +1925,12 @@ cli-truncate@^2.1.0: cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== dependencies: string-width "^4.2.0" @@ -1918,7 +1939,7 @@ cliui@^6.0.0: cliui@^7.0.2: version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" @@ -1927,43 +1948,43 @@ cliui@^7.0.2: clone-response@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= dependencies: mimic-response "^1.0.0" code-point-at@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.5.4: version "1.5.5" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz" integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg== dependencies: color-name "^1.0.0" @@ -1971,7 +1992,7 @@ color-string@^1.5.4: color@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e" + resolved "https://registry.npmjs.org/color/-/color-3.1.3.tgz" integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== dependencies: color-convert "^1.9.1" @@ -1979,49 +2000,49 @@ color@^3.1.3: colorette@^1.2.1, colorette@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@^6.0.0, commander@^6.2.0: version "6.2.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== commander@^7.2.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== common-tags@1.8.0, common-tags@^1.8.0: version "1.8.0" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz" integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== commondir@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= console-browserify@^1.1.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + resolved "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== constant-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" + resolved "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz" integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== dependencies: no-case "^3.0.4" @@ -2030,43 +2051,43 @@ constant-case@^3.0.4: constants-browserify@1.0.0, constants-browserify@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= convert-source-map@1.7.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: safe-buffer "~5.1.1" convert-source-map@^1.7.0: version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" cookie@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== core-util-is@~1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cosmiconfig-toml-loader@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig-toml-loader/-/cosmiconfig-toml-loader-1.0.0.tgz#0681383651cceff918177debe9084c0d3769509b" + resolved "https://registry.npmjs.org/cosmiconfig-toml-loader/-/cosmiconfig-toml-loader-1.0.0.tgz" integrity sha512-H/2gurFWVi7xXvCyvsWRLCMekl4tITJcX0QEsDMpzxtuxDyM59xLatYNg4s/k9AA/HdtCYfj2su8mgA0GSDLDA== dependencies: "@iarna/toml" "^2.2.5" cosmiconfig@7.0.0, cosmiconfig@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz" integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== dependencies: "@types/parse-json" "^4.0.0" @@ -2077,7 +2098,7 @@ cosmiconfig@7.0.0, cosmiconfig@^7.0.0: create-ecdh@^4.0.0: version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz" integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== dependencies: bn.js "^4.1.0" @@ -2085,7 +2106,7 @@ create-ecdh@^4.0.0: create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== dependencies: cipher-base "^1.0.1" @@ -2096,7 +2117,7 @@ create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== dependencies: cipher-base "^1.0.3" @@ -2108,26 +2129,26 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@3.0.6: version "3.0.6" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz" integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== dependencies: node-fetch "2.6.1" cross-fetch@3.1.4, cross-fetch@^3.0.4, cross-fetch@^3.0.6: version "3.1.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz" integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== dependencies: node-fetch "2.6.1" cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -2136,7 +2157,7 @@ cross-spawn@^7.0.3: crypto-browserify@3.12.0, crypto-browserify@^3.11.0: version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz" integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== dependencies: browserify-cipher "^1.0.0" @@ -2153,14 +2174,14 @@ crypto-browserify@3.12.0, crypto-browserify@^3.11.0: css-blank-pseudo@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" + resolved "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz" integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== dependencies: postcss "^7.0.5" css-has-pseudo@^0.10.0: version "0.10.0" - resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee" + resolved "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz" integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== dependencies: postcss "^7.0.6" @@ -2168,170 +2189,170 @@ css-has-pseudo@^0.10.0: css-prefers-color-scheme@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4" + resolved "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz" integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== dependencies: postcss "^7.0.5" css-unit-converter@^1.1.1: version "1.1.2" - resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21" + resolved "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz" integrity sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA== css.escape@1.5.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz" integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= cssdb@^4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0" + resolved "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz" integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== cssesc@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz" integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== cssesc@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== cssnano-preset-simple@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-2.0.0.tgz#b55e72cb970713f425560a0e141b0335249e2f96" + resolved "https://registry.npmjs.org/cssnano-preset-simple/-/cssnano-preset-simple-2.0.0.tgz" integrity sha512-HkufSLkaBJbKBFx/7aj5HmCK9Ni/JedRQm0mT2qBzMG/dEuJOLnMt2lK6K1rwOOyV4j9aSY+knbW9WoS7BYpzg== dependencies: caniuse-lite "^1.0.30001202" cssnano-simple@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-2.0.0.tgz#930d9dcd8ba105c5a62ce719cb00854da58b5c05" + resolved "https://registry.npmjs.org/cssnano-simple/-/cssnano-simple-2.0.0.tgz" integrity sha512-0G3TXaFxlh/szPEG/o3VcmCwl0N3E60XNb9YZZijew5eIs6fLjJuOPxQd9yEBaX2p/YfJtt49i4vYi38iH6/6w== dependencies: cssnano-preset-simple "^2.0.0" csstype@^3.0.2: version "3.0.8" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz" integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== data-uri-to-buffer@3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" + resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== dataloader@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" + resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz" integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== date-fns@^1.27.2: version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== debounce@^1.2.0: version "1.2.1" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== debug@2: version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" debug@4, debug@^4.1.0, debug@^4.3.1: version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" debug@^3.1.0: version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" decamelize@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decompress-response@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= dependencies: mimic-response "^1.0.0" dedent@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= deep-extend@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deepmerge@4.2.2, deepmerge@^4.2.2: version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defer-to-connect@^1.0.1: version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== define-properties@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" defined@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + resolved "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= depd@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= dependency-graph@^0.11.0: version "0.11.0" - resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" + resolved "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz" integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== dequal@2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz" integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug== des.js@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + resolved "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz" integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== dependencies: inherits "^2.0.1" @@ -2339,12 +2360,12 @@ des.js@^1.0.0: detect-indent@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detective@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" + resolved "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz" integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== dependencies: acorn-node "^1.6.1" @@ -2353,17 +2374,17 @@ detective@^5.2.0: didyoumean@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff" + resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz" integrity sha1-6S7f2tplN9SE1zwBcv0eugxJdv8= diff@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== diffie-hellman@^5.0.0: version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== dependencies: bn.js "^4.1.0" @@ -2372,29 +2393,29 @@ diffie-hellman@^5.0.0: dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" dlv@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== domain-browser@4.19.0: version "4.19.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1" + resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-4.19.0.tgz" integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ== domain-browser@^1.1.1: version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== dot-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== dependencies: no-case "^3.0.4" @@ -2402,39 +2423,39 @@ dot-case@^3.0.4: dotenv@^8.2.0: version "8.6.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== duplexer3@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= duplexer@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== ecdsa-sig-formatter@1.0.11: version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== dependencies: safe-buffer "^5.0.1" electron-to-chromium@^1.3.723: version "1.3.755" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.755.tgz#4b6101f13de910cf3f0a1789ddc57328133b9332" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.755.tgz" integrity sha512-BJ1s/kuUuOeo1bF/EM2E4yqW9te0Hpof3wgwBx40AWJE18zsD1Tqo0kr7ijnOc+lRsrlrqKPauJAHqaxOItoUA== elegant-spinner@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + resolved "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz" integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= elliptic@^6.5.3: version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== dependencies: bn.js "^4.11.9" @@ -2447,50 +2468,50 @@ elliptic@^6.5.3: email-validator@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" + resolved "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz" integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emojis-list@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= encoding@0.1.13: version "0.1.13" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: iconv-lite "^0.6.2" end-of-stream@^1.1.0: version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" enquirer@^2.3.6: version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: version "1.18.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz" integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== dependencies: call-bind "^1.0.2" @@ -2512,7 +2533,7 @@ es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -2521,52 +2542,52 @@ es-to-primitive@^1.2.1: es6-object-assign@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + resolved "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= escalade@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@1.8.1: version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= event-target-shim@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== eventemitter3@^3.1.0: version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== events@^3.0.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== dependencies: md5.js "^1.3.4" @@ -2574,7 +2595,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: execa@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -2589,7 +2610,7 @@ execa@^5.0.0: external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -2598,12 +2619,12 @@ external-editor@^3.0.3: extract-files@9.0.0, extract-files@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" + resolved "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz" integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== fast-glob@^3.1.1, fast-glob@^3.2.5: version "3.2.5" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -2615,26 +2636,26 @@ fast-glob@^3.1.1, fast-glob@^3.2.5: fastq@^1.6.0: version "1.11.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz" integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz" integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== dependencies: bser "2.1.1" fbjs-css-vars@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + resolved "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz" integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== fbjs@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.0.tgz#0907067fb3f57a78f45d95f1eacffcacd623c165" + resolved "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz" integrity sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg== dependencies: cross-fetch "^3.0.4" @@ -2647,7 +2668,7 @@ fbjs@^3.0.0: figures@^1.7.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz" integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= dependencies: escape-string-regexp "^1.0.5" @@ -2655,28 +2676,28 @@ figures@^1.7.0: figures@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz" integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= dependencies: escape-string-regexp "^1.0.5" figures@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" find-cache-dir@3.3.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz" integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== dependencies: commondir "^1.0.1" @@ -2685,7 +2706,7 @@ find-cache-dir@3.3.1: find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -2693,17 +2714,22 @@ find-up@^4.0.0, find-up@^4.1.0: flatten@^1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" + resolved "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== +follow-redirects@^1.10.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" + integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== + foreach@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + resolved "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= form-data@4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" @@ -2712,7 +2738,7 @@ form-data@4.0.0: form-data@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" @@ -2721,12 +2747,12 @@ form-data@^3.0.0: fraction.js@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.1.tgz#ac4e520473dae67012d618aab91eda09bcb400ff" + resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz" integrity sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg== fs-extra@^10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz" integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== dependencies: graceful-fs "^4.2.0" @@ -2735,32 +2761,32 @@ fs-extra@^10.0.0: fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" @@ -2769,57 +2795,57 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: get-orientation@1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/get-orientation/-/get-orientation-1.1.2.tgz#20507928951814f8a91ded0a0e67b29dfab98947" + resolved "https://registry.npmjs.org/get-orientation/-/get-orientation-1.1.2.tgz" integrity sha512-/pViTfifW+gBbh/RnlFYHINvELT9Znt+SYyDKAUL6uV6By019AK/s+i9XP4jSwq7lwP38Fd8HVeTxym3+hkwmQ== dependencies: stream-parser "^0.3.1" get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + resolved "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== get-stream@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^5.1.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== glob-parent@^5.1.0, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-parent@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.0.tgz#f851b59b388e788f3a44d63fab50382b2859c33c" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.0.tgz" integrity sha512-Hdd4287VEJcZXUwv1l8a+vXC1GjOQqXe+VS30w/ypihpcnu9M1n3xeYeJu5CBpeEQj2nAab2xxz28GuA3vp4Ww== dependencies: is-glob "^4.0.1" glob-to-regexp@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.6: version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" @@ -2831,12 +2857,12 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.6: globals@^11.1.0: version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globby@11.0.3: version "11.0.3" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" + resolved "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz" integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== dependencies: array-union "^2.1.0" @@ -2848,7 +2874,7 @@ globby@11.0.3: got@^9.6.0: version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz" integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== dependencies: "@sindresorhus/is" "^0.14.0" @@ -2865,12 +2891,12 @@ got@^9.6.0: graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== graphql-config@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-3.3.0.tgz#24c3672a427cb67c0c717ca3b9d70e9f0c9e752b" + resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-3.3.0.tgz" integrity sha512-mSQIsPMssr7QrgqhnjI+CyVH6oQgCrgS6irHsTvwf7RFDRnR2k9kqpQOQgVoOytBSn0DOYryS0w0SAg9xor/Jw== dependencies: "@endemolshinegroup/cosmiconfig-typescript-loader" "3.0.2" @@ -2887,7 +2913,7 @@ graphql-config@^3.3.0: graphql-request@^3.3.0: version "3.4.0" - resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.4.0.tgz#3a400cd5511eb3c064b1873afb059196bbea9c2b" + resolved "https://registry.npmjs.org/graphql-request/-/graphql-request-3.4.0.tgz" integrity sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg== dependencies: cross-fetch "^3.0.6" @@ -2896,65 +2922,65 @@ graphql-request@^3.3.0: graphql-tag@^2.11.0: version "2.12.4" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.4.tgz#d34066688a4f09e72d6f4663c74211e9b4b7c4bf" + resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.4.tgz" integrity sha512-VV1U4O+9x99EkNpNmCUV5RZwq6MnK4+pGbRYWG+lA/m3uo7TSqJF81OkcOP148gFP6fzdl7JWYBrwWVTS9jXww== dependencies: tslib "^2.1.0" graphql-ws@^4.4.1: version "4.9.0" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-4.9.0.tgz#5cfd8bb490b35e86583d8322f5d5d099c26e365c" + resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.9.0.tgz" integrity sha512-sHkK9+lUm20/BGawNEWNtVAeJzhZeBg21VmvmLoT5NdGVeZWv5PdIhkcayQIAgjSyyQ17WMKmbDijIPG2On+Ag== graphql@^15.5.1: version "15.5.1" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad" + resolved "https://registry.npmjs.org/graphql/-/graphql-15.5.1.tgz" integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw== gzip-size@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz" integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== dependencies: duplexer "^0.1.2" has-ansi@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= dependencies: ansi-regex "^2.0.0" has-bigints@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== has@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hash-base@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz" integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== dependencies: inherits "^2.0.4" @@ -2963,7 +2989,7 @@ hash-base@^3.0.0: hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" @@ -2971,12 +2997,12 @@ hash.js@^1.0.0, hash.js@^1.0.3: he@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== header-case@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" + resolved "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz" integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== dependencies: capital-case "^1.0.4" @@ -2984,7 +3010,7 @@ header-case@^2.0.4: hmac-drbg@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= dependencies: hash.js "^1.0.3" @@ -2993,17 +3019,17 @@ hmac-drbg@^1.0.1: html-tags@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + resolved "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz" integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== http-cache-semantics@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-errors@1.7.3: version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: depd "~1.1.2" @@ -3014,7 +3040,7 @@ http-errors@1.7.3: http-proxy-agent@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== dependencies: "@tootallnate/once" "1" @@ -3023,12 +3049,12 @@ http-proxy-agent@^4.0.1: https-browserify@1.0.0, https-browserify@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= https-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== dependencies: agent-base "6" @@ -3036,72 +3062,72 @@ https-proxy-agent@^5.0.0: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== humanize-ms@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= dependencies: ms "^2.0.0" husky@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" + resolved "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz" integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" iconv-lite@^0.6.2: version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.1.4: version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== image-size@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.0.tgz#58b31fe4743b1cec0a0ac26f5c914d3c5b2f0750" + resolved "https://registry.npmjs.org/image-size/-/image-size-1.0.0.tgz" integrity sha512-JLJ6OwBfO1KcA+TvJT+v8gbE6iWbj24LyDNFgFEN0lzegn6cC6a/p3NIDaepMsJjQjlUWqIC7wJv8lBFxPNjcw== dependencies: queue "6.0.2" immutability-helper@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-3.1.1.tgz#2b86b2286ed3b1241c9e23b7b21e0444f52f77b7" + resolved "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz" integrity sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ== immutable@~3.7.6: version "3.7.6" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" + resolved "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz" integrity sha1-E7TTyxK++hVIKib+Gy665kAHHks= import-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" + resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz" integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== dependencies: import-from "^3.0.0" import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -3109,29 +3135,29 @@ import-fresh@^3.2.1: import-from@3.0.0, import-from@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" + resolved "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz" integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== dependencies: resolve-from "^5.0.0" indent-string@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz" integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== indexes-of@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" @@ -3139,27 +3165,27 @@ inflight@^1.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= inherits@2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= ini@~1.3.0: version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== inquirer@^7.3.3: version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz" integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" @@ -3178,7 +3204,7 @@ inquirer@^7.3.3: is-absolute@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + resolved "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz" integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== dependencies: is-relative "^1.0.0" @@ -3186,101 +3212,101 @@ is-absolute@^1.0.0: is-arguments@^1.0.4: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz" integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== dependencies: call-bind "^1.0.0" is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-arrayish@^0.3.1: version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== is-bigint@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz" integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz" integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== dependencies: call-bind "^1.0.2" is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== is-core-module@^2.2.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz" integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== dependencies: has "^1.0.3" is-date-object@^1.0.1: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz" integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-fullwidth-code-point@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-function@^1.0.7: version "1.0.9" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz" integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== is-glob@4.0.1, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" is-lower-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" + resolved "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz" integrity sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ== dependencies: tslib "^2.0.3" is-nan@^1.2.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz" integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== dependencies: call-bind "^1.0.0" @@ -3288,44 +3314,44 @@ is-nan@^1.2.1: is-negative-zero@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-number-object@^1.0.4: version "1.0.5" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz" integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= is-observable@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" + resolved "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz" integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== dependencies: symbol-observable "^1.1.0" is-promise@4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + resolved "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== is-promise@^2.1.0: version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + resolved "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== is-regex@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz" integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== dependencies: call-bind "^1.0.2" @@ -3333,41 +3359,41 @@ is-regex@^1.1.3: is-regexp@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + resolved "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= is-relative@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + resolved "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz" integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== dependencies: is-unc-path "^1.0.0" is-stream@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== is-string@^1.0.5, is-string@^1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz" integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" is-typed-array@^1.1.3: version "1.1.5" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz" integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== dependencies: available-typed-arrays "^1.0.2" @@ -3378,41 +3404,41 @@ is-typed-array@^1.1.3: is-unc-path@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + resolved "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz" integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== dependencies: unc-path-regex "^0.1.2" is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-upper-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-2.0.2.tgz#f1105ced1fe4de906a5f39553e7d3803fd804649" + resolved "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz" integrity sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ== dependencies: tslib "^2.0.3" is-windows@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isomorphic-fetch@3.0.0, isomorphic-fetch@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz" integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== dependencies: node-fetch "^2.6.1" @@ -3420,17 +3446,17 @@ isomorphic-fetch@3.0.0, isomorphic-fetch@^3.0.0: isomorphic-ws@4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== iterall@^1.2.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" + resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz" integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== jest-worker@27.0.0-next.5: version "27.0.0-next.5" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.0-next.5.tgz#5985ee29b12a4e191f4aae4bb73b97971d86ec28" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.0-next.5.tgz" integrity sha512-mk0umAQ5lT+CaOJ+Qp01N6kz48sJG2kr2n1rX0koqKf6FIygQV0qLOdN9SCYID4IVeSigDOcPeGLozdMLYfb5g== dependencies: "@types/node" "*" @@ -3439,46 +3465,46 @@ jest-worker@27.0.0-next.5: js-cookie@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz" integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== json-buffer@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-stable-stringify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= dependencies: jsonify "~0.0.0" json-to-pretty-yaml@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz#f4cd0bd0a5e8fe1df25aaf5ba118b099fd992d5b" + resolved "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz" integrity sha1-9M0L0KXo/h3yWq9boRiwmf2ZLVs= dependencies: remedial "^1.0.7" @@ -3486,21 +3512,21 @@ json-to-pretty-yaml@^1.2.2: json5@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== dependencies: minimist "^1.2.0" json5@^2.1.2: version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" @@ -3509,12 +3535,12 @@ jsonfile@^6.0.1: jsonify@~0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= jsonwebtoken@^8.5.1: version "8.5.1" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz" integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== dependencies: jws "^3.2.2" @@ -3530,7 +3556,7 @@ jsonwebtoken@^8.5.1: jwa@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== dependencies: buffer-equal-constant-time "1.0.1" @@ -3539,7 +3565,7 @@ jwa@^1.4.1: jws@^3.2.2: version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== dependencies: jwa "^1.4.1" @@ -3547,36 +3573,36 @@ jws@^3.2.2: keen-slider@^5.5.1: version "5.5.1" - resolved "https://registry.yarnpkg.com/keen-slider/-/keen-slider-5.5.1.tgz#1493f552eadf0f5b8229defd83c626eb6dd58c13" + resolved "https://registry.npmjs.org/keen-slider/-/keen-slider-5.5.1.tgz" integrity sha512-QXGZGt5Hbe0YufR/RYbOG03MmOk43RQEXqkkSvjr8ZS67sVR7LRp5RIvJALfjl+A7BnHNr1wd1QBOemwy65Lfw== keyv@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== dependencies: json-buffer "3.0.0" latest-version@5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + resolved "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz" integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== dependencies: package-json "^6.3.0" lilconfig@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz" integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg== lines-and-columns@^1.1.6: version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= lint-staged@^11.0.0: version "11.0.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.0.0.tgz#24d0a95aa316ba28e257f5c4613369a75a10c712" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-11.0.0.tgz" integrity sha512-3rsRIoyaE8IphSUtO1RVTFl1e0SLBtxxUOPBtHxQgBHS5/i6nqvjcUfNioMa4BU9yGnPzbO+xkfLtXtxBpCzjw== dependencies: chalk "^4.1.1" @@ -3597,12 +3623,12 @@ lint-staged@^11.0.0: listr-silent-renderer@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + resolved "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz" integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= listr-update-renderer@^0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" + resolved "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz" integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== dependencies: chalk "^1.1.3" @@ -3616,7 +3642,7 @@ listr-update-renderer@^0.5.0: listr-verbose-renderer@^0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" + resolved "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz" integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== dependencies: chalk "^2.4.1" @@ -3626,7 +3652,7 @@ listr-verbose-renderer@^0.5.0: listr2@^3.8.2: version "3.10.0" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.10.0.tgz#58105a53ed7fa1430d1b738c6055ef7bb006160f" + resolved "https://registry.npmjs.org/listr2/-/listr2-3.10.0.tgz" integrity sha512-eP40ZHihu70sSmqFNbNy2NL1YwImmlMmPh9WO5sLmPDleurMHt3n+SwEWNu2kzKScexZnkyFtc1VI0z/TGlmpw== dependencies: cli-truncate "^2.1.0" @@ -3639,7 +3665,7 @@ listr2@^3.8.2: listr@^0.14.3: version "0.14.3" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" + resolved "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz" integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== dependencies: "@samverschueren/stream-to-observable" "^0.3.0" @@ -3654,7 +3680,7 @@ listr@^0.14.3: loader-utils@1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz" integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== dependencies: big.js "^5.2.2" @@ -3663,121 +3689,121 @@ loader-utils@1.2.3: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" lodash.camelcase@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= lodash.clonedeep@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= lodash.get@^4: version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= lodash.includes@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= lodash.isboolean@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= lodash.isdate@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isdate/-/lodash.isdate-4.0.1.tgz#35a543673b9d76110de4114b32cc577048a7f366" + resolved "https://registry.npmjs.org/lodash.isdate/-/lodash.isdate-4.0.1.tgz" integrity sha1-NaVDZzuddhEN5BFLMsxXcEin82Y= lodash.isinteger@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + resolved "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= lodash.isnumber@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + resolved "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= lodash.isplainobject@^4.0.6: version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= lodash.isstring@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= lodash.once@^4.0.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= lodash.random@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.random/-/lodash.random-3.2.0.tgz#96e24e763333199130d2c9e2fd57f91703cc262d" + resolved "https://registry.npmjs.org/lodash.random/-/lodash.random-3.2.0.tgz" integrity sha1-luJOdjMzGZEw0sni/Vf5FwPMJi0= lodash.snakecase@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz" integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= lodash.sortby@^4.7.0: version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= lodash.throttle@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= lodash.toarray@^4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" + resolved "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= lodash.topath@^4.5.2: version "4.5.2" - resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" + resolved "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz" integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= lodash.uniq@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= lodash@4.17.21, lodash@^4.17.13, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.0: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz" integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= dependencies: chalk "^1.0.0" log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -3785,7 +3811,7 @@ log-symbols@^4.0.0, log-symbols@^4.1.0: log-update@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" + resolved "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz" integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= dependencies: ansi-escapes "^3.0.0" @@ -3794,7 +3820,7 @@ log-update@^2.3.0: log-update@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz" integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== dependencies: ansi-escapes "^4.3.0" @@ -3804,69 +3830,69 @@ log-update@^4.0.0: loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" lower-case-first@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-2.0.2.tgz#64c2324a2250bf7c37c5901e76a5b5309301160b" + resolved "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz" integrity sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg== dependencies: tslib "^2.0.3" lower-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== dependencies: tslib "^2.0.3" lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lowercase-keys@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== lru-cache@5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" make-dir@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" make-error@^1, make-error@^1.1.1: version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== map-cache@^0.2.0: version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= matcher@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-4.0.0.tgz#a42a05a09aaed92e2d241eb91fddac689461ea51" + resolved "https://registry.npmjs.org/matcher/-/matcher-4.0.0.tgz" integrity sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ== dependencies: escape-string-regexp "^4.0.0" md5.js@^1.3.4: version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== dependencies: hash-base "^3.0.0" @@ -3875,22 +3901,22 @@ md5.js@^1.3.4: merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== meros@1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/meros/-/meros-1.1.4.tgz#c17994d3133db8b23807f62bec7f0cb276cfd948" + resolved "https://registry.npmjs.org/meros/-/meros-1.1.4.tgz" integrity sha512-E9ZXfK9iQfG9s73ars9qvvvbSIkJZF5yOo9j4tcwM5tN8mUKfj/EKN5PzOr3ZH0y5wL7dLAHw3RVEfpQV9Q7VQ== micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" @@ -3898,7 +3924,7 @@ micromatch@^4.0.2, micromatch@^4.0.4: miller-rabin@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz" integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== dependencies: bn.js "^4.0.0" @@ -3906,113 +3932,113 @@ miller-rabin@^4.0.0: mime-db@1.48.0: version "1.48.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz" integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== mime-types@^2.1.12: version "2.1.31" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz" integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== dependencies: mime-db "1.48.0" mime@^2.3.1: version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" + resolved "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz" integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== mimic-fn@^1.0.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimalistic-crypto-utils@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== modern-normalize@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7" + resolved "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz" integrity sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA== ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@^2.0.0, ms@^2.1.1: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mute-stream@0.0.8: version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nanoid@^3.1.22, nanoid@^3.1.23: version "3.1.23" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz" integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== native-url@0.3.4: version "0.3.4" - resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8" + resolved "https://registry.npmjs.org/native-url/-/native-url-0.3.4.tgz" integrity sha512-6iM8R99ze45ivyH8vybJ7X0yekIcPf5GgLV5K0ENCbmRcaRIDoj37BC8iLEmaaBfqqb8enuZ5p0uhY+lVAbAcA== dependencies: querystring "^0.2.0" next-seo@^4.26.0: version "4.26.0" - resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.26.0.tgz#4218cfae5651fdc2e330dcdf1cc1b34ce199d41c" + resolved "https://registry.npmjs.org/next-seo/-/next-seo-4.26.0.tgz" integrity sha512-5TqywQ3XAwqdmEU1AyNZjR7WdDKFTkDD8aBtgQelPvzBUEy8i0mTjtiw+09jhiHFNik6FqS8uPKCaYcY6jRgSQ== next-themes@^0.0.14: version "0.0.14" - resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.14.tgz#2b9861990bc453149e23d8e6ef1a25a119e36675" + resolved "https://registry.npmjs.org/next-themes/-/next-themes-0.0.14.tgz" integrity sha512-x09OaM+wg3SIlEjOv8B21aw/E36jxTtfW3Dm/DPwMsSMluGt7twe1LigA6nc+mXP1u0qu9MxBaIrPPH6UTiKnA== next@^11.0.0: version "11.0.1" - resolved "https://registry.yarnpkg.com/next/-/next-11.0.1.tgz#b8e3914d153aaf7143cb98c09bcd3c8230eeb17a" + resolved "https://registry.npmjs.org/next/-/next-11.0.1.tgz" integrity sha512-yR7be7asNbvpVNpi6xxEg28wZ7Gqmj1nOt0sABH9qORmF3+pms2KZ7Cng33oK5nqPIzEEFJD0pp2PCe3/ueMIg== dependencies: "@babel/runtime" "7.12.5" @@ -4068,7 +4094,7 @@ next@^11.0.0: no-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== dependencies: lower-case "^2.0.2" @@ -4076,31 +4102,31 @@ no-case@^3.0.4: node-emoji@^1.8.1: version "1.10.0" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" + resolved "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz" integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== dependencies: lodash.toarray "^4.4.0" node-fetch@2.6.1, node-fetch@^2.6.1: version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== node-html-parser@1.4.9: version "1.4.9" - resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-1.4.9.tgz#3c8f6cac46479fae5800725edb532e9ae8fd816c" + resolved "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.4.9.tgz" integrity sha512-UVcirFD1Bn0O+TSmloHeHqZZCxHjvtIeGdVdGMhyZ8/PWlEiZaZ5iJzR189yKZr8p0FXN58BUeC7RHRkf/KYGw== dependencies: he "1.2.0" node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= node-libs-browser@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + resolved "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz" integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== dependencies: assert "^1.1.1" @@ -4129,71 +4155,76 @@ node-libs-browser@^2.2.1: node-releases@^1.1.71: version "1.1.73" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz" integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-range@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= normalize-url@^4.1.0: version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" nullthrows@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" + resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== num2fraction@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz" integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= number-is-nan@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-hash@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== object-inspect@^1.10.3: version "1.10.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz" integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== +object-inspect@^1.9.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" + integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== + object-is@^1.0.1: version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== dependencies: call-bind "^1.0.2" @@ -4201,7 +4232,7 @@ object-is@^1.0.1: object-keys-normalizer@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/object-keys-normalizer/-/object-keys-normalizer-1.0.1.tgz#db178dbba5e4c7b18b40837c8ef83365ee9348e7" + resolved "https://registry.npmjs.org/object-keys-normalizer/-/object-keys-normalizer-1.0.1.tgz" integrity sha1-2xeNu6Xkx7GLQIN8jvgzZe6TSOc= dependencies: lodash.camelcase "^4.3.0" @@ -4209,12 +4240,12 @@ object-keys-normalizer@1.0.1: object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object-merge-advanced@12.0.3: version "12.0.3" - resolved "https://registry.yarnpkg.com/object-merge-advanced/-/object-merge-advanced-12.0.3.tgz#e03c19aa33cf88da6b32187e4907b487668808d9" + resolved "https://registry.npmjs.org/object-merge-advanced/-/object-merge-advanced-12.0.3.tgz" integrity sha512-xQIf2Vup1rpKiHr2tQca5jyNYgT4O0kNxOfAp3ZNonm2hS+5yaJgI0Czdk/QMy52bcRwQKX3uc3H8XtAiiYfVA== dependencies: "@babel/runtime" "^7.12.13" @@ -4228,7 +4259,7 @@ object-merge-advanced@12.0.3: object.assign@^4.1.0, object.assign@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: call-bind "^1.0.0" @@ -4238,86 +4269,86 @@ object.assign@^4.1.0, object.assign@^4.1.2: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz" integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= dependencies: mimic-fn "^1.0.0" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" opener@^1.5.2: version "1.5.2" - resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + resolved "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== os-browserify@0.3.0, os-browserify@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= p-cancelable@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== p-limit@3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-map@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + resolved "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== p-map@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== package-json@^6.3.0: version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + resolved "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz" integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== dependencies: got "^9.6.0" @@ -4327,12 +4358,12 @@ package-json@^6.3.0: pako@~1.0.5: version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== param-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== dependencies: dot-case "^3.0.4" @@ -4340,14 +4371,14 @@ param-case@^3.0.4: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-asn1@^5.0.0, parse-asn1@^5.1.5: version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + resolved "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz" integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== dependencies: asn1.js "^5.2.0" @@ -4358,7 +4389,7 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5: parse-filepath@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + resolved "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz" integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= dependencies: is-absolute "^1.0.0" @@ -4367,7 +4398,7 @@ parse-filepath@^1.0.2: parse-json@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -4377,7 +4408,7 @@ parse-json@^5.0.0: pascal-case@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== dependencies: no-case "^3.0.4" @@ -4385,17 +4416,17 @@ pascal-case@^3.1.2: path-browserify@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== path-browserify@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== path-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" + resolved "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz" integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== dependencies: dot-case "^3.0.4" @@ -4403,44 +4434,44 @@ path-case@^3.0.4: path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + resolved "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz" integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= path-root@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + resolved "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz" integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= dependencies: path-root-regex "^0.1.0" path-type@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pbkdf2@^3.0.3: version "3.1.2" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz" integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== dependencies: create-hash "^1.1.2" @@ -4451,38 +4482,38 @@ pbkdf2@^3.0.3: picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== pkg-dir@^4.1.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" platform@1.3.6: version "1.3.6" - resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + resolved "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz" integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== please-upgrade-node@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + resolved "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz" integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== dependencies: semver-compare "^1.0.0" pnp-webpack-plugin@1.6.4: version "1.6.4" - resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + resolved "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz" integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== dependencies: ts-pnp "^1.1.6" postcss-attribute-case-insensitive@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880" + resolved "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz" integrity sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA== dependencies: postcss "^7.0.2" @@ -4490,7 +4521,7 @@ postcss-attribute-case-insensitive@^4.0.1: postcss-color-functional-notation@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0" + resolved "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz" integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== dependencies: postcss "^7.0.2" @@ -4498,7 +4529,7 @@ postcss-color-functional-notation@^2.0.1: postcss-color-gray@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547" + resolved "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz" integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== dependencies: "@csstools/convert-colors" "^1.4.0" @@ -4507,7 +4538,7 @@ postcss-color-gray@^5.0.0: postcss-color-hex-alpha@^5.0.3: version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388" + resolved "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz" integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== dependencies: postcss "^7.0.14" @@ -4515,7 +4546,7 @@ postcss-color-hex-alpha@^5.0.3: postcss-color-mod-function@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d" + resolved "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz" integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== dependencies: "@csstools/convert-colors" "^1.4.0" @@ -4524,7 +4555,7 @@ postcss-color-mod-function@^3.0.3: postcss-color-rebeccapurple@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz#c7a89be872bb74e45b1e3022bfe5748823e6de77" + resolved "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz" integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== dependencies: postcss "^7.0.2" @@ -4532,14 +4563,14 @@ postcss-color-rebeccapurple@^4.0.1: postcss-custom-media@^7.0.8: version "7.0.8" - resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c" + resolved "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz" integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== dependencies: postcss "^7.0.14" postcss-custom-properties@^8.0.11: version "8.0.11" - resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz#2d61772d6e92f22f5e0d52602df8fae46fa30d97" + resolved "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz" integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== dependencies: postcss "^7.0.17" @@ -4547,7 +4578,7 @@ postcss-custom-properties@^8.0.11: postcss-custom-selectors@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz#64858c6eb2ecff2fb41d0b28c9dd7b3db4de7fba" + resolved "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz" integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== dependencies: postcss "^7.0.2" @@ -4555,7 +4586,7 @@ postcss-custom-selectors@^5.1.2: postcss-dir-pseudo-class@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2" + resolved "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz" integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== dependencies: postcss "^7.0.2" @@ -4563,7 +4594,7 @@ postcss-dir-pseudo-class@^5.0.0: postcss-double-position-gradients@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e" + resolved "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz" integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== dependencies: postcss "^7.0.5" @@ -4571,7 +4602,7 @@ postcss-double-position-gradients@^1.0.0: postcss-env-function@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7" + resolved "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz" integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== dependencies: postcss "^7.0.2" @@ -4579,40 +4610,40 @@ postcss-env-function@^2.0.2: postcss-flexbugs-fixes@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz#2028e145313074fc9abe276cb7ca14e5401eb49d" + resolved "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz" integrity sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ== postcss-focus-visible@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e" + resolved "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz" integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== dependencies: postcss "^7.0.2" postcss-focus-within@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680" + resolved "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz" integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== dependencies: postcss "^7.0.2" postcss-font-variant@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz#42d4c0ab30894f60f98b17561eb5c0321f502641" + resolved "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz" integrity sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA== dependencies: postcss "^7.0.2" postcss-gap-properties@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715" + resolved "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz" integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== dependencies: postcss "^7.0.2" postcss-image-set-function@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288" + resolved "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz" integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== dependencies: postcss "^7.0.2" @@ -4620,14 +4651,14 @@ postcss-image-set-function@^3.0.1: postcss-initial@^3.0.0: version "3.0.4" - resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-3.0.4.tgz#9d32069a10531fe2ecafa0b6ac750ee0bc7efc53" + resolved "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz" integrity sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg== dependencies: postcss "^7.0.2" postcss-js@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-3.0.3.tgz#2f0bd370a2e8599d45439f6970403b5873abda33" + resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz" integrity sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw== dependencies: camelcase-css "^2.0.1" @@ -4635,7 +4666,7 @@ postcss-js@^3.0.3: postcss-lab-function@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e" + resolved "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz" integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== dependencies: "@csstools/convert-colors" "^1.4.0" @@ -4644,7 +4675,7 @@ postcss-lab-function@^2.0.1: postcss-load-config@^3.0.1: version "3.1.0" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.0.tgz#d39c47091c4aec37f50272373a6a648ef5e97829" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz" integrity sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g== dependencies: import-cwd "^3.0.0" @@ -4653,54 +4684,54 @@ postcss-load-config@^3.0.1: postcss-logical@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-3.0.0.tgz#2495d0f8b82e9f262725f75f9401b34e7b45d5b5" + resolved "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz" integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== dependencies: postcss "^7.0.2" postcss-media-minmax@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5" + resolved "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz" integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== dependencies: postcss "^7.0.2" postcss-nested@5.0.5: version "5.0.5" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.5.tgz#f0a107d33a9fab11d7637205f5321e27223e3603" + resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.5.tgz" integrity sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew== dependencies: postcss-selector-parser "^6.0.4" postcss-nesting@^7.0.0: version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052" + resolved "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz" integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== dependencies: postcss "^7.0.2" postcss-nesting@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-8.0.1.tgz#4a8ab3c540a0f138fd3f5d2ee65e4a24d1888024" + resolved "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-8.0.1.tgz" integrity sha512-cHPNhW5VvRQjszFDxmy16mis9qFQqQLBNw6KVmueLqqE3M182ZAk9+QoxGqbGVryzLVhannw2B5Yhosqq522fA== postcss-overflow-shorthand@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz#31ecf350e9c6f6ddc250a78f0c3e111f32dd4c30" + resolved "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz" integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== dependencies: postcss "^7.0.2" postcss-page-break@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf" + resolved "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz" integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== dependencies: postcss "^7.0.2" postcss-place@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62" + resolved "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz" integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== dependencies: postcss "^7.0.2" @@ -4708,7 +4739,7 @@ postcss-place@^4.0.1: postcss-preset-env@^6.7.0: version "6.7.0" - resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz#c34ddacf8f902383b35ad1e030f178f4cdf118a5" + resolved "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz" integrity sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg== dependencies: autoprefixer "^9.6.1" @@ -4751,7 +4782,7 @@ postcss-preset-env@^6.7.0: postcss-pseudo-class-any-link@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1" + resolved "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz" integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== dependencies: postcss "^7.0.2" @@ -4759,14 +4790,14 @@ postcss-pseudo-class-any-link@^6.0.0: postcss-replace-overflow-wrap@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c" + resolved "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz" integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== dependencies: postcss "^7.0.2" postcss-selector-matches@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff" + resolved "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz" integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== dependencies: balanced-match "^1.0.0" @@ -4774,7 +4805,7 @@ postcss-selector-matches@^4.0.0: postcss-selector-not@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz#263016eef1cf219e0ade9a913780fc1f48204cbf" + resolved "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz" integrity sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ== dependencies: balanced-match "^1.0.0" @@ -4782,7 +4813,7 @@ postcss-selector-not@^4.0.0: postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz" integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== dependencies: cssesc "^2.0.0" @@ -4791,7 +4822,7 @@ postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.6: version "6.0.6" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz" integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== dependencies: cssesc "^3.0.0" @@ -4799,17 +4830,17 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector postcss-value-parser@^3.3.0: version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== postcss-value-parser@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" + resolved "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz" integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== dependencies: flatten "^1.0.2" @@ -4818,7 +4849,7 @@ postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: postcss@8.2.13: version "8.2.13" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.13.tgz#dbe043e26e3c068e45113b1ed6375d2d37e2129f" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz" integrity sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ== dependencies: colorette "^1.2.2" @@ -4827,7 +4858,7 @@ postcss@8.2.13: postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: version "7.0.36" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz" integrity sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw== dependencies: chalk "^2.4.2" @@ -4836,7 +4867,7 @@ postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.32, postcss@^7.0. postcss@^8.1.6, postcss@^8.2.1, postcss@^8.3.5: version "8.3.5" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz" integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== dependencies: colorette "^1.2.2" @@ -4845,39 +4876,39 @@ postcss@^8.1.6, postcss@^8.2.1, postcss@^8.3.5: prepend-http@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= prettier@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz" integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== pretty-hrtime@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process@0.11.10, process@^0.11.10: version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= promise@^7.1.1: version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== dependencies: asap "~2.0.3" prop-types@15.7.2: version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== dependencies: loose-envify "^1.4.0" @@ -4886,7 +4917,7 @@ prop-types@15.7.2: public-encrypt@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz" integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== dependencies: bn.js "^4.1.0" @@ -4898,7 +4929,7 @@ public-encrypt@^4.0.0: pump@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -4906,22 +4937,22 @@ pump@^3.0.0: punycode@1.3.2: version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= punycode@^1.2.4: version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= punycode@^2.1.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== purgecss@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-4.0.3.tgz#8147b429f9c09db719e05d64908ea8b672913742" + resolved "https://registry.npmjs.org/purgecss/-/purgecss-4.0.3.tgz" integrity sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw== dependencies: commander "^6.0.0" @@ -4931,51 +4962,58 @@ purgecss@^4.0.3: qs@6.7.0: version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@^6.10.1: + version "6.10.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" + integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== + dependencies: + side-channel "^1.0.4" + querystring-es3@0.2.1, querystring-es3@^0.2.0: version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= querystring@0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= querystring@^0.2.0: version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" + resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz" integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== queue@6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + resolved "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz" integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== dependencies: inherits "~2.0.3" quick-lru@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" randomfill@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + resolved "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz" integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== dependencies: randombytes "^2.0.5" @@ -4983,7 +5021,7 @@ randomfill@^1.0.3: raw-body@2.4.1: version "2.4.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz" integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== dependencies: bytes "3.1.0" @@ -4993,7 +5031,7 @@ raw-body@2.4.1: rc@^1.2.8: version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" @@ -5003,7 +5041,7 @@ rc@^1.2.8: react-dom@^17.0.2: version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== dependencies: loose-envify "^1.1.0" @@ -5012,39 +5050,39 @@ react-dom@^17.0.2: react-fast-marquee@^1.1.4: version "1.2.1" - resolved "https://registry.yarnpkg.com/react-fast-marquee/-/react-fast-marquee-1.2.1.tgz#8a8ee91ec0cee04bcff7f1ea5c3fd5a2c4dc227d" + resolved "https://registry.npmjs.org/react-fast-marquee/-/react-fast-marquee-1.2.1.tgz" integrity sha512-rd9ZDhiUrrL2puZNlnb633df/1gjtknuIaf749LIgTLXa18fzPIdFBrz4VjERu1ECUdU9tniaF5FuosNt/GYKQ== react-is@17.0.2: version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== react-is@^16.8.1: version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-merge-refs@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" + resolved "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz" integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ== react-refresh@0.8.3: version "0.8.3" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" + resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz" integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== react-use-measure@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.0.4.tgz#cb675b36eaeaf3681b94d5f5e08b2a1e081fedc9" + resolved "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.0.4.tgz" integrity sha512-7K2HIGaPMl3Q9ZQiEVjen3tRXl4UDda8LiTPy/QxP8dP2rl5gPBhf7mMH6MVjjRNv3loU7sNzey/ycPNnHVTxQ== dependencies: debounce "^1.2.0" react@^17.0.2: version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== dependencies: loose-envify "^1.1.0" @@ -5052,7 +5090,7 @@ react@^17.0.2: readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6: version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" @@ -5065,7 +5103,7 @@ readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6: readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" @@ -5074,21 +5112,21 @@ readable-stream@^3.5.0, readable-stream@^3.6.0: readdirp@~3.5.0: version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz" integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: picomatch "^2.2.1" readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" reduce-css-calc@^2.1.8: version "2.1.8" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz#7ef8761a28d614980dc0c982f772c93f7a99de03" + resolved "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz" integrity sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg== dependencies: css-unit-converter "^1.1.1" @@ -5096,26 +5134,26 @@ reduce-css-calc@^2.1.8: regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== registry-auth-token@^4.0.0: version "4.2.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz" integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== dependencies: rc "^1.2.8" registry-url@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + resolved "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz" integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== dependencies: rc "^1.2.8" relay-compiler@10.1.0: version "10.1.0" - resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-10.1.0.tgz#fb4672cdbe9b54869a3a79759edd8c2d91609cbe" + resolved "https://registry.npmjs.org/relay-compiler/-/relay-compiler-10.1.0.tgz" integrity sha512-HPqc3N3tNgEgUH5+lTr5lnLbgnsZMt+MRiyS0uAVNhuPY2It0X1ZJG+9qdA3L9IqKFUNwVn6zTO7RArjMZbARQ== dependencies: "@babel/core" "^7.0.0" @@ -5137,7 +5175,7 @@ relay-compiler@10.1.0: relay-runtime@10.1.0: version "10.1.0" - resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-10.1.0.tgz#4753bf36e95e8d862cef33608e3d98b4ed730d16" + resolved "https://registry.npmjs.org/relay-runtime/-/relay-runtime-10.1.0.tgz" integrity sha512-bxznLnQ1ST6APN/cFi7l0FpjbZVchWQjjhj9mAuJBuUqNNCh9uV+UTRhpQF7Q8ycsPp19LHTpVyGhYb0ustuRQ== dependencies: "@babel/runtime" "^7.0.0" @@ -5145,47 +5183,47 @@ relay-runtime@10.1.0: remedial@^1.0.7: version "1.0.8" - resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" + resolved "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz" integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= remove-trailing-spaces@^1.0.6: version "1.0.8" - resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz#4354d22f3236374702f58ee373168f6d6887ada7" + resolved "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz" integrity sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA== replaceall@^0.1.6: version "0.1.6" - resolved "https://registry.yarnpkg.com/replaceall/-/replaceall-0.1.6.tgz#81d81ac7aeb72d7f5c4942adf2697a3220688d8e" + resolved "https://registry.npmjs.org/replaceall/-/replaceall-0.1.6.tgz" integrity sha1-gdgax663LX9cSUKt8ml6MiBojY4= require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-main-filename@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve@^1.20.0: version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: is-core-module "^2.2.0" @@ -5193,14 +5231,14 @@ resolve@^1.20.0: responselike@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= dependencies: lowercase-keys "^1.0.0" restore-cursor@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz" integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= dependencies: onetime "^2.0.0" @@ -5208,7 +5246,7 @@ restore-cursor@^2.0.0: restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -5216,24 +5254,24 @@ restore-cursor@^3.1.0: retry@0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= reusify@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== dependencies: hash-base "^3.0.0" @@ -5241,41 +5279,41 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: run-async@^2.4.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@^6.3.3, rxjs@^6.6.0, rxjs@^6.6.7: version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== scheduler@^0.20.2: version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz" integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== dependencies: loose-envify "^1.1.0" @@ -5283,27 +5321,27 @@ scheduler@^0.20.2: scuid@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" + resolved "https://registry.npmjs.org/scuid/-/scuid-1.1.0.tgz" integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== semver-compare@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= semver@^5.6.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== sentence-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" + resolved "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz" integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== dependencies: no-case "^3.0.4" @@ -5312,22 +5350,22 @@ sentence-case@^3.0.4: set-blocking@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= setprototypeof@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== dependencies: inherits "^2.0.1" @@ -5335,41 +5373,50 @@ sha.js@^2.4.0, sha.js@^2.4.8: shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@1.7.2: version "1.7.2" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== signedsource@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" + resolved "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz" integrity sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo= simple-swizzle@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= dependencies: is-arrayish "^0.3.1" sirv@^1.0.7: version "1.0.12" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.12.tgz#d816c882b35489b3c63290e2f455ae3eccd5f652" + resolved "https://registry.npmjs.org/sirv/-/sirv-1.0.12.tgz" integrity sha512-+jQoCxndz7L2tqQL4ZyzfDhky0W/4ZJip3XoOuxyQWnAwMxindLl3Xv1qT4x1YX/re0leShvTm8Uk0kQspGhBg== dependencies: "@polka/url" "^1.0.0-next.15" @@ -5378,17 +5425,17 @@ sirv@^1.0.7: slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@0.0.4: version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= slice-ansi@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz" integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== dependencies: ansi-styles "^4.0.0" @@ -5397,7 +5444,7 @@ slice-ansi@^3.0.0: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -5406,7 +5453,7 @@ slice-ansi@^4.0.0: snake-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + resolved "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== dependencies: dot-case "^3.0.4" @@ -5414,12 +5461,12 @@ snake-case@^3.0.4: source-map-js@^0.6.2: version "0.6.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz" integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== source-map-support@^0.5.17: version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" @@ -5427,48 +5474,48 @@ source-map-support@^0.5.17: source-map@0.7.3: version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== source-map@0.8.0-beta.0: version "0.8.0-beta.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz" integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== dependencies: whatwg-url "^7.0.0" source-map@^0.5.0: version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== sponge-case@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" + resolved "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz" integrity sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA== dependencies: tslib "^2.0.3" stacktrace-parser@0.1.10: version "0.1.10" - resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + resolved "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz" integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== dependencies: type-fest "^0.7.1" "statuses@>= 1.5.0 < 2": version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= stream-browserify@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz" integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== dependencies: inherits "~2.0.4" @@ -5476,7 +5523,7 @@ stream-browserify@3.0.0: stream-browserify@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz" integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== dependencies: inherits "~2.0.1" @@ -5484,7 +5531,7 @@ stream-browserify@^2.0.1: stream-http@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.1.1.tgz#0370a8017cf8d050b9a8554afe608f043eaff564" + resolved "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz" integrity sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg== dependencies: builtin-status-codes "^3.0.0" @@ -5494,7 +5541,7 @@ stream-http@3.1.1: stream-http@^2.7.2: version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + resolved "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz" integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== dependencies: builtin-status-codes "^3.0.0" @@ -5505,29 +5552,29 @@ stream-http@^2.7.2: stream-parser@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773" + resolved "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz" integrity sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M= dependencies: debug "2" string-argv@0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" + resolved "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== string-hash@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" + resolved "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz" integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= string-width@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" @@ -5536,7 +5583,7 @@ string-width@^1.0.1: string-width@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" @@ -5544,7 +5591,7 @@ string-width@^2.1.1: string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== dependencies: emoji-regex "^8.0.0" @@ -5553,7 +5600,7 @@ string-width@^4.1.0, string-width@^4.2.0: string.prototype.trimend@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: call-bind "^1.0.2" @@ -5561,7 +5608,7 @@ string.prototype.trimend@^1.0.4: string.prototype.trimstart@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz" integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: call-bind "^1.0.2" @@ -5569,21 +5616,21 @@ string.prototype.trimstart@^1.0.4: string_decoder@1.3.0, string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" stringify-object@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== dependencies: get-own-enumerable-property-symbols "^3.0.0" @@ -5592,38 +5639,38 @@ stringify-object@^3.3.0: strip-ansi@6.0.0, strip-ansi@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz" integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== dependencies: ansi-regex "^5.0.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= dependencies: ansi-regex "^3.0.0" strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-json-comments@~2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= styled-jsx@3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.3.2.tgz#2474601a26670a6049fb4d3f94bd91695b3ce018" + resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-3.3.2.tgz" integrity sha512-daAkGd5mqhbBhLd6jYAjYBa9LpxYCzsgo/f6qzPdFxVB8yoGbhxvzQgkC0pfmCVvW3JuAEBn0UzFLBfkHVZG1g== dependencies: "@babel/types" "7.8.3" @@ -5637,17 +5684,17 @@ styled-jsx@3.3.2: stylis-rule-sheet@0.0.10: version "0.0.10" - resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" + resolved "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz" integrity sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw== stylis@3.5.4: version "3.5.4" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" + resolved "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz" integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== subscriptions-transport-ws@^0.9.18: version "0.9.19" - resolved "https://registry.yarnpkg.com/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.19.tgz#10ca32f7e291d5ee8eb728b9c02e43c52606cdcf" + resolved "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.19.tgz" integrity sha512-dxdemxFFB0ppCLg10FTtRqH/31FNRL1y1BQv8209MK5I4CwALb7iihQg+7p65lFcIl8MHatINWBLOqpgU4Kyyw== dependencies: backo2 "^1.0.2" @@ -5658,47 +5705,47 @@ subscriptions-transport-ws@^0.9.18: supports-color@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz" integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.0.0: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" swap-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-2.0.2.tgz#671aedb3c9c137e2985ef51c51f9e98445bf70d9" + resolved "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz" integrity sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw== dependencies: tslib "^2.0.3" swell-js@^4.0.0-next.0: version "4.0.0-next.0" - resolved "https://registry.yarnpkg.com/swell-js/-/swell-js-4.0.0-next.0.tgz#870599372e3c9eafefeafc2c63863c4032d8be6b" + resolved "https://registry.npmjs.org/swell-js/-/swell-js-4.0.0-next.0.tgz" integrity sha512-OQ1FLft3ruKpQw5P0TiCzs/X2Ma95+Qz+I2Xzs4KC6v+zVaFVUGNs80dQdtjfInisWoFC7iFZF2AITgellVGAg== dependencies: "@babel/runtime" "7.4.5" @@ -5711,19 +5758,19 @@ swell-js@^4.0.0-next.0: swr@^0.5.6: version "0.5.6" - resolved "https://registry.yarnpkg.com/swr/-/swr-0.5.6.tgz#70bfe9bc9d7ac49a064be4a0f4acf57982e55a31" + resolved "https://registry.npmjs.org/swr/-/swr-0.5.6.tgz" integrity sha512-Bmx3L4geMZjYT5S2Z6EE6/5Cx6v1Ka0LhqZKq8d6WL2eu9y6gHWz3dUzfIK/ymZVHVfwT/EweFXiYGgfifei3w== dependencies: dequal "2.0.2" symbol-observable@^1.0.4, symbol-observable@^1.1.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== sync-fetch@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.3.0.tgz#77246da949389310ad978ab26790bb05f88d1335" + resolved "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.3.0.tgz" integrity sha512-dJp4qg+x4JwSEW1HibAuMi0IIrBI3wuQr2GimmqB7OXR50wmwzfdusG+p39R9w3R6aFtZ2mzvxvWKQ3Bd/vx3g== dependencies: buffer "^5.7.0" @@ -5731,12 +5778,12 @@ sync-fetch@0.3.0: tabbable@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.2.0.tgz#4fba60991d8bb89d06e5d9455c92b453acf88fb2" + resolved "https://registry.npmjs.org/tabbable/-/tabbable-5.2.0.tgz" integrity sha512-0uyt8wbP0P3T4rrsfYg/5Rg3cIJ8Shl1RJ54QMqYxm1TLdWqJD1u6+RQjr2Lor3wmfT7JRHkirIwy99ydBsyPg== tailwindcss@^2.2.2: version "2.2.2" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.2.tgz#28a99c87b5a6b2bf6298a77d88dc0590e84fa8ee" + resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.2.tgz" integrity sha512-OzFWhlnfrO3JXZKHQiqZcb0Wwl3oJSmQ7PvT2jdIgCjV5iUoAyql9bb9ZLCSBI5TYXmawujXAoNxXVfP5Auy/Q== dependencies: "@fullhuman/postcss-purgecss" "^4.0.3" @@ -5773,84 +5820,84 @@ tailwindcss@^2.2.2: through@^2.3.6, through@^2.3.8: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= timers-browserify@2.0.12, timers-browserify@^2.0.4: version "2.0.12" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz" integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== dependencies: setimmediate "^1.0.4" title-case@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" + resolved "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz" integrity sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA== dependencies: tslib "^2.0.3" tmp@^0.0.33: version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" tmp@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== dependencies: rimraf "^3.0.0" to-arraybuffer@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + resolved "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz" integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= to-readable-stream@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" toidentifier@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== totalist@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + resolved "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== tr46@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz" integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= dependencies: punycode "^2.1.0" ts-log@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.3.tgz#4da5640fe25a9fb52642cd32391c886721318efb" + resolved "https://registry.npmjs.org/ts-log/-/ts-log-2.2.3.tgz" integrity sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w== ts-node@^9: version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz" integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== dependencies: arg "^4.1.0" @@ -5862,67 +5909,67 @@ ts-node@^9: ts-pnp@^1.1.6: version "1.2.0" - resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" + resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== tslib@^1.9.0: version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2, tslib@^2.0.3, tslib@^2.1.0, tslib@~2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== tslib@~2.0.1: version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz" integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== tslib@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== tslib@~2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz" integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== tty-browserify@0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz" integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= tty-browserify@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz" integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.7.1: version "0.7.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== typescript@4.3.4: version "4.3.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz" integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew== ua-parser-js@^0.7.18: version "0.7.28" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" + resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz" integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== unbox-primitive@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" @@ -5932,55 +5979,55 @@ unbox-primitive@^1.0.1: unc-path-regex@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz" integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= uniq@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + resolved "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= universalify@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== unixify@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" + resolved "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz" integrity sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA= dependencies: normalize-path "^2.1.1" unpipe@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= upper-case-first@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + resolved "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz" integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== dependencies: tslib "^2.0.3" upper-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" + resolved "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz" integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== dependencies: tslib "^2.0.3" url-parse-lax@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= dependencies: prepend-http "^2.0.0" url@^0.11.0: version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz" integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= dependencies: punycode "1.3.2" @@ -5988,19 +6035,19 @@ url@^0.11.0: use-subscription@1.5.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" + resolved "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz" integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== dependencies: object-assign "^4.1.1" util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= util-nonempty@^3.0.6: version "3.1.0" - resolved "https://registry.yarnpkg.com/util-nonempty/-/util-nonempty-3.1.0.tgz#927a9472ead1817afca159b209e5806523b752d3" + resolved "https://registry.npmjs.org/util-nonempty/-/util-nonempty-3.1.0.tgz" integrity sha512-OSZlWoCL74Go83Qw/aeZgSmFZnp9d06bF77b1eAOKipkPWhvxjRYB2nmKiGspoVjkJJEJimzxAgBFUQiUV/oZQ== dependencies: "@babel/runtime" "^7.14.0" @@ -6008,14 +6055,14 @@ util-nonempty@^3.0.6: util@0.10.3: version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + resolved "https://registry.npmjs.org/util/-/util-0.10.3.tgz" integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= dependencies: inherits "2.0.1" util@0.12.3: version "0.12.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" + resolved "https://registry.npmjs.org/util/-/util-0.12.3.tgz" integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== dependencies: inherits "^2.0.3" @@ -6027,14 +6074,14 @@ util@0.12.3: util@^0.11.0: version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + resolved "https://registry.npmjs.org/util/-/util-0.11.1.tgz" integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== dependencies: inherits "2.0.3" util@^0.12.0: version "0.12.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + resolved "https://registry.npmjs.org/util/-/util-0.12.4.tgz" integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== dependencies: inherits "^2.0.3" @@ -6044,24 +6091,37 @@ util@^0.12.0: safe-buffer "^5.1.2" which-typed-array "^1.1.2" +uuid@8.3.2: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +uuidv4@^6.2.10: + version "6.2.11" + resolved "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.11.tgz" + integrity sha512-OTS4waH9KplrXNADKo+Q1kT9AHWr8DaC0S5F54RQzEwcUaEzBEWQQlJyDUw/u1bkRhJyqkqhLD4M4lbFbV+89g== + dependencies: + "@types/uuid" "8.3.1" + uuid "8.3.2" + valid-url@1.0.9, valid-url@^1.0.9: version "1.0.9" - resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + resolved "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz" integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= value-or-promise@1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.6.tgz#218aa4794aa2ee24dcf48a29aba4413ed584747f" + resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.6.tgz" integrity sha512-9r0wQsWD8z/BxPOvnwbPf05ZvFngXyouE9EKB+5GbYix+BYnAwrIChCUyFIinfbf2FL/U71z+CPpbnmTdxrwBg== vm-browserify@1.1.2, vm-browserify@^1.0.1: version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== watchpack@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz" integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== dependencies: glob-to-regexp "^0.4.1" @@ -6069,12 +6129,12 @@ watchpack@2.1.1: webidl-conversions@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== webpack-bundle-analyzer@4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz#2f3c0ca9041d5ee47fa418693cf56b4a518b578b" + resolved "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz" integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA== dependencies: acorn "^8.0.4" @@ -6089,12 +6149,12 @@ webpack-bundle-analyzer@4.3.0: whatwg-fetch@^3.4.1: version "3.6.2" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== whatwg-url@^7.0.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz" integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== dependencies: lodash.sortby "^4.7.0" @@ -6103,7 +6163,7 @@ whatwg-url@^7.0.0: which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -6114,12 +6174,12 @@ which-boxed-primitive@^1.0.2: which-module@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which-typed-array@^1.1.2: version "1.1.4" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz" integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== dependencies: available-typed-arrays "^1.0.2" @@ -6132,14 +6192,14 @@ which-typed-array@^1.1.2: which@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" wrap-ansi@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz" integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= dependencies: string-width "^2.1.1" @@ -6147,7 +6207,7 @@ wrap-ansi@^3.0.1: wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -6156,7 +6216,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -6165,52 +6225,52 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= ws@7.4.5: version "7.4.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" + resolved "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz" integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== "ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.3.1: version "7.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz" integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw== xtend@^4.0.0, xtend@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yaml-ast-parser@^0.0.43: version "0.0.43" - resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" + resolved "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz" integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== yaml@^1.10.0, yaml@^1.10.2: version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@^18.1.2: version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" @@ -6218,12 +6278,12 @@ yargs-parser@^18.1.2: yargs-parser@^20.2.2: version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs@^15.3.1: version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" @@ -6240,7 +6300,7 @@ yargs@^15.3.1: yargs@^17.0.0: version "17.0.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz" integrity sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ== dependencies: cliui "^7.0.2" @@ -6253,10 +6313,10 @@ yargs@^17.0.0: yn@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From a3ef27f5e71df976d23926ea87d6724fbbb99a6f Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 23 Jul 2021 11:23:42 +0200 Subject: [PATCH 02/98] Add basic Spree framework structure --- framework/spree/.env.template | 8 ++ framework/spree/README.md | 2 + framework/spree/commerce.config.json | 9 ++ framework/spree/createFetcher.ts | 88 +++++++++++++++++++ framework/spree/createProvider.ts | 43 +++++++++ framework/spree/index.tsx | 46 ++++++++++ framework/spree/next.config.js | 13 +++ framework/spree/product/use-search.tsx | 62 +++++++++++++ .../utils/convertSpreeErrorToGraphQlError.ts | 46 ++++++++++ 9 files changed, 317 insertions(+) create mode 100644 framework/spree/.env.template create mode 100644 framework/spree/README.md create mode 100644 framework/spree/commerce.config.json create mode 100644 framework/spree/createFetcher.ts create mode 100644 framework/spree/createProvider.ts create mode 100644 framework/spree/index.tsx create mode 100644 framework/spree/next.config.js create mode 100644 framework/spree/product/use-search.tsx create mode 100644 framework/spree/utils/convertSpreeErrorToGraphQlError.ts diff --git a/framework/spree/.env.template b/framework/spree/.env.template new file mode 100644 index 0000000000..d6644591a5 --- /dev/null +++ b/framework/spree/.env.template @@ -0,0 +1,8 @@ +# Template to be used for creating .env* files (.env, .env.local etc.) in the project's root directory. + +COMMERCE_PROVIDER=spree + +SPREE_API_HOST = 'http://localhost:3000' + +# TODO: +# COMMERCE_IMAGE_HOST diff --git a/framework/spree/README.md b/framework/spree/README.md new file mode 100644 index 0000000000..1d2f12f870 --- /dev/null +++ b/framework/spree/README.md @@ -0,0 +1,2 @@ +TODO: Base README on other Framework READMEs. +TODO: Link to demo site running NextJS Commerce communicating with Spree. diff --git a/framework/spree/commerce.config.json b/framework/spree/commerce.config.json new file mode 100644 index 0000000000..4129d48540 --- /dev/null +++ b/framework/spree/commerce.config.json @@ -0,0 +1,9 @@ +{ + "provider": "spree", + "features": { + "wishlist": false, + "cart": false, + "search": false, + "customerAuth": false + } +} diff --git a/framework/spree/createFetcher.ts b/framework/spree/createFetcher.ts new file mode 100644 index 0000000000..fafce34eed --- /dev/null +++ b/framework/spree/createFetcher.ts @@ -0,0 +1,88 @@ +import type { Fetcher } from '@commerce/utils/types' +import convertSpreeErrorToGraphQlError from './utils/convertSpreeErrorToGraphQlError' +import { makeClient } from '@spree/storefront-api-v2-sdk' +import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' +import type { + JsonApiDocument, + JsonApiListResponse, +} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +import { errors } from '@spree/storefront-api-v2-sdk' +// import { API_TOKEN, API_URL } from './const' +// import { handleFetchResponse } from './utils' + +const createFetcher = (fetcherOptions: any): Fetcher => { + const { host } = fetcherOptions + const client = makeClient({ host }) + + //TODO: Add types to fetcherOptions + return async (requestOptions) => { + console.log('FETCHER') + // url?: string + // query?: string + // method?: string + // variables?: any + // body?: Body + const { url, method, variables, query } = requestOptions + const { locale, ...vars } = variables ?? {} + + if (!url) { + // TODO: Create a custom type for this error. + throw new Error('Url not provider for fetcher.') + } + + console.log( + `Fetching products using options: ${JSON.stringify(requestOptions)}.` + ) + + // const storeResponse = await fetch(url, { + // method, + // body: JSON.stringify({ query, variables: vars }), + // headers: { + // 'X-Shopify-Storefront-Access-Token': API_TOKEN, + // 'Content-Type': 'application/json', TODO: Probably not needed. Check! + // }, + // }) + + // const storeResponse.json() + + // if (storeResponse.ok) { + // return + // } + + // TODO: Not best to use url for finding the method, but should be good enough for now. + + const clientEndpointMethod = url + .split('.') + .reduce((clientNode: any, pathPart) => { + // TODO: Fix clientNode type + return clientNode[pathPart] + }, client) + + const storeResponse: ResultResponse = + await clientEndpointMethod(...variables.args) // TODO: Not the best to use variables here as it's type is any. + + if (storeResponse.success()) { + return storeResponse.success() + } + + if (storeResponse.fail() instanceof errors.SpreeError) { + throw convertSpreeErrorToGraphQlError(storeResponse.fail()) + } + + throw storeResponse.fail() + } +} + +// import { Fetcher } from '@commerce/utils/types' + +// export const fetcher: Fetcher = async () => { +// console.log('FETCHER') +// const res = await fetch('./data.json') +// if (res.ok) { +// const { data } = await res.json() +// return data +// } +// throw res +// } + +export default createFetcher diff --git a/framework/spree/createProvider.ts b/framework/spree/createProvider.ts new file mode 100644 index 0000000000..d8756bd737 --- /dev/null +++ b/framework/spree/createProvider.ts @@ -0,0 +1,43 @@ +import type { Provider } from '@commerce' +import { Config } from '.' +import fetcher from './fetcher' + +// import { handler as useCart } from './cart/use-cart' +// import { handler as useAddItem } from './cart/use-add-item' +// import { handler as useUpdateItem } from './cart/use-update-item' +// import { handler as useRemoveItem } from './cart/use-remove-item' + +// import { handler as useCustomer } from './customer/use-customer' +// import { handler as useSearch } from './product/use-search' + +// import { handler as useLogin } from './auth/use-login' +// import { handler as useLogout } from './auth/use-logout' +// import { handler as useSignup } from './auth/use-signup' + +// export const saleorProvider = { +// locale: 'en-us', +// cartCookie: '', +// cartCookieToken: '', +// fetcher, +// cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, +// customer: { useCustomer }, +// products: { useSearch }, +// auth: { useLogin, useLogout, useSignup }, +// } + +export const createProvider = (options: { config: Config }): Provider => { + const { config } = options + + return { + locale: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string. + cartCookie: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string. + fetcher: createFetcher({ host: config.store.host }), + // FIXME: Add dummy hooks for below based on framework/local EXCEPT use-product + cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, + customer: { useCustomer }, + products: { useSearch }, + auth: { useLogin, useLogout, useSignup }, + } +} + +export type { Provider } diff --git a/framework/spree/index.tsx b/framework/spree/index.tsx new file mode 100644 index 0000000000..db8865117b --- /dev/null +++ b/framework/spree/index.tsx @@ -0,0 +1,46 @@ +import * as React from 'react' +import { ReactNode } from 'react' + +import { + CommerceConfig, + CommerceProvider as CoreCommerceProvider, + useCommerce as useCoreCommerce, +} from '@commerce' + +// import { provider, Provider } from './provider' +import { createProvider, Provider } from './createProvider' + +// export { provider } + +// TODO: Below is probably not needed. Expect default values to be set by NextJS Commerce and be ok for now. +// export const saleorConfig: CommerceConfig = { +// locale: 'en-us', +// cartCookie: Const.CHECKOUT_ID_COOKIE, +// } + +export type Config = { + store: { + host: string + } +} & CommerceConfig // This is the type that holds any custom values specifically for the Spree Framework. + +export type SpreeProps = { + children: ReactNode + provider: Provider + config: Config +} & Config + +export function CommerceProvider({ children, ...config }: SpreeProps) { + console.log('CommerceProvider called') + + // TODO: Make sure this doesn't get called all the time. If it does, useMemo. + const provider = createProvider({ config }) + + return ( + + {children} + + ) +} + +export const useCommerce = () => useCoreCommerce() diff --git a/framework/spree/next.config.js b/framework/spree/next.config.js new file mode 100644 index 0000000000..0bef26aba9 --- /dev/null +++ b/framework/spree/next.config.js @@ -0,0 +1,13 @@ +const commerce = require('./commerce.config.json') + +module.exports = { + commerce, + store: { + host: process.env.SPREE_API_HOST, + }, + // images: { + // domains: [process.env.COMMERCE_IMAGE_HOST], + // }, + // locale: 'en-us', + // cartCookie: Const.CHECKOUT_ID_COOKIE, +} diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx new file mode 100644 index 0000000000..85bb68a777 --- /dev/null +++ b/framework/spree/product/use-search.tsx @@ -0,0 +1,62 @@ +import type { SWRHook, Fetcher } from '@commerce/utils/types' +import useSearch from '@commerce/product/use-search' +import type { UseSearch } from '@commerce/product/use-search' + +export const handler: SWRHook = { + fetchOptions: { + url: 'client.products.list', // Add custom option for method name later + query: '', + }, + async fetcher({ input, options, fetch }) { + // This method is only needed if the options need to be modified before calling the generic fetcher (created in createFetcher). + // TODO: Actually filter by input and query. + + console.log( + `Calling useSearch fetcher with input: ${JSON.stringify( + input + )} and options: ${JSON.stringify(options)}.` + ) + + // FIXME: IMPLEMENT + + return fetch({ + url: options.url, + variables: { args: [] }, // TODO: Actually provide args later. + }) + }, + // useHook is used for both, SWR and mutation requests to the store. + // useHook is called in React components. For example, after clicking `Add to cart`. + useHook: + ({ useData }) => + (input = {}) => { + console.log('useHook called') + + // useData calls the fetcher method (above). + // The difference between useHook and calling fetcher directly is + // useHook accepts swrOptions. + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { + revalidateOnFocus: false, + // revalidateOnFocus: false means do not fetch products again when website is refocused in the web browser. + ...input.swrOptions, + }, + }) + }, + // (input = {}) => { + + // return { + // data: { + // // FIXME: Use actual fetcher + // products: [], + // }, + // } + // }, +} + +export default useSearch as UseSearch diff --git a/framework/spree/utils/convertSpreeErrorToGraphQlError.ts b/framework/spree/utils/convertSpreeErrorToGraphQlError.ts new file mode 100644 index 0000000000..5db1a64b2b --- /dev/null +++ b/framework/spree/utils/convertSpreeErrorToGraphQlError.ts @@ -0,0 +1,46 @@ +import { FetcherError } from '@commerce/utils/errors' +import { errors } from '@spree/storefront-api-v2-sdk' + +const convertSpreeErrorToGraphQlError = ( + error: errors.SpreeError +): FetcherError => { + if (error instanceof errors.ExpandedSpreeError) { + // Assuming error.errors[key] is a list of strings. + + if ('base' in error.errors) { + const baseErrorMessage = error.errors.base as unknown as string + + return new FetcherError({ + status: error.serverResponse.status, + message: baseErrorMessage, + }) + } + + const fetcherErrors = Object.keys(error.errors).map((sdkErrorKey) => { + const errors = error.errors[sdkErrorKey] as string[] + + return { + message: `${sdkErrorKey} ${errors.join(', ')}`, + } + }) + + return new FetcherError({ + status: error.serverResponse.status, + errors: fetcherErrors, + }) + } + + if (error instanceof errors.BasicSpreeError) { + return new FetcherError({ + status: error.serverResponse.status, + message: error.summary, + }) + } + + return new FetcherError({ + status: error.serverResponse.status, + message: error.message, + }) +} + +export default convertSpreeErrorToGraphQlError From c90fa7abf2a531e84b90a541fe9aa49a86115605 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 23 Jul 2021 17:01:43 +0200 Subject: [PATCH 03/98] Add Spree as allowed Framework --- framework/commerce/config.js | 1 + framework/spree/.env.template | 11 +- framework/spree/api/endpoints/cart/index.ts | 1 + .../spree/api/endpoints/catalog/index.ts | 1 + .../spree/api/endpoints/catalog/products.ts | 1 + .../spree/api/endpoints/checkout/index.ts | 1 + .../spree/api/endpoints/customer/index.ts | 1 + framework/spree/api/endpoints/login/index.ts | 1 + framework/spree/api/endpoints/logout/index.ts | 1 + framework/spree/api/endpoints/signup/index.ts | 1 + .../spree/api/endpoints/wishlist/index.tsx | 1 + framework/spree/api/index.ts | 39 +++++ .../spree/api/operations/get-all-pages.ts | 37 +++++ .../api/operations/get-all-product-paths.ts | 17 ++ .../spree/api/operations/get-all-products.ts | 79 +++++++++ .../api/operations/get-customer-wishlist.ts | 6 + framework/spree/api/operations/get-page.ts | 13 ++ framework/spree/api/operations/get-product.ts | 28 ++++ .../spree/api/operations/get-site-info.ts | 44 +++++ framework/spree/api/operations/index.ts | 6 + framework/spree/api/utils/create-api-fetch.ts | 154 ++++++++++++++++++ framework/spree/api/utils/fetch.ts | 3 + framework/spree/auth/index.ts | 3 + framework/spree/auth/use-login.tsx | 16 ++ framework/spree/auth/use-logout.tsx | 17 ++ framework/spree/auth/use-signup.tsx | 19 +++ framework/spree/cart/index.ts | 4 + framework/spree/cart/use-add-item.tsx | 17 ++ framework/spree/cart/use-cart.tsx | 42 +++++ framework/spree/cart/use-remove-item.tsx | 18 ++ framework/spree/cart/use-update-item.tsx | 18 ++ framework/spree/createFetcher.ts | 88 ---------- framework/spree/createProvider.ts | 43 ----- framework/spree/customer/index.ts | 1 + framework/spree/customer/use-customer.tsx | 15 ++ .../spree/errors/MisconfigurationError.ts | 1 + .../errors/MissingConfigurationValueError.ts | 1 + framework/spree/fetcher.ts | 86 ++++++++++ framework/spree/index.tsx | 38 ++--- framework/spree/isomorphicConfig.ts | 21 +++ framework/spree/next.config.js | 5 - framework/spree/product/index.ts | 2 + framework/spree/product/use-price.tsx | 2 + framework/spree/provider.ts | 28 ++++ framework/spree/types/index.ts | 5 + .../utils/forceIsomorphicConfigValues.ts | 43 +++++ framework/spree/utils/isServer.ts | 1 + framework/spree/utils/requireConfig.ts | 16 ++ framework/spree/wishlist/use-add-item.tsx | 13 ++ framework/spree/wishlist/use-remove-item.tsx | 17 ++ framework/spree/wishlist/use-wishlist.tsx | 43 +++++ package.json | 4 +- tsconfig.json | 4 +- 53 files changed, 912 insertions(+), 166 deletions(-) create mode 100644 framework/spree/api/endpoints/cart/index.ts create mode 100644 framework/spree/api/endpoints/catalog/index.ts create mode 100644 framework/spree/api/endpoints/catalog/products.ts create mode 100644 framework/spree/api/endpoints/checkout/index.ts create mode 100644 framework/spree/api/endpoints/customer/index.ts create mode 100644 framework/spree/api/endpoints/login/index.ts create mode 100644 framework/spree/api/endpoints/logout/index.ts create mode 100644 framework/spree/api/endpoints/signup/index.ts create mode 100644 framework/spree/api/endpoints/wishlist/index.tsx create mode 100644 framework/spree/api/index.ts create mode 100644 framework/spree/api/operations/get-all-pages.ts create mode 100644 framework/spree/api/operations/get-all-product-paths.ts create mode 100644 framework/spree/api/operations/get-all-products.ts create mode 100644 framework/spree/api/operations/get-customer-wishlist.ts create mode 100644 framework/spree/api/operations/get-page.ts create mode 100644 framework/spree/api/operations/get-product.ts create mode 100644 framework/spree/api/operations/get-site-info.ts create mode 100644 framework/spree/api/operations/index.ts create mode 100644 framework/spree/api/utils/create-api-fetch.ts create mode 100644 framework/spree/api/utils/fetch.ts create mode 100644 framework/spree/auth/index.ts create mode 100644 framework/spree/auth/use-login.tsx create mode 100644 framework/spree/auth/use-logout.tsx create mode 100644 framework/spree/auth/use-signup.tsx create mode 100644 framework/spree/cart/index.ts create mode 100644 framework/spree/cart/use-add-item.tsx create mode 100644 framework/spree/cart/use-cart.tsx create mode 100644 framework/spree/cart/use-remove-item.tsx create mode 100644 framework/spree/cart/use-update-item.tsx delete mode 100644 framework/spree/createFetcher.ts delete mode 100644 framework/spree/createProvider.ts create mode 100644 framework/spree/customer/index.ts create mode 100644 framework/spree/customer/use-customer.tsx create mode 100644 framework/spree/errors/MisconfigurationError.ts create mode 100644 framework/spree/errors/MissingConfigurationValueError.ts create mode 100644 framework/spree/fetcher.ts create mode 100644 framework/spree/isomorphicConfig.ts create mode 100644 framework/spree/product/index.ts create mode 100644 framework/spree/product/use-price.tsx create mode 100644 framework/spree/provider.ts create mode 100644 framework/spree/types/index.ts create mode 100644 framework/spree/utils/forceIsomorphicConfigValues.ts create mode 100644 framework/spree/utils/isServer.ts create mode 100644 framework/spree/utils/requireConfig.ts create mode 100644 framework/spree/wishlist/use-add-item.tsx create mode 100644 framework/spree/wishlist/use-remove-item.tsx create mode 100644 framework/spree/wishlist/use-wishlist.tsx diff --git a/framework/commerce/config.js b/framework/commerce/config.js index 28502a04e1..d629b45e3e 100644 --- a/framework/commerce/config.js +++ b/framework/commerce/config.js @@ -14,6 +14,7 @@ const PROVIDERS = [ 'swell', 'vendure', 'local', + 'spree', ] function getProviderName() { diff --git a/framework/spree/.env.template b/framework/spree/.env.template index d6644591a5..8831ec06cf 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -2,7 +2,12 @@ COMMERCE_PROVIDER=spree -SPREE_API_HOST = 'http://localhost:3000' +{# public (available in the web browser) #} +NEXT_PUBLIC_SPREE_API_HOST=http://localhost:3000 -# TODO: -# COMMERCE_IMAGE_HOST +{# private #} +NEXT_PUBLIC_SPREE_DEFAULT_LOCALE=en-us +NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart + +{# # TODO: #} +{# # COMMERCE_IMAGE_HOST #} diff --git a/framework/spree/api/endpoints/cart/index.ts b/framework/spree/api/endpoints/cart/index.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/cart/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/catalog/index.ts b/framework/spree/api/endpoints/catalog/index.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/catalog/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/catalog/products.ts b/framework/spree/api/endpoints/catalog/products.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/catalog/products.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/checkout/index.ts b/framework/spree/api/endpoints/checkout/index.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/checkout/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/customer/index.ts b/framework/spree/api/endpoints/customer/index.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/customer/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/login/index.ts b/framework/spree/api/endpoints/login/index.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/login/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/logout/index.ts b/framework/spree/api/endpoints/logout/index.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/logout/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/signup/index.ts b/framework/spree/api/endpoints/signup/index.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/signup/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/wishlist/index.tsx b/framework/spree/api/endpoints/wishlist/index.tsx new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/wishlist/index.tsx @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/index.ts b/framework/spree/api/index.ts new file mode 100644 index 0000000000..13461dcb71 --- /dev/null +++ b/framework/spree/api/index.ts @@ -0,0 +1,39 @@ +import type { APIProvider, CommerceAPI, CommerceAPIConfig } from '@commerce/api' +import { getCommerceApi as commerceApi } from '@commerce/api' +import createApiFetch from './utils/create-api-fetch' + +import getAllPages from './operations/get-all-pages' +import getPage from './operations/get-page' +import getSiteInfo from './operations/get-site-info' +import getCustomerWishlist from './operations/get-customer-wishlist' +import getAllProductPaths from './operations/get-all-product-paths' +import getAllProducts from './operations/get-all-products' +import getProduct from './operations/get-product' + +export interface SpreeApiConfig extends CommerceAPIConfig {} + +const config: SpreeApiConfig = { + commerceUrl: '', + apiToken: '', + cartCookie: '', + customerCookie: '', + cartCookieMaxAge: 2592000, + fetch: createApiFetch(() => getCommerceApi().getConfig()), +} + +const operations = { + getAllPages, + getPage, + getSiteInfo, + getCustomerWishlist, + getAllProductPaths, + getAllProducts, + getProduct, +} + +export const provider: APIProvider = { config, operations } + +export type SpreeApiProvider = APIProvider + +export const getCommerceApi = (customProvider: APIProvider = provider) => + commerceApi(customProvider) diff --git a/framework/spree/api/operations/get-all-pages.ts b/framework/spree/api/operations/get-all-pages.ts new file mode 100644 index 0000000000..4ff56b8e8e --- /dev/null +++ b/framework/spree/api/operations/get-all-pages.ts @@ -0,0 +1,37 @@ +export type Page = { url: string } +import { OperationContext, OperationOptions } from '@commerce/api/operations' +import { GetAllPagesOperation } from '@commerce/types/page' +import type { SpreeApiConfig, SpreeApiProvider } from '../index' + +export default function getAllPagesOperation({ + commerce, +}: OperationContext) { + async function getAllPages(options?: { + config?: Partial + preview?: boolean + }): Promise + + async function getAllPages( + opts: { + config?: Partial + preview?: boolean + } & OperationOptions + ): Promise + + async function getAllPages({ + config, + preview, + query, + }: { + url?: string + config?: Partial + preview?: boolean + query?: string + } = {}): Promise { + return { + pages: [], + } + } + + return getAllPages +} diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts new file mode 100644 index 0000000000..e2368a8335 --- /dev/null +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -0,0 +1,17 @@ +// import data from '../../data.json' + +export type GetAllProductPathsResult = { + products: Array<{ path: string }> +} + +export default function getAllProductPathsOperation() { + function getAllProductPaths(): Promise { + return Promise.resolve({ + // products: data.products.map(({ path }) => ({ path })), + // TODO: Return Storefront [{ path: '/long-sleeve-shirt' }, ...] from Spree products. Paths using product IDs are fine too. + products: [], + }) + } + + return getAllProductPaths +} diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts new file mode 100644 index 0000000000..d19280f4c3 --- /dev/null +++ b/framework/spree/api/operations/get-all-products.ts @@ -0,0 +1,79 @@ +import { Product } from '@commerce/types/product' +import { GetAllProductsOperation } from '@commerce/types/product' +import type { OperationContext } from '@commerce/api/operations' +import type { LocalConfig, Provider, SpreeApiProvider } from '../index' +import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +// import data from '../../../local/data.json' + +export default function getAllProductsOperation({ + commerce, +}: OperationContext) { + async function getAllProducts({ + query = 'products.list', + variables = { first: 10 }, + config: userConfig, + }: { + query?: string + variables?: T['variables'] + config?: Partial + } = {}): Promise<{ products: Product[] | any[] }> { + const config = commerce.getConfig(userConfig) + const { fetch: apiFetch /*, locale*/ } = config + const first = variables.first // How many products to fetch. + + console.log( + 'sdfuasdufahsdf variables = ', + variables, + 'query = ', + query, + 'config = ', + config + ) + + console.log('sdfasdg') + + const { data } = await apiFetch( + query, + { variables } + // { + // ...(locale && {}), + // } + ) + + console.log('asuidfhasdf', data) + + // return { + // products: data.products.edges.map(({ node }) => + // normalizeProduct(node as ShopifyProduct) + // ), + // } + + const normalizedProducts: Product[] = data.data.map((spreeProduct) => { + return { + id: spreeProduct.id, + name: spreeProduct.attributes.name, + description: spreeProduct.attributes.description, + images: [], + variants: [], + options: [], + price: { + value: 10, + currencyCode: 'USD', + retailPrice: 8, + salePrice: 7, + listPrice: 6, + extendedSalePrice: 2, + extendedListPrice: 1, + }, + } + }) + + return { + // products: data.products, + // TODO: Return Spree products. + products: normalizedProducts, + } + } + + return getAllProducts +} diff --git a/framework/spree/api/operations/get-customer-wishlist.ts b/framework/spree/api/operations/get-customer-wishlist.ts new file mode 100644 index 0000000000..8c34b9e875 --- /dev/null +++ b/framework/spree/api/operations/get-customer-wishlist.ts @@ -0,0 +1,6 @@ +export default function getCustomerWishlistOperation() { + function getCustomerWishlist(): any { + return { wishlist: {} } + } + return getCustomerWishlist +} diff --git a/framework/spree/api/operations/get-page.ts b/framework/spree/api/operations/get-page.ts new file mode 100644 index 0000000000..b0cfdf58f1 --- /dev/null +++ b/framework/spree/api/operations/get-page.ts @@ -0,0 +1,13 @@ +export type Page = any +export type GetPageResult = { page?: Page } + +export type PageVariables = { + id: number +} + +export default function getPageOperation() { + function getPage(): Promise { + return Promise.resolve({}) + } + return getPage +} diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts new file mode 100644 index 0000000000..de0804592d --- /dev/null +++ b/framework/spree/api/operations/get-product.ts @@ -0,0 +1,28 @@ +import type { LocalConfig } from '../index' +import { Product } from '@commerce/types/product' +import { GetProductOperation } from '@commerce/types/product' +import data from '../../../local/data.json' +import type { OperationContext } from '@commerce/api/operations' + +export default function getProductOperation({ + commerce, +}: OperationContext) { + async function getProduct({ + query = '', + variables, + config, + }: { + query?: string + variables?: T['variables'] + config?: Partial + preview?: boolean + } = {}): Promise { + return { + product: data.products.find(({ slug }) => slug === variables!.slug), + // TODO: Return Spree product. + // product: {}, + } + } + + return getProduct +} diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts new file mode 100644 index 0000000000..159fb6004c --- /dev/null +++ b/framework/spree/api/operations/get-site-info.ts @@ -0,0 +1,44 @@ +import { OperationContext } from '@commerce/api/operations' +import { Category } from '@commerce/types/site' +import { LocalConfig } from '../index' + +export type GetSiteInfoResult< + T extends { categories: any[]; brands: any[] } = { + categories: Category[] + brands: any[] + } +> = T + +export default function getSiteInfoOperation({}: OperationContext) { + // TODO: Get Spree categories for display in React components. + function getSiteInfo({ + query, + variables, + config: cfg, + }: { + query?: string + variables?: any + config?: Partial + preview?: boolean + } = {}): Promise { + return Promise.resolve({ + categories: [ + { + id: 'new-arrivals', + name: 'New Arrivals', + slug: 'new-arrivals', + path: '/new-arrivals', + }, + { + id: 'featured', + name: 'Featured', + slug: 'featured', + path: '/featured', + }, + ], + brands: [], + }) + } + + return getSiteInfo +} diff --git a/framework/spree/api/operations/index.ts b/framework/spree/api/operations/index.ts new file mode 100644 index 0000000000..086fdf83ae --- /dev/null +++ b/framework/spree/api/operations/index.ts @@ -0,0 +1,6 @@ +export { default as getPage } from './get-page' +export { default as getSiteInfo } from './get-site-info' +export { default as getAllPages } from './get-all-pages' +export { default as getProduct } from './get-product' +export { default as getAllProducts } from './get-all-products' +export { default as getAllProductPaths } from './get-all-product-paths' diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts new file mode 100644 index 0000000000..162e5c1d59 --- /dev/null +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -0,0 +1,154 @@ +// import { FetcherError } from '@commerce/utils/errors' +// import type { GraphQLFetcher } from '@commerce/api' +// import type { BigcommerceConfig } from '../index' + +import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' +import { SpreeApiConfig } from '..' +import { errors, makeClient } from '@spree/storefront-api-v2-sdk' +import { requireConfigValue } from 'framework/spree/isomorphicConfig' +import convertSpreeErrorToGraphQlError from 'framework/spree/utils/convertSpreeErrorToGraphQlError' +import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' +import type { + JsonApiDocument, + JsonApiListResponse, +} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +// import fetch from './fetch' + +// const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher = +// (getConfig) => +// async (query: string, { variables, preview } = {}, fetchOptions) => { +// // log.warn(query) +// const config = getConfig() +// const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { +// ...fetchOptions, +// method: 'POST', +// headers: { +// Authorization: `Bearer ${config.apiToken}`, +// ...fetchOptions?.headers, +// 'Content-Type': 'application/json', +// }, +// body: JSON.stringify({ +// query, +// variables, +// }), +// }) + +// const json = await res.json() +// if (json.errors) { +// throw new FetcherError({ +// errors: json.errors ?? [{ message: 'Failed to fetch Bigcommerce API' }], +// status: res.status, +// }) +// } + +// return { data: json.data, res } +// } + +// export default fetchGraphqlApi + +const createApiFetch: ( + getConfig: () => SpreeApiConfig +) => GraphQLFetcher< + GraphQLFetcherResult +> = (getConfig) => { + const client = makeClient({ host: requireConfigValue('spreeApiHost') }) + + // FIXME: Allow Spree SDK to use fetch instead of axios. + return async (query, queryData = {}, fetchOptions = {}) => { + const url = query + console.log('ydsfgasgdfagsdf', url) + const { variables } = queryData + let prev = null // FIXME: + const clientEndpointMethod = url + .split('.') + .reduce((clientNode: any, pathPart) => { + prev = clientNode + //FIXME: use actual type instead of any. + // TODO: Fix clientNode type + return clientNode[pathPart] + }, client) + .bind(prev) + + console.log('aisdfuiuashdf', clientEndpointMethod) + + const storeResponse: ResultResponse = + await clientEndpointMethod() // FIXME: Not the best to use variables here as it's type is any. + // await clientEndpointMethod(...variables.args) // FIXME: Not the best to use variables here as it's type is any. + + console.log('87868767868', storeResponse) + + if (storeResponse.success()) { + return { + data: storeResponse.success(), + res: storeResponse as any, //FIXME: MUST BE FETCH RESPONSE + } + } + + const storeResponseError = storeResponse.fail() + + if (storeResponseError instanceof errors.SpreeError) { + throw convertSpreeErrorToGraphQlError(storeResponseError) + } + + throw storeResponseError + // throw getError( + // [ + // { + // message: `${err} \n Most likely related to an unexpected output. e.g the store might be protected with password or not available.`, + // }, + // ], + // 500 + // ) + // console.log('jsdkfhjasdf', getConfig()) + // // await + // return { + // data: [], + // res: , + // } + } +} + +export default createApiFetch + +// LOCAL + +// fetch( +// query: string, +// queryData?: CommerceAPIFetchOptions, +// fetchOptions?: RequestInit +// ): Promise> + +// import { FetcherError } from '@commerce/utils/errors' +// import type { GraphQLFetcher } from '@commerce/api' +// import type { LocalConfig } from '../index' +// import fetch from './fetch' + +// const fetchGraphqlApi: (getConfig: () => LocalConfig) => GraphQLFetcher = +// (getConfig) => +// async (query: string, { variables, preview } = {}, fetchOptions) => { +// const config = getConfig() +// const res = await fetch(config.commerceUrl, { +// ...fetchOptions, +// method: 'POST', +// headers: { +// ...fetchOptions?.headers, +// 'Content-Type': 'application/json', +// }, +// body: JSON.stringify({ +// query, +// variables, +// }), +// }) + +// const json = await res.json() +// if (json.errors) { +// throw new FetcherError({ +// errors: json.errors ?? [{ message: 'Failed to fetch for API' }], +// status: res.status, +// }) +// } + +// return { data: json.data, res } +// } + +// export default fetchGraphqlApi diff --git a/framework/spree/api/utils/fetch.ts b/framework/spree/api/utils/fetch.ts new file mode 100644 index 0000000000..9d9fff3edc --- /dev/null +++ b/framework/spree/api/utils/fetch.ts @@ -0,0 +1,3 @@ +import zeitFetch from '@vercel/fetch' + +export default zeitFetch() diff --git a/framework/spree/auth/index.ts b/framework/spree/auth/index.ts new file mode 100644 index 0000000000..36e757a89c --- /dev/null +++ b/framework/spree/auth/index.ts @@ -0,0 +1,3 @@ +export { default as useLogin } from './use-login' +export { default as useLogout } from './use-logout' +export { default as useSignup } from './use-signup' diff --git a/framework/spree/auth/use-login.tsx b/framework/spree/auth/use-login.tsx new file mode 100644 index 0000000000..28351dc7fa --- /dev/null +++ b/framework/spree/auth/use-login.tsx @@ -0,0 +1,16 @@ +import { MutationHook } from '@commerce/utils/types' +import useLogin, { UseLogin } from '@commerce/auth/use-login' + +export default useLogin as UseLogin + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher() { + return null + }, + useHook: () => () => { + return async function () {} + }, +} diff --git a/framework/spree/auth/use-logout.tsx b/framework/spree/auth/use-logout.tsx new file mode 100644 index 0000000000..9b3fc3e44c --- /dev/null +++ b/framework/spree/auth/use-logout.tsx @@ -0,0 +1,17 @@ +import { MutationHook } from '@commerce/utils/types' +import useLogout, { UseLogout } from '@commerce/auth/use-logout' + +export default useLogout as UseLogout + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher() { + return null + }, + useHook: + ({ fetch }) => + () => + async () => {}, +} diff --git a/framework/spree/auth/use-signup.tsx b/framework/spree/auth/use-signup.tsx new file mode 100644 index 0000000000..e9ad134584 --- /dev/null +++ b/framework/spree/auth/use-signup.tsx @@ -0,0 +1,19 @@ +import { useCallback } from 'react' +import useCustomer from '../customer/use-customer' +import { MutationHook } from '@commerce/utils/types' +import useSignup, { UseSignup } from '@commerce/auth/use-signup' + +export default useSignup as UseSignup + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher() { + return null + }, + useHook: + ({ fetch }) => + () => + () => {}, +} diff --git a/framework/spree/cart/index.ts b/framework/spree/cart/index.ts new file mode 100644 index 0000000000..3b8ba990ef --- /dev/null +++ b/framework/spree/cart/index.ts @@ -0,0 +1,4 @@ +export { default as useCart } from './use-cart' +export { default as useAddItem } from './use-add-item' +export { default as useRemoveItem } from './use-remove-item' +export { default as useUpdateItem } from './use-update-item' diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx new file mode 100644 index 0000000000..7f3d1061fa --- /dev/null +++ b/framework/spree/cart/use-add-item.tsx @@ -0,0 +1,17 @@ +import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => { + return async function addItem() { + return {} + } + }, +} diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx new file mode 100644 index 0000000000..b3e509a21b --- /dev/null +++ b/framework/spree/cart/use-cart.tsx @@ -0,0 +1,42 @@ +import { useMemo } from 'react' +import { SWRHook } from '@commerce/utils/types' +import useCart, { UseCart } from '@commerce/cart/use-cart' + +export default useCart as UseCart + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher() { + return { + id: '', + createdAt: '', + currency: { code: '' }, + taxesIncluded: '', + lineItems: [], + lineItemsSubtotalPrice: '', + subtotalPrice: 0, + totalPrice: 0, + } + }, + useHook: + ({ useData }) => + (input) => { + return useMemo( + () => + Object.create( + {}, + { + isEmpty: { + get() { + return true + }, + enumerable: true, + }, + } + ), + [] + ) + }, +} diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx new file mode 100644 index 0000000000..b4ed583b84 --- /dev/null +++ b/framework/spree/cart/use-remove-item.tsx @@ -0,0 +1,18 @@ +import { MutationHook } from '@commerce/utils/types' +import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item' + +export default useRemoveItem as UseRemoveItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => { + return async function removeItem(input) { + return {} + } + }, +} diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx new file mode 100644 index 0000000000..06d703f70f --- /dev/null +++ b/framework/spree/cart/use-update-item.tsx @@ -0,0 +1,18 @@ +import { MutationHook } from '@commerce/utils/types' +import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item' + +export default useUpdateItem as UseUpdateItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => { + return async function addItem() { + return {} + } + }, +} diff --git a/framework/spree/createFetcher.ts b/framework/spree/createFetcher.ts deleted file mode 100644 index fafce34eed..0000000000 --- a/framework/spree/createFetcher.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { Fetcher } from '@commerce/utils/types' -import convertSpreeErrorToGraphQlError from './utils/convertSpreeErrorToGraphQlError' -import { makeClient } from '@spree/storefront-api-v2-sdk' -import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' -import type { - JsonApiDocument, - JsonApiListResponse, -} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' -import { errors } from '@spree/storefront-api-v2-sdk' -// import { API_TOKEN, API_URL } from './const' -// import { handleFetchResponse } from './utils' - -const createFetcher = (fetcherOptions: any): Fetcher => { - const { host } = fetcherOptions - const client = makeClient({ host }) - - //TODO: Add types to fetcherOptions - return async (requestOptions) => { - console.log('FETCHER') - // url?: string - // query?: string - // method?: string - // variables?: any - // body?: Body - const { url, method, variables, query } = requestOptions - const { locale, ...vars } = variables ?? {} - - if (!url) { - // TODO: Create a custom type for this error. - throw new Error('Url not provider for fetcher.') - } - - console.log( - `Fetching products using options: ${JSON.stringify(requestOptions)}.` - ) - - // const storeResponse = await fetch(url, { - // method, - // body: JSON.stringify({ query, variables: vars }), - // headers: { - // 'X-Shopify-Storefront-Access-Token': API_TOKEN, - // 'Content-Type': 'application/json', TODO: Probably not needed. Check! - // }, - // }) - - // const storeResponse.json() - - // if (storeResponse.ok) { - // return - // } - - // TODO: Not best to use url for finding the method, but should be good enough for now. - - const clientEndpointMethod = url - .split('.') - .reduce((clientNode: any, pathPart) => { - // TODO: Fix clientNode type - return clientNode[pathPart] - }, client) - - const storeResponse: ResultResponse = - await clientEndpointMethod(...variables.args) // TODO: Not the best to use variables here as it's type is any. - - if (storeResponse.success()) { - return storeResponse.success() - } - - if (storeResponse.fail() instanceof errors.SpreeError) { - throw convertSpreeErrorToGraphQlError(storeResponse.fail()) - } - - throw storeResponse.fail() - } -} - -// import { Fetcher } from '@commerce/utils/types' - -// export const fetcher: Fetcher = async () => { -// console.log('FETCHER') -// const res = await fetch('./data.json') -// if (res.ok) { -// const { data } = await res.json() -// return data -// } -// throw res -// } - -export default createFetcher diff --git a/framework/spree/createProvider.ts b/framework/spree/createProvider.ts deleted file mode 100644 index d8756bd737..0000000000 --- a/framework/spree/createProvider.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { Provider } from '@commerce' -import { Config } from '.' -import fetcher from './fetcher' - -// import { handler as useCart } from './cart/use-cart' -// import { handler as useAddItem } from './cart/use-add-item' -// import { handler as useUpdateItem } from './cart/use-update-item' -// import { handler as useRemoveItem } from './cart/use-remove-item' - -// import { handler as useCustomer } from './customer/use-customer' -// import { handler as useSearch } from './product/use-search' - -// import { handler as useLogin } from './auth/use-login' -// import { handler as useLogout } from './auth/use-logout' -// import { handler as useSignup } from './auth/use-signup' - -// export const saleorProvider = { -// locale: 'en-us', -// cartCookie: '', -// cartCookieToken: '', -// fetcher, -// cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, -// customer: { useCustomer }, -// products: { useSearch }, -// auth: { useLogin, useLogout, useSignup }, -// } - -export const createProvider = (options: { config: Config }): Provider => { - const { config } = options - - return { - locale: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string. - cartCookie: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string. - fetcher: createFetcher({ host: config.store.host }), - // FIXME: Add dummy hooks for below based on framework/local EXCEPT use-product - cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, - customer: { useCustomer }, - products: { useSearch }, - auth: { useLogin, useLogout, useSignup }, - } -} - -export type { Provider } diff --git a/framework/spree/customer/index.ts b/framework/spree/customer/index.ts new file mode 100644 index 0000000000..6c903ecc55 --- /dev/null +++ b/framework/spree/customer/index.ts @@ -0,0 +1 @@ +export { default as useCustomer } from './use-customer' diff --git a/framework/spree/customer/use-customer.tsx b/framework/spree/customer/use-customer.tsx new file mode 100644 index 0000000000..41757cd0d5 --- /dev/null +++ b/framework/spree/customer/use-customer.tsx @@ -0,0 +1,15 @@ +import { SWRHook } from '@commerce/utils/types' +import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' + +export default useCustomer as UseCustomer +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: () => () => { + return async function addItem() { + return {} + } + }, +} diff --git a/framework/spree/errors/MisconfigurationError.ts b/framework/spree/errors/MisconfigurationError.ts new file mode 100644 index 0000000000..0717ae4045 --- /dev/null +++ b/framework/spree/errors/MisconfigurationError.ts @@ -0,0 +1 @@ +export default class MisconfigurationError extends Error {} diff --git a/framework/spree/errors/MissingConfigurationValueError.ts b/framework/spree/errors/MissingConfigurationValueError.ts new file mode 100644 index 0000000000..02b497bf14 --- /dev/null +++ b/framework/spree/errors/MissingConfigurationValueError.ts @@ -0,0 +1 @@ +export default class MissingConfigurationValueError extends Error {} diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts new file mode 100644 index 0000000000..36e79c7a26 --- /dev/null +++ b/framework/spree/fetcher.ts @@ -0,0 +1,86 @@ +import type { Fetcher } from '@commerce/utils/types' +import convertSpreeErrorToGraphQlError from './utils/convertSpreeErrorToGraphQlError' +import { makeClient } from '@spree/storefront-api-v2-sdk' +import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' +import type { + JsonApiDocument, + JsonApiListResponse, +} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +import { errors } from '@spree/storefront-api-v2-sdk' +import { requireConfigValue } from './isomorphicConfig' +// import { handleFetchResponse } from './utils' + +const client = makeClient({ host: requireConfigValue('spreeApiHost') }) + +const fetcher: Fetcher = async (requestOptions) => { + console.log('Fetcher called') + // url?: string + // query?: string + // method?: string + // variables?: any + // body?: Body + const { url, method, variables, query } = requestOptions + const { locale, ...vars } = variables ?? {} + + if (!url) { + // TODO: Create a custom type for this error. + throw new Error('Url not provider for fetcher.') + } + + console.log( + `Fetching products using options: ${JSON.stringify(requestOptions)}.` + ) + + // const storeResponse = await fetch(url, { + // method, + // body: JSON.stringify({ query, variables: vars }), + // headers: { + // 'X-Shopify-Storefront-Access-Token': API_TOKEN, + // 'Content-Type': 'application/json', TODO: Probably not needed. Check! + // }, + // }) + + // const storeResponse.json() + + // if (storeResponse.ok) { + // return + // } + + // TODO: Not best to use url for finding the method, but should be good enough for now. + + const clientEndpointMethod = url + .split('.') + .reduce((clientNode: any, pathPart) => { + // TODO: Fix clientNode type + return clientNode[pathPart] + }, client) + + const storeResponse: ResultResponse = + await clientEndpointMethod(...variables.args) // TODO: Not the best to use variables here as it's type is any. + + if (storeResponse.success()) { + return storeResponse.success() + } + + const storeResponseError = storeResponse.fail() + + if (storeResponseError instanceof errors.SpreeError) { + throw convertSpreeErrorToGraphQlError(storeResponseError) + } + + throw storeResponseError +} + +// import { Fetcher } from '@commerce/utils/types' + +// export const fetcher: Fetcher = async () => { +// console.log('FETCHER') +// const res = await fetch('./data.json') +// if (res.ok) { +// const { data } = await res.json() +// return data +// } +// throw res +// } + +export default fetcher diff --git a/framework/spree/index.tsx b/framework/spree/index.tsx index db8865117b..afc1f91eb7 100644 --- a/framework/spree/index.tsx +++ b/framework/spree/index.tsx @@ -7,37 +7,29 @@ import { useCommerce as useCoreCommerce, } from '@commerce' -// import { provider, Provider } from './provider' -import { createProvider, Provider } from './createProvider' - -// export { provider } - -// TODO: Below is probably not needed. Expect default values to be set by NextJS Commerce and be ok for now. -// export const saleorConfig: CommerceConfig = { -// locale: 'en-us', -// cartCookie: Const.CHECKOUT_ID_COOKIE, -// } - -export type Config = { - store: { - host: string - } -} & CommerceConfig // This is the type that holds any custom values specifically for the Spree Framework. +import { provider } from './provider' +import type { Provider } from './provider' +import { requireConfigValue } from './isomorphicConfig' export type SpreeProps = { children: ReactNode provider: Provider - config: Config -} & Config + config: SpreeConfig +} & SpreeConfig -export function CommerceProvider({ children, ...config }: SpreeProps) { - console.log('CommerceProvider called') +export const spreeCommerceConfigDefaults: CommerceConfig = { + locale: requireConfigValue('defaultLocale'), + cartCookie: requireConfigValue('cartCookieName'), +} - // TODO: Make sure this doesn't get called all the time. If it does, useMemo. - const provider = createProvider({ config }) +export type SpreeConfig = CommerceConfig +export function CommerceProvider({ children, ...restProps }: SpreeProps) { return ( - + {children} ) diff --git a/framework/spree/isomorphicConfig.ts b/framework/spree/isomorphicConfig.ts new file mode 100644 index 0000000000..4d5938b789 --- /dev/null +++ b/framework/spree/isomorphicConfig.ts @@ -0,0 +1,21 @@ +import forceIsomorphicConfigValues from './utils/forceIsomorphicConfigValues' +import requireConfig from './utils/requireConfig' + +const isomorphicConfig = { + spreeApiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, + defaultLocale: process.env.NEXT_PUBLIC_SPREE_DEFAULT_LOCALE, + cartCookieName: process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_NAME, +} + +export default forceIsomorphicConfigValues( + isomorphicConfig, + ['defaultLocale', 'cartCookieName'], + ['spreeApiHost'] +) + +type IsomorphicConfig = typeof isomorphicConfig + +const requireConfigValue = (key: keyof IsomorphicConfig) => + requireConfig(isomorphicConfig, key) + +export { requireConfigValue } diff --git a/framework/spree/next.config.js b/framework/spree/next.config.js index 0bef26aba9..11b9ef289b 100644 --- a/framework/spree/next.config.js +++ b/framework/spree/next.config.js @@ -2,12 +2,7 @@ const commerce = require('./commerce.config.json') module.exports = { commerce, - store: { - host: process.env.SPREE_API_HOST, - }, // images: { // domains: [process.env.COMMERCE_IMAGE_HOST], // }, - // locale: 'en-us', - // cartCookie: Const.CHECKOUT_ID_COOKIE, } diff --git a/framework/spree/product/index.ts b/framework/spree/product/index.ts new file mode 100644 index 0000000000..426a3edcd5 --- /dev/null +++ b/framework/spree/product/index.ts @@ -0,0 +1,2 @@ +export { default as usePrice } from './use-price' +export { default as useSearch } from './use-search' diff --git a/framework/spree/product/use-price.tsx b/framework/spree/product/use-price.tsx new file mode 100644 index 0000000000..0174faf5e8 --- /dev/null +++ b/framework/spree/product/use-price.tsx @@ -0,0 +1,2 @@ +export * from '@commerce/product/use-price' +export { default } from '@commerce/product/use-price' diff --git a/framework/spree/provider.ts b/framework/spree/provider.ts new file mode 100644 index 0000000000..3a96f456d8 --- /dev/null +++ b/framework/spree/provider.ts @@ -0,0 +1,28 @@ +import type { Provider } from '@commerce' +import fetcher from './fetcher' + +// TODO: Using dummy hooks to fetch static content. Based on the local framework. +import { handler as useCart } from './cart/use-cart' +import { handler as useAddItem } from './cart/use-add-item' +import { handler as useUpdateItem } from './cart/use-update-item' +import { handler as useRemoveItem } from './cart/use-remove-item' +import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' +import { handler as useLogin } from './auth/use-login' +import { handler as useLogout } from './auth/use-logout' +import { handler as useSignup } from './auth/use-signup' + +const provider = { + locale: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string. + cartCookie: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string. + fetcher, + // FIXME: Add dummy hooks for below based on framework/local EXCEPT use-product + cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, + customer: { useCustomer }, + products: { useSearch }, + auth: { useLogin, useLogout, useSignup }, +} + +export { provider } + +export type { Provider } diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts new file mode 100644 index 0000000000..5ddeee8e54 --- /dev/null +++ b/framework/spree/types/index.ts @@ -0,0 +1,5 @@ +export type UnknownObjectValues = Record + +export type NonUndefined = T extends undefined ? never : T + +export type ValueOf = T[keyof T] diff --git a/framework/spree/utils/forceIsomorphicConfigValues.ts b/framework/spree/utils/forceIsomorphicConfigValues.ts new file mode 100644 index 0000000000..94c38c2af1 --- /dev/null +++ b/framework/spree/utils/forceIsomorphicConfigValues.ts @@ -0,0 +1,43 @@ +import type { NonUndefined, UnknownObjectValues } from '../types' +import MisconfigurationError from '../errors/MisconfigurationError' +import isServer from './isServer' + +const generateMisconfigurationErrorMessage = ( + keys: Array +) => `${keys.join(', ')} must have values before running the Framework.` + +const forceIsomorphicConfigValues = < + X extends keyof T, + T extends UnknownObjectValues, + H extends Record> +>( + config: T, + requiredServerKeys: string[], + requiredPublicKeys: X[] +) => { + if (isServer) { + const missingServerConfigValues = requiredServerKeys.filter( + (requiredServerKey) => typeof config[requiredServerKey] === 'undefined' + ) + + if (missingServerConfigValues.length > 0) { + throw new MisconfigurationError( + generateMisconfigurationErrorMessage(missingServerConfigValues) + ) + } + } + + const missingPublicConfigValues = requiredPublicKeys.filter( + (requiredPublicKey) => typeof config[requiredPublicKey] === 'undefined' + ) + + if (missingPublicConfigValues.length > 0) { + throw new MisconfigurationError( + generateMisconfigurationErrorMessage(missingPublicConfigValues) + ) + } + + return config as T & H +} + +export default forceIsomorphicConfigValues diff --git a/framework/spree/utils/isServer.ts b/framework/spree/utils/isServer.ts new file mode 100644 index 0000000000..4544a48840 --- /dev/null +++ b/framework/spree/utils/isServer.ts @@ -0,0 +1 @@ +export default typeof window === 'undefined' diff --git a/framework/spree/utils/requireConfig.ts b/framework/spree/utils/requireConfig.ts new file mode 100644 index 0000000000..92b7916ca4 --- /dev/null +++ b/framework/spree/utils/requireConfig.ts @@ -0,0 +1,16 @@ +import MissingConfigurationValueError from '../errors/MissingConfigurationValueError' +import type { NonUndefined, ValueOf } from '../types' + +const requireConfig = (isomorphicConfig: T, key: keyof T) => { + const valueUnderKey = isomorphicConfig[key] + + if (typeof valueUnderKey === 'undefined') { + throw new MissingConfigurationValueError( + `Value for configuration key ${key} was undefined.` + ) + } + + return valueUnderKey as NonUndefined> +} + +export default requireConfig diff --git a/framework/spree/wishlist/use-add-item.tsx b/framework/spree/wishlist/use-add-item.tsx new file mode 100644 index 0000000000..75f067c3a3 --- /dev/null +++ b/framework/spree/wishlist/use-add-item.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/spree/wishlist/use-remove-item.tsx b/framework/spree/wishlist/use-remove-item.tsx new file mode 100644 index 0000000000..a2d3a8a052 --- /dev/null +++ b/framework/spree/wishlist/use-remove-item.tsx @@ -0,0 +1,17 @@ +import { useCallback } from 'react' + +type Options = { + includeProducts?: boolean +} + +export function emptyHook(options?: Options) { + const useEmptyHook = async ({ id }: { id: string | number }) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/spree/wishlist/use-wishlist.tsx b/framework/spree/wishlist/use-wishlist.tsx new file mode 100644 index 0000000000..9fe0e758f3 --- /dev/null +++ b/framework/spree/wishlist/use-wishlist.tsx @@ -0,0 +1,43 @@ +import { HookFetcher } from '@commerce/utils/types' +import type { Product } from '@commerce/types/product' + +const defaultOpts = {} + +export type Wishlist = { + items: [ + { + product_id: number + variant_id: number + id: number + product: Product + } + ] +} + +export interface UseWishlistOptions { + includeProducts?: boolean +} + +export interface UseWishlistInput extends UseWishlistOptions { + customerId?: number +} + +export const fetcher: HookFetcher = () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + // swrOptions?: SwrOptions + swrOptions?: any +) { + const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { + return { data: null } + } + + useWishlist.extend = extendHook + + return useWishlist +} + +export default extendHook(fetcher) diff --git a/package.json b/package.json index 6451806ec2..832b9c8fd6 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "nextjs-commerce", "version": "1.0.0", "scripts": { - "dev": "NODE_OPTIONS='--inspect' next dev", + "dev": "NODE_OPTIONS='--inspect' next dev -p 4000", "build": "next build", - "start": "next start", + "start": "next start -p 4000", "analyze": "BUNDLE_ANALYZE=both yarn build", "prettier-fix": "prettier --write .", "find:unused": "npx next-unused", diff --git a/tsconfig.json b/tsconfig.json index 340929669f..b06e78b379 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,8 +23,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/local"], - "@framework/*": ["framework/local/*"] + "@framework": ["framework/spree"], + "@framework/*": ["framework/spree/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 14e7a4fe088df0f2460e12345d8431e3559f002c Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 27 Jul 2021 17:42:05 +0200 Subject: [PATCH 04/98] Fetch product images, standardize API fetch using Spree SDK --- framework/spree/.env.template | 7 +- framework/spree/api/index.ts | 2 +- .../spree/api/operations/get-all-products.ts | 176 ++++++++++++------ framework/spree/api/utils/create-api-fetch.ts | 150 +++------------ .../spree/errors/SpreeResponseContentError.ts | 1 + .../SpreeSdkMethodFromEndpointPathError.ts | 1 + framework/spree/fetcher.ts | 29 +-- framework/spree/isomorphicConfig.ts | 5 +- framework/spree/next.config.js | 6 +- framework/spree/types/index.ts | 30 +++ .../spree/utils/createGetAbsoluteImageUrl.ts | 20 ++ framework/spree/utils/expandOptions.ts | 89 +++++++++ .../utils/forceIsomorphicConfigValues.ts | 2 +- framework/spree/utils/getImageUrl.ts | 44 +++++ framework/spree/utils/getMediaGallery.ts | 30 +++ .../getSpreeSdkMethodFromEndpointPath.ts | 56 ++++++ framework/spree/utils/jsonApi.ts | 46 +++++ 17 files changed, 482 insertions(+), 212 deletions(-) create mode 100644 framework/spree/errors/SpreeResponseContentError.ts create mode 100644 framework/spree/errors/SpreeSdkMethodFromEndpointPathError.ts create mode 100644 framework/spree/utils/createGetAbsoluteImageUrl.ts create mode 100644 framework/spree/utils/expandOptions.ts create mode 100644 framework/spree/utils/getImageUrl.ts create mode 100644 framework/spree/utils/getMediaGallery.ts create mode 100644 framework/spree/utils/getSpreeSdkMethodFromEndpointPath.ts create mode 100644 framework/spree/utils/jsonApi.ts diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 8831ec06cf..42de48b17a 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -4,10 +4,7 @@ COMMERCE_PROVIDER=spree {# public (available in the web browser) #} NEXT_PUBLIC_SPREE_API_HOST=http://localhost:3000 - -{# private #} NEXT_PUBLIC_SPREE_DEFAULT_LOCALE=en-us NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart - -{# # TODO: #} -{# # COMMERCE_IMAGE_HOST #} +NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:3000 +NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost diff --git a/framework/spree/api/index.ts b/framework/spree/api/index.ts index 13461dcb71..c8bac3bf1e 100644 --- a/framework/spree/api/index.ts +++ b/framework/spree/api/index.ts @@ -1,4 +1,4 @@ -import type { APIProvider, CommerceAPI, CommerceAPIConfig } from '@commerce/api' +import type { APIProvider, CommerceAPIConfig } from '@commerce/api' import { getCommerceApi as commerceApi } from '@commerce/api' import createApiFetch from './utils/create-api-fetch' diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index d19280f4c3..1791b5ac86 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -1,78 +1,142 @@ -import { Product } from '@commerce/types/product' -import { GetAllProductsOperation } from '@commerce/types/product' +import type { + Product, + ProductOption, + ProductOptionValues, + ProductPrice, + ProductVariant, +} from '@commerce/types/product' +import type { GetAllProductsOperation } from '@commerce/types/product' import type { OperationContext } from '@commerce/api/operations' -import type { LocalConfig, Provider, SpreeApiProvider } from '../index' import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' -// import data from '../../../local/data.json' +import { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' +import type { SpreeApiConfig, SpreeApiProvider } from '../index' +import type { SpreeSdkVariables } from 'framework/spree/types' +import { findIncluded, findIncludedOfType } from 'framework/spree/utils/jsonApi' +import getMediaGallery from 'framework/spree/utils/getMediaGallery' +import createGetAbsoluteImageUrl from 'framework/spree/utils/createGetAbsoluteImageUrl' +import { requireConfigValue } from 'framework/spree/isomorphicConfig' +import SpreeResponseContentError from 'framework/spree/errors/SpreeResponseContentError' +import expandOptions from 'framework/spree/utils/expandOptions' export default function getAllProductsOperation({ commerce, }: OperationContext) { async function getAllProducts({ - query = 'products.list', - variables = { first: 10 }, + variables: getAllProductsVariables = {}, config: userConfig, }: { - query?: string variables?: T['variables'] - config?: Partial + config?: Partial } = {}): Promise<{ products: Product[] | any[] }> { - const config = commerce.getConfig(userConfig) - const { fetch: apiFetch /*, locale*/ } = config - const first = variables.first // How many products to fetch. - - console.log( - 'sdfuasdufahsdf variables = ', - variables, - 'query = ', - query, - 'config = ', - config + console.info( + 'getAllProducts called. Configuration: ', + 'getAllProductsVariables: ', + getAllProductsVariables, + 'config: ', + userConfig ) - console.log('sdfasdg') + const first = getAllProductsVariables.first + const variables: SpreeSdkVariables = { + methodPath: 'products.list', + arguments: [ + { + include: 'variants,images,option_types,variants.option_values', + per_page: first, + }, + ], + } + + const config = commerce.getConfig(userConfig) + const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. - const { data } = await apiFetch( - query, + const { data: spreeSuccessResponse } = await apiFetch( + '__UNUSED__', { variables } - // { - // ...(locale && {}), - // } ) - console.log('asuidfhasdf', data) - - // return { - // products: data.products.edges.map(({ node }) => - // normalizeProduct(node as ShopifyProduct) - // ), - // } - - const normalizedProducts: Product[] = data.data.map((spreeProduct) => { - return { - id: spreeProduct.id, - name: spreeProduct.attributes.name, - description: spreeProduct.attributes.description, - images: [], - variants: [], - options: [], - price: { - value: 10, - currencyCode: 'USD', - retailPrice: 8, - salePrice: 7, - listPrice: 6, - extendedSalePrice: 2, - extendedListPrice: 1, - }, + const normalizedProducts: Product[] = spreeSuccessResponse.data.map( + (spreeProduct) => { + const spreeImageRecords = findIncludedOfType( + spreeSuccessResponse, + spreeProduct, + 'images' + ) + + const images = getMediaGallery( + spreeImageRecords, + createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost')) + ) + + const price: ProductPrice = { + value: parseFloat(spreeProduct.attributes.price), + currencyCode: spreeProduct.attributes.currency, + } + + // TODO: Add sku to product object equal to master SKU from Spree. + // Currently, the Spree API doesn't return it. + + const hasNonMasterVariants = + (spreeProduct.relationships.variants.data as RelationType[]).length > + 0 + + let variants: ProductVariant[] + let options: ProductOption[] = [] + + if (hasNonMasterVariants) { + const spreeVariantRecords = findIncludedOfType( + spreeSuccessResponse, + spreeProduct, + 'variants' + ) + + variants = spreeVariantRecords.map((spreeVariantRecord) => { + const spreeOptionValues = findIncludedOfType( + spreeSuccessResponse, + spreeVariantRecord, + 'option_values' + ) + + let variantOptions: ProductOption[] = [] + + // Only include options which are used by variants. + + spreeOptionValues.forEach((spreeOptionValue) => { + variantOptions = expandOptions( + spreeSuccessResponse, + spreeOptionValue, + variantOptions + ) + + options = expandOptions( + spreeSuccessResponse, + spreeOptionValue, + options + ) + }) + + return { + id: spreeVariantRecord.id, + options: variantOptions, + } + }) + } else { + variants = [] + } + + return { + id: spreeProduct.id, + name: spreeProduct.attributes.name, + description: spreeProduct.attributes.description, + images, + variants, + options, + price, + } } - }) + ) - return { - // products: data.products, - // TODO: Return Spree products. - products: normalizedProducts, - } + return { products: normalizedProducts } } return getAllProducts diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index 162e5c1d59..68652aca20 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -1,7 +1,3 @@ -// import { FetcherError } from '@commerce/utils/errors' -// import type { GraphQLFetcher } from '@commerce/api' -// import type { BigcommerceConfig } from '../index' - import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' import { SpreeApiConfig } from '..' import { errors, makeClient } from '@spree/storefront-api-v2-sdk' @@ -9,81 +5,54 @@ import { requireConfigValue } from 'framework/spree/isomorphicConfig' import convertSpreeErrorToGraphQlError from 'framework/spree/utils/convertSpreeErrorToGraphQlError' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { - JsonApiDocument, JsonApiListResponse, + JsonApiResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' -// import fetch from './fetch' - -// const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher = -// (getConfig) => -// async (query: string, { variables, preview } = {}, fetchOptions) => { -// // log.warn(query) -// const config = getConfig() -// const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { -// ...fetchOptions, -// method: 'POST', -// headers: { -// Authorization: `Bearer ${config.apiToken}`, -// ...fetchOptions?.headers, -// 'Content-Type': 'application/json', -// }, -// body: JSON.stringify({ -// query, -// variables, -// }), -// }) - -// const json = await res.json() -// if (json.errors) { -// throw new FetcherError({ -// errors: json.errors ?? [{ message: 'Failed to fetch Bigcommerce API' }], -// status: res.status, -// }) -// } - -// return { data: json.data, res } -// } - -// export default fetchGraphqlApi +import getSpreeSdkMethodFromEndpointPath from 'framework/spree/utils/getSpreeSdkMethodFromEndpointPath' +import { SpreeSdkVariables } from 'framework/spree/types' +import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' const createApiFetch: ( getConfig: () => SpreeApiConfig -) => GraphQLFetcher< - GraphQLFetcherResult -> = (getConfig) => { +) => GraphQLFetcher, SpreeSdkVariables> = ( + getConfig +) => { const client = makeClient({ host: requireConfigValue('spreeApiHost') }) - // FIXME: Allow Spree SDK to use fetch instead of axios. - return async (query, queryData = {}, fetchOptions = {}) => { - const url = query - console.log('ydsfgasgdfagsdf', url) - const { variables } = queryData - let prev = null // FIXME: - const clientEndpointMethod = url - .split('.') - .reduce((clientNode: any, pathPart) => { - prev = clientNode - //FIXME: use actual type instead of any. - // TODO: Fix clientNode type - return clientNode[pathPart] - }, client) - .bind(prev) + return async (url, queryData = {}, fetchOptions = {}) => { + console.log( + 'apiFetch called. query = ', + url, + 'url = ', + queryData, + 'fetchOptions = ', + fetchOptions + ) - console.log('aisdfuiuashdf', clientEndpointMethod) + const { variables } = queryData - const storeResponse: ResultResponse = - await clientEndpointMethod() // FIXME: Not the best to use variables here as it's type is any. - // await clientEndpointMethod(...variables.args) // FIXME: Not the best to use variables here as it's type is any. + if (!variables) { + throw new SpreeSdkMethodFromEndpointPathError( + `Required SpreeSdkVariables not provided.` + ) + } - console.log('87868767868', storeResponse) + const storeResponse: ResultResponse = + await getSpreeSdkMethodFromEndpointPath( + client, + variables.methodPath + )(...variables.arguments) if (storeResponse.success()) { return { data: storeResponse.success(), - res: storeResponse as any, //FIXME: MUST BE FETCH RESPONSE + res: storeResponse as any, //FIXME: MUST BE fetch() RESPONSE instead of axios. } } + // FIXME: Allow Spree SDK to use fetch instead of axios + // (https://github.com/spree/spree-storefront-api-v2-js-sdk/issues/189) + const storeResponseError = storeResponse.fail() if (storeResponseError instanceof errors.SpreeError) { @@ -91,64 +60,7 @@ const createApiFetch: ( } throw storeResponseError - // throw getError( - // [ - // { - // message: `${err} \n Most likely related to an unexpected output. e.g the store might be protected with password or not available.`, - // }, - // ], - // 500 - // ) - // console.log('jsdkfhjasdf', getConfig()) - // // await - // return { - // data: [], - // res: , - // } } } export default createApiFetch - -// LOCAL - -// fetch( -// query: string, -// queryData?: CommerceAPIFetchOptions, -// fetchOptions?: RequestInit -// ): Promise> - -// import { FetcherError } from '@commerce/utils/errors' -// import type { GraphQLFetcher } from '@commerce/api' -// import type { LocalConfig } from '../index' -// import fetch from './fetch' - -// const fetchGraphqlApi: (getConfig: () => LocalConfig) => GraphQLFetcher = -// (getConfig) => -// async (query: string, { variables, preview } = {}, fetchOptions) => { -// const config = getConfig() -// const res = await fetch(config.commerceUrl, { -// ...fetchOptions, -// method: 'POST', -// headers: { -// ...fetchOptions?.headers, -// 'Content-Type': 'application/json', -// }, -// body: JSON.stringify({ -// query, -// variables, -// }), -// }) - -// const json = await res.json() -// if (json.errors) { -// throw new FetcherError({ -// errors: json.errors ?? [{ message: 'Failed to fetch for API' }], -// status: res.status, -// }) -// } - -// return { data: json.data, res } -// } - -// export default fetchGraphqlApi diff --git a/framework/spree/errors/SpreeResponseContentError.ts b/framework/spree/errors/SpreeResponseContentError.ts new file mode 100644 index 0000000000..19c10cf2e9 --- /dev/null +++ b/framework/spree/errors/SpreeResponseContentError.ts @@ -0,0 +1 @@ +export default class SpreeResponseContentError extends Error {} diff --git a/framework/spree/errors/SpreeSdkMethodFromEndpointPathError.ts b/framework/spree/errors/SpreeSdkMethodFromEndpointPathError.ts new file mode 100644 index 0000000000..bf15aada0d --- /dev/null +++ b/framework/spree/errors/SpreeSdkMethodFromEndpointPathError.ts @@ -0,0 +1 @@ +export default class SpreeSdkMethodFromEndpointPathError extends Error {} diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 36e79c7a26..48834cf32e 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -3,11 +3,12 @@ import convertSpreeErrorToGraphQlError from './utils/convertSpreeErrorToGraphQlE import { makeClient } from '@spree/storefront-api-v2-sdk' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { - JsonApiDocument, JsonApiListResponse, + JsonApiResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import { errors } from '@spree/storefront-api-v2-sdk' import { requireConfigValue } from './isomorphicConfig' +import getSpreeSdkMethodFromEndpointPath from './utils/getSpreeSdkMethodFromEndpointPath' // import { handleFetchResponse } from './utils' const client = makeClient({ host: requireConfigValue('spreeApiHost') }) @@ -31,32 +32,10 @@ const fetcher: Fetcher = async (requestOptions) => { `Fetching products using options: ${JSON.stringify(requestOptions)}.` ) - // const storeResponse = await fetch(url, { - // method, - // body: JSON.stringify({ query, variables: vars }), - // headers: { - // 'X-Shopify-Storefront-Access-Token': API_TOKEN, - // 'Content-Type': 'application/json', TODO: Probably not needed. Check! - // }, - // }) - - // const storeResponse.json() - - // if (storeResponse.ok) { - // return - // } - // TODO: Not best to use url for finding the method, but should be good enough for now. - const clientEndpointMethod = url - .split('.') - .reduce((clientNode: any, pathPart) => { - // TODO: Fix clientNode type - return clientNode[pathPart] - }, client) - - const storeResponse: ResultResponse = - await clientEndpointMethod(...variables.args) // TODO: Not the best to use variables here as it's type is any. + const storeResponse: ResultResponse = + await getSpreeSdkMethodFromEndpointPath(client, url)(...variables.args) // TODO: Not the best to use variables here as it's type is any. if (storeResponse.success()) { return storeResponse.success() diff --git a/framework/spree/isomorphicConfig.ts b/framework/spree/isomorphicConfig.ts index 4d5938b789..650125842b 100644 --- a/framework/spree/isomorphicConfig.ts +++ b/framework/spree/isomorphicConfig.ts @@ -5,12 +5,13 @@ const isomorphicConfig = { spreeApiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, defaultLocale: process.env.NEXT_PUBLIC_SPREE_DEFAULT_LOCALE, cartCookieName: process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_NAME, + spreeImageHost: process.env.NEXT_PUBLIC_SPREE_IMAGE_HOST, } export default forceIsomorphicConfigValues( isomorphicConfig, - ['defaultLocale', 'cartCookieName'], - ['spreeApiHost'] + [], + ['spreeApiHost', 'defaultLocale', 'cartCookieName', 'spreeImageHost'] ) type IsomorphicConfig = typeof isomorphicConfig diff --git a/framework/spree/next.config.js b/framework/spree/next.config.js index 11b9ef289b..13d530a49f 100644 --- a/framework/spree/next.config.js +++ b/framework/spree/next.config.js @@ -2,7 +2,7 @@ const commerce = require('./commerce.config.json') module.exports = { commerce, - // images: { - // domains: [process.env.COMMERCE_IMAGE_HOST], - // }, + images: { + domains: [process.env.NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN], + }, } diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 5ddeee8e54..6b5e98b05a 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -1,5 +1,35 @@ +import type { + JsonApiDocument, + JsonApiListResponse, + JsonApiSingleResponse, +} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' + export type UnknownObjectValues = Record export type NonUndefined = T extends undefined ? never : T export type ValueOf = T[keyof T] + +export type SpreeSdkMethodReturnType = Promise< + ResultResponse +> + +export type SpreeSdkMethod = (...args: any[]) => SpreeSdkMethodReturnType + +export type SpreeSdkVariables = { + methodPath: string + arguments: any[] +} + +export interface ImageStyle { + url: string + width: string + height: string +} + +export interface SpreeProductImage extends JsonApiDocument { + attributes: { + styles: ImageStyle[] + } +} diff --git a/framework/spree/utils/createGetAbsoluteImageUrl.ts b/framework/spree/utils/createGetAbsoluteImageUrl.ts new file mode 100644 index 0000000000..37932f15d9 --- /dev/null +++ b/framework/spree/utils/createGetAbsoluteImageUrl.ts @@ -0,0 +1,20 @@ +import { SpreeProductImage } from '../types' +import getImageUrl from './getImageUrl' + +const createGetAbsoluteImageUrl = + (host: string) => + ( + image: SpreeProductImage, + minWidth: number, + minHeight: number + ): string | null => { + const url = getImageUrl(image, minWidth, minHeight) + + if (url === null) { + return null + } + + return `${host}${url}` + } + +export default createGetAbsoluteImageUrl diff --git a/framework/spree/utils/expandOptions.ts b/framework/spree/utils/expandOptions.ts new file mode 100644 index 0000000000..75f4089e8c --- /dev/null +++ b/framework/spree/utils/expandOptions.ts @@ -0,0 +1,89 @@ +import type { + ProductOption, + ProductOptionValues, +} from '@commerce/types/product' +import type { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' +import SpreeResponseContentError from '../errors/SpreeResponseContentError' +import { findIncluded } from './jsonApi' + +const isColorProductOption = (productOption: ProductOption) => + productOption.displayName === 'Color' + +const expandOptions = ( + spreeSuccessResponse: IProducts, + spreeOptionValue: JsonApiDocument, + accumulatedOptions: ProductOption[] +): ProductOption[] => { + const spreeOptionTypeIdentifier = spreeOptionValue.relationships.option_type + .data as RelationType + + const existingOptionIndex = accumulatedOptions.findIndex( + (option) => option.id == spreeOptionTypeIdentifier.id + ) + + let option: ProductOption + + if (existingOptionIndex === -1) { + const spreeOptionType = findIncluded( + spreeSuccessResponse, + spreeOptionTypeIdentifier.type, + spreeOptionTypeIdentifier.id + ) + + if (!spreeOptionType) { + throw new SpreeResponseContentError( + `Option type with id ${spreeOptionTypeIdentifier.id} not found.` + ) + } + + option = { + id: spreeOptionType.id, + displayName: spreeOptionType.attributes.presentation, + values: [], + } + } else { + const existingOption = accumulatedOptions[existingOptionIndex] + + option = existingOption + } + + let optionValue: ProductOptionValues + + const label = isColorProductOption(option) + ? spreeOptionValue.attributes.name + : spreeOptionValue.attributes.presentation + + const productOptionValueExists = option.values.some( + (optionValue: ProductOptionValues) => optionValue.label === label + ) + + if (!productOptionValueExists) { + if (isColorProductOption(option)) { + optionValue = { + label, + hexColors: [spreeOptionValue.attributes.presentation], + } + } else { + optionValue = { + label, + } + } + + const expandedOptionValues = [...option.values, optionValue] + + const expandedOptions = [...accumulatedOptions] + + expandedOptions[existingOptionIndex] = { + ...option, + values: expandedOptionValues, + } + + return expandedOptions + } + + return accumulatedOptions +} + +export default expandOptions diff --git a/framework/spree/utils/forceIsomorphicConfigValues.ts b/framework/spree/utils/forceIsomorphicConfigValues.ts index 94c38c2af1..359ec51670 100644 --- a/framework/spree/utils/forceIsomorphicConfigValues.ts +++ b/framework/spree/utils/forceIsomorphicConfigValues.ts @@ -4,7 +4,7 @@ import isServer from './isServer' const generateMisconfigurationErrorMessage = ( keys: Array -) => `${keys.join(', ')} must have values before running the Framework.` +) => `${keys.join(', ')} must have a value before running the Framework.` const forceIsomorphicConfigValues = < X extends keyof T, diff --git a/framework/spree/utils/getImageUrl.ts b/framework/spree/utils/getImageUrl.ts new file mode 100644 index 0000000000..8594f5c344 --- /dev/null +++ b/framework/spree/utils/getImageUrl.ts @@ -0,0 +1,44 @@ +// Based on https://github.com/spark-solutions/spree2vuestorefront/blob/d88d85ae1bcd2ec99b13b81cd2e3c25600a0216e/src/utils/index.ts + +import type { ImageStyle, SpreeProductImage } from '../types' + +const getImageUrl = ( + image: SpreeProductImage, + minWidth: number, + _: number +): string | null => { + // every image is still resized in vue-storefront-api, no matter what getImageUrl returns + if (image) { + const { + attributes: { styles }, + } = image + const bestStyleIndex = styles.reduce( + (bSIndex: number | null, style: ImageStyle, styleIndex: number) => { + // assuming all images are the same dimensions, just scaled + if (bSIndex === null) { + return 0 + } + const bestStyle = styles[bSIndex] + const widthDiff = +bestStyle.width - minWidth + const minWidthDiff = +style.width - minWidth + if (widthDiff < 0 && minWidthDiff > 0) { + return styleIndex + } + if (widthDiff > 0 && minWidthDiff < 0) { + return bSIndex + } + return Math.abs(widthDiff) < Math.abs(minWidthDiff) + ? bSIndex + : styleIndex + }, + null + ) + + if (bestStyleIndex !== null) { + return styles[bestStyleIndex].url + } + } + return null +} + +export default getImageUrl diff --git a/framework/spree/utils/getMediaGallery.ts b/framework/spree/utils/getMediaGallery.ts new file mode 100644 index 0000000000..fe5587aa20 --- /dev/null +++ b/framework/spree/utils/getMediaGallery.ts @@ -0,0 +1,30 @@ +// Based on https://github.com/spark-solutions/spree2vuestorefront/blob/d88d85ae1bcd2ec99b13b81cd2e3c25600a0216e/src/utils/index.ts + +import type { ProductImage } from '@commerce/types/product' +import type { SpreeProductImage } from '../types' + +const getMediaGallery = ( + images: SpreeProductImage[], + getImageUrl: ( + image: SpreeProductImage, + minWidth: number, + minHeight: number + ) => string | null +) => { + return images.reduce((productImages, _, imageIndex) => { + const imageUrl = getImageUrl(images[imageIndex], 9001, 9001) + + if (imageUrl) { + return [ + ...productImages, + { + url: imageUrl, + }, + ] + } + + return productImages + }, []) +} + +export default getMediaGallery diff --git a/framework/spree/utils/getSpreeSdkMethodFromEndpointPath.ts b/framework/spree/utils/getSpreeSdkMethodFromEndpointPath.ts new file mode 100644 index 0000000000..f1ce31f86a --- /dev/null +++ b/framework/spree/utils/getSpreeSdkMethodFromEndpointPath.ts @@ -0,0 +1,56 @@ +import type { Client } from '@spree/storefront-api-v2-sdk' +import SpreeSdkMethodFromEndpointPathError from '../errors/SpreeSdkMethodFromEndpointPathError' +import { SpreeSdkMethod } from '../types' + +const getSpreeSdkMethodFromEndpointPath = < + ExactSpreeSdkClientType extends Client +>( + client: ExactSpreeSdkClientType, + path: string +) => { + const pathParts = path.split('.') + const reachedPath: string[] = [] + let node = >client + + console.log(`Looking for ${path} in Spree Sdk.`) + + while (reachedPath.length < pathParts.length - 1) { + const checkedPathPart = pathParts[reachedPath.length] + const checkedNode = node[checkedPathPart] + + console.log(`Checking part ${checkedPathPart}.`) + + if (typeof checkedNode !== 'object') { + throw new SpreeSdkMethodFromEndpointPathError( + `Couldn't reach ${path}. Farthest path reached was: ${reachedPath.join( + '.' + )}.` + ) + } + + if (checkedNode === null) { + throw new SpreeSdkMethodFromEndpointPathError( + `Path ${path} doesn't exist.` + ) + } + + node = >checkedNode + reachedPath.push(checkedPathPart) + } + + if ( + reachedPath.length !== pathParts.length - 1 || + typeof node[pathParts[reachedPath.length]] !== 'function' + ) { + throw new SpreeSdkMethodFromEndpointPathError( + `Couldn't reach ${path}. Farthest path reached was: ${reachedPath.join( + '.' + )}.` + ) + } + + return (...args: any[]) => + (node[pathParts[reachedPath.length]] as SpreeSdkMethod)(...args) +} + +export default getSpreeSdkMethodFromEndpointPath diff --git a/framework/spree/utils/jsonApi.ts b/framework/spree/utils/jsonApi.ts new file mode 100644 index 0000000000..6dc30f89f8 --- /dev/null +++ b/framework/spree/utils/jsonApi.ts @@ -0,0 +1,46 @@ +// Based on https://github.com/spark-solutions/spree2vuestorefront + +import type { + JsonApiResponse, + JsonApiDocument, +} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' + +export const findIncluded = ( + response: JsonApiResponse, + objectType: string, + objectId: string +): T | null => { + if (!response.included) { + return null + } + + return ( + (response.included.find( + (includedObject) => + includedObject.type === objectType && includedObject.id === objectId + ) as T) || null + ) +} + +export const findIncludedOfType = ( + response: JsonApiResponse, + singlePrimaryRecord: JsonApiDocument, + objectRelationshipType: string +): T[] => { + if (!response.included) { + return [] + } + + const typeRelationships = + singlePrimaryRecord.relationships[objectRelationshipType] + + if (!typeRelationships) { + return [] + } + + return typeRelationships.data + .map((typeObject: JsonApiDocument) => + findIncluded(response, typeObject.type, typeObject.id) + ) + .filter((typeRecord: JsonApiDocument | null) => !!typeRecord) +} From 24e635b048328a275d3683c2cb65ac2a53f9bc18 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 13:35:15 +0200 Subject: [PATCH 05/98] Include slug and path in products --- framework/spree/api/operations/get-all-products.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 1791b5ac86..2e74eb8b50 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -124,6 +124,9 @@ export default function getAllProductsOperation({ variants = [] } + const slug = spreeProduct.attributes.slug + const path = `/${spreeProduct.attributes.slug}` + return { id: spreeProduct.id, name: spreeProduct.attributes.name, @@ -132,6 +135,8 @@ export default function getAllProductsOperation({ variants, options, price, + slug, + path, } } ) From a7a75e7f6918afc07d166e6a324f009cb1071940 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 14:05:46 +0200 Subject: [PATCH 06/98] Fetch single product during build time --- .../api/operations/get-all-product-paths.ts | 2 + .../spree/api/operations/get-all-products.ts | 121 ++++-------------- framework/spree/api/operations/get-product.ts | 76 +++++++++-- .../spree/errors/MissingSlugVariableError.ts | 1 + framework/spree/utils/expandOptions.ts | 8 +- framework/spree/utils/normalizeProduct.ts | 100 +++++++++++++++ 6 files changed, 193 insertions(+), 115 deletions(-) create mode 100644 framework/spree/errors/MissingSlugVariableError.ts create mode 100644 framework/spree/utils/normalizeProduct.ts diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts index e2368a8335..b96f7008a5 100644 --- a/framework/spree/api/operations/get-all-product-paths.ts +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -6,6 +6,8 @@ export type GetAllProductPathsResult = { export default function getAllProductPathsOperation() { function getAllProductPaths(): Promise { + console.log('getAllProductPaths called.') + return Promise.resolve({ // products: data.products.map(({ path }) => ({ path })), // TODO: Return Storefront [{ path: '/long-sleeve-shirt' }, ...] from Spree products. Paths using product IDs are fine too. diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 2e74eb8b50..3572ee2d29 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -1,33 +1,38 @@ -import type { - Product, - ProductOption, - ProductOptionValues, - ProductPrice, - ProductVariant, -} from '@commerce/types/product' +import type { Product } from '@commerce/types/product' import type { GetAllProductsOperation } from '@commerce/types/product' -import type { OperationContext } from '@commerce/api/operations' +import type { + OperationContext, + OperationOptions, +} from '@commerce/api/operations' import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' -import { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import type { SpreeApiConfig, SpreeApiProvider } from '../index' import type { SpreeSdkVariables } from 'framework/spree/types' -import { findIncluded, findIncludedOfType } from 'framework/spree/utils/jsonApi' -import getMediaGallery from 'framework/spree/utils/getMediaGallery' -import createGetAbsoluteImageUrl from 'framework/spree/utils/createGetAbsoluteImageUrl' -import { requireConfigValue } from 'framework/spree/isomorphicConfig' -import SpreeResponseContentError from 'framework/spree/errors/SpreeResponseContentError' -import expandOptions from 'framework/spree/utils/expandOptions' +import normalizeProduct from 'framework/spree/utils/normalizeProduct' export default function getAllProductsOperation({ commerce, }: OperationContext) { + async function getAllProducts(opts?: { + variables?: T['variables'] + config?: Partial + preview?: boolean + }): Promise + + async function getAllProducts( + opts: { + variables?: T['variables'] + config?: Partial + preview?: boolean + } & OperationOptions + ): Promise + async function getAllProducts({ variables: getAllProductsVariables = {}, config: userConfig, }: { variables?: T['variables'] config?: Partial - } = {}): Promise<{ products: Product[] | any[] }> { + } = {}): Promise<{ products: Product[] }> { console.info( 'getAllProducts called. Configuration: ', 'getAllProductsVariables: ', @@ -56,89 +61,7 @@ export default function getAllProductsOperation({ ) const normalizedProducts: Product[] = spreeSuccessResponse.data.map( - (spreeProduct) => { - const spreeImageRecords = findIncludedOfType( - spreeSuccessResponse, - spreeProduct, - 'images' - ) - - const images = getMediaGallery( - spreeImageRecords, - createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost')) - ) - - const price: ProductPrice = { - value: parseFloat(spreeProduct.attributes.price), - currencyCode: spreeProduct.attributes.currency, - } - - // TODO: Add sku to product object equal to master SKU from Spree. - // Currently, the Spree API doesn't return it. - - const hasNonMasterVariants = - (spreeProduct.relationships.variants.data as RelationType[]).length > - 0 - - let variants: ProductVariant[] - let options: ProductOption[] = [] - - if (hasNonMasterVariants) { - const spreeVariantRecords = findIncludedOfType( - spreeSuccessResponse, - spreeProduct, - 'variants' - ) - - variants = spreeVariantRecords.map((spreeVariantRecord) => { - const spreeOptionValues = findIncludedOfType( - spreeSuccessResponse, - spreeVariantRecord, - 'option_values' - ) - - let variantOptions: ProductOption[] = [] - - // Only include options which are used by variants. - - spreeOptionValues.forEach((spreeOptionValue) => { - variantOptions = expandOptions( - spreeSuccessResponse, - spreeOptionValue, - variantOptions - ) - - options = expandOptions( - spreeSuccessResponse, - spreeOptionValue, - options - ) - }) - - return { - id: spreeVariantRecord.id, - options: variantOptions, - } - }) - } else { - variants = [] - } - - const slug = spreeProduct.attributes.slug - const path = `/${spreeProduct.attributes.slug}` - - return { - id: spreeProduct.id, - name: spreeProduct.attributes.name, - description: spreeProduct.attributes.description, - images, - variants, - options, - price, - slug, - path, - } - } + (spreeProduct) => normalizeProduct(spreeSuccessResponse, spreeProduct) ) return { products: normalizedProducts } diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index de0804592d..87c0ff284a 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -1,26 +1,76 @@ -import type { LocalConfig } from '../index' -import { Product } from '@commerce/types/product' -import { GetProductOperation } from '@commerce/types/product' -import data from '../../../local/data.json' -import type { OperationContext } from '@commerce/api/operations' +import type { SpreeApiConfig, SpreeApiProvider } from '../index' +import type { GetProductOperation } from '@commerce/types/product' +import type { + OperationContext, + OperationOptions, +} from '@commerce/api/operations' +import type { IProduct } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import type { SpreeSdkVariables } from 'framework/spree/types' +import MissingSlugVariableError from 'framework/spree/errors/MissingSlugVariableError' +import normalizeProduct from 'framework/spree/utils/normalizeProduct' export default function getProductOperation({ commerce, -}: OperationContext) { +}: OperationContext) { + async function getProduct(opts: { + variables: T['variables'] + config?: Partial + preview?: boolean + }): Promise + + async function getProduct( + opts: { + variables: T['variables'] + config?: Partial + preview?: boolean + } & OperationOptions + ): Promise + async function getProduct({ query = '', - variables, - config, + variables: getProductVariables, + config: userConfig, }: { query?: string variables?: T['variables'] - config?: Partial + config?: Partial preview?: boolean - } = {}): Promise { + }): Promise { + console.log( + 'getProduct called. Configuration: ', + 'getProductVariables: ', + getProductVariables, + 'config: ', + userConfig + ) + + if (!getProductVariables?.slug) { + throw new MissingSlugVariableError() + } + + const variables: SpreeSdkVariables = { + methodPath: 'products.show', + arguments: [ + getProductVariables.slug, + { + include: 'variants,images,option_types,variants.option_values', + }, + ], + } + + const config = commerce.getConfig(userConfig) + const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. + + const { data: spreeSuccessResponse } = await apiFetch( + '__UNUSED__', + { variables } + ) + return { - product: data.products.find(({ slug }) => slug === variables!.slug), - // TODO: Return Spree product. - // product: {}, + product: normalizeProduct( + spreeSuccessResponse, + spreeSuccessResponse.data + ), } } diff --git a/framework/spree/errors/MissingSlugVariableError.ts b/framework/spree/errors/MissingSlugVariableError.ts new file mode 100644 index 0000000000..09b9d2e200 --- /dev/null +++ b/framework/spree/errors/MissingSlugVariableError.ts @@ -0,0 +1 @@ +export default class MissingSlugVariableError extends Error {} diff --git a/framework/spree/utils/expandOptions.ts b/framework/spree/utils/expandOptions.ts index 75f4089e8c..455354608e 100644 --- a/framework/spree/utils/expandOptions.ts +++ b/framework/spree/utils/expandOptions.ts @@ -2,8 +2,10 @@ import type { ProductOption, ProductOptionValues, } from '@commerce/types/product' -import type { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' -import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import type { + JsonApiDocument, + JsonApiResponse, +} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import SpreeResponseContentError from '../errors/SpreeResponseContentError' import { findIncluded } from './jsonApi' @@ -12,7 +14,7 @@ const isColorProductOption = (productOption: ProductOption) => productOption.displayName === 'Color' const expandOptions = ( - spreeSuccessResponse: IProducts, + spreeSuccessResponse: JsonApiResponse, spreeOptionValue: JsonApiDocument, accumulatedOptions: ProductOption[] ): ProductOption[] => { diff --git a/framework/spree/utils/normalizeProduct.ts b/framework/spree/utils/normalizeProduct.ts new file mode 100644 index 0000000000..0953d85a51 --- /dev/null +++ b/framework/spree/utils/normalizeProduct.ts @@ -0,0 +1,100 @@ +import type { + ProductOption, + ProductPrice, + ProductVariant, +} from '@commerce/types/product' +import type { + JsonApiListResponse, + JsonApiResponse, +} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' +import { requireConfigValue } from '../isomorphicConfig' +import createGetAbsoluteImageUrl from './createGetAbsoluteImageUrl' +import expandOptions from './expandOptions' +import getMediaGallery from './getMediaGallery' +import { findIncludedOfType } from './jsonApi' + +const normalizeProduct = ( + spreeSuccessResponse: JsonApiResponse | JsonApiListResponse, + spreeProduct: ProductAttr +) => { + const spreeImageRecords = findIncludedOfType( + spreeSuccessResponse, + spreeProduct, + 'images' + ) + + const images = getMediaGallery( + spreeImageRecords, + createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost')) + ) + + const price: ProductPrice = { + value: parseFloat(spreeProduct.attributes.price), + currencyCode: spreeProduct.attributes.currency, + } + + // TODO: Add sku to product object equal to master SKU from Spree. + // Currently, the Spree API doesn't return it. + + const hasNonMasterVariants = + (spreeProduct.relationships.variants.data as RelationType[]).length > 0 + + let variants: ProductVariant[] + let options: ProductOption[] = [] + + if (hasNonMasterVariants) { + const spreeVariantRecords = findIncludedOfType( + spreeSuccessResponse, + spreeProduct, + 'variants' + ) + + variants = spreeVariantRecords.map((spreeVariantRecord) => { + const spreeOptionValues = findIncludedOfType( + spreeSuccessResponse, + spreeVariantRecord, + 'option_values' + ) + + let variantOptions: ProductOption[] = [] + + // Only include options which are used by variants. + + spreeOptionValues.forEach((spreeOptionValue) => { + variantOptions = expandOptions( + spreeSuccessResponse, + spreeOptionValue, + variantOptions + ) + + options = expandOptions(spreeSuccessResponse, spreeOptionValue, options) + }) + + return { + id: spreeVariantRecord.id, + options: variantOptions, + } + }) + } else { + variants = [] + } + + const slug = spreeProduct.attributes.slug + const path = `/${spreeProduct.attributes.slug}` + + return { + id: spreeProduct.id, + name: spreeProduct.attributes.name, + description: spreeProduct.attributes.description, + images, + variants, + options, + price, + slug, + path, + } +} + +export default normalizeProduct From c546d26bbe3c214c78d55ddb5bcae9f82aca1a1e Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 14:54:20 +0200 Subject: [PATCH 07/98] PLP with searching by category --- framework/spree/api/utils/create-api-fetch.ts | 3 +- framework/spree/fetcher.ts | 41 ++++++++----- framework/spree/product/use-search.tsx | 59 +++++++++++++++---- 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index 68652aca20..e6c1f30e80 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -22,8 +22,9 @@ const createApiFetch: ( return async (url, queryData = {}, fetchOptions = {}) => { console.log( 'apiFetch called. query = ', - url, 'url = ', + url, + 'queryData = ', queryData, 'fetchOptions = ', fetchOptions diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 48834cf32e..882a6c2bf0 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -9,12 +9,15 @@ import type { import { errors } from '@spree/storefront-api-v2-sdk' import { requireConfigValue } from './isomorphicConfig' import getSpreeSdkMethodFromEndpointPath from './utils/getSpreeSdkMethodFromEndpointPath' -// import { handleFetchResponse } from './utils' +import SpreeSdkMethodFromEndpointPathError from './errors/SpreeSdkMethodFromEndpointPathError' +import type { SpreeSdkVariables } from './types' +import { GraphQLFetcherResult } from '@commerce/api' const client = makeClient({ host: requireConfigValue('spreeApiHost') }) -const fetcher: Fetcher = async (requestOptions) => { - console.log('Fetcher called') +const fetcher: Fetcher, SpreeSdkVariables> = async ( + requestOptions +) => { // url?: string // query?: string // method?: string @@ -23,24 +26,36 @@ const fetcher: Fetcher = async (requestOptions) => { const { url, method, variables, query } = requestOptions const { locale, ...vars } = variables ?? {} - if (!url) { - // TODO: Create a custom type for this error. - throw new Error('Url not provider for fetcher.') - } - console.log( - `Fetching products using options: ${JSON.stringify(requestOptions)}.` + 'Fetcher called. Configuration: ', + 'url = ', + url, + 'requestOptions = ', + requestOptions ) - // TODO: Not best to use url for finding the method, but should be good enough for now. + if (!variables) { + throw new SpreeSdkMethodFromEndpointPathError( + `Required SpreeSdkVariables not provided.` + ) + } const storeResponse: ResultResponse = - await getSpreeSdkMethodFromEndpointPath(client, url)(...variables.args) // TODO: Not the best to use variables here as it's type is any. + await getSpreeSdkMethodFromEndpointPath( + client, + variables.methodPath + )(...variables.arguments) if (storeResponse.success()) { - return storeResponse.success() + return { + data: storeResponse.success(), + res: storeResponse as any, //FIXME: MUST BE fetch() RESPONSE instead of axios. + } } + // FIXME: Allow Spree SDK to use fetch instead of axios + // (https://github.com/spree/spree-storefront-api-v2-js-sdk/issues/189) + const storeResponseError = storeResponse.fail() if (storeResponseError instanceof errors.SpreeError) { @@ -50,8 +65,6 @@ const fetcher: Fetcher = async (requestOptions) => { throw storeResponseError } -// import { Fetcher } from '@commerce/utils/types' - // export const fetcher: Fetcher = async () => { // console.log('FETCHER') // const res = await fetch('./data.json') diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 85bb68a777..f9d49df978 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -1,39 +1,72 @@ -import type { SWRHook, Fetcher } from '@commerce/utils/types' +import type { Fetcher, SWRHook } from '@commerce/utils/types' import useSearch from '@commerce/product/use-search' +import type { Product, SearchProductsHook } from '@commerce/types/product' import type { UseSearch } from '@commerce/product/use-search' +import normalizeProduct from '../utils/normalizeProduct' +import type { GraphQLFetcherResult } from '@commerce/api' +import { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' -export const handler: SWRHook = { +export const handler: SWRHook = { fetchOptions: { - url: 'client.products.list', // Add custom option for method name later + url: '__UNUSED__', query: '', }, async fetcher({ input, options, fetch }) { // This method is only needed if the options need to be modified before calling the generic fetcher (created in createFetcher). // TODO: Actually filter by input and query. - console.log( - `Calling useSearch fetcher with input: ${JSON.stringify( - input - )} and options: ${JSON.stringify(options)}.` + console.info( + 'useSearch fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options ) - // FIXME: IMPLEMENT + const categoryOrBrandId = input.categoryId || input.brandId - return fetch({ - url: options.url, - variables: { args: [] }, // TODO: Actually provide args later. + const filter = categoryOrBrandId + ? { + filter: { + taxons: categoryOrBrandId, + }, + } + : {} + + const { data: spreeSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ + variables: { + methodPath: 'products.list', + arguments: [ + { + include: 'variants,images,option_types,variants.option_values', + per_page: 50, + ...filter, + }, + ], + }, }) + + const normalizedProducts: Product[] = spreeSuccessResponse.data.map( + (spreeProduct) => normalizeProduct(spreeSuccessResponse, spreeProduct) + ) + + const found = spreeSuccessResponse.data.length > 0 + + return { products: normalizedProducts, found } }, // useHook is used for both, SWR and mutation requests to the store. // useHook is called in React components. For example, after clicking `Add to cart`. useHook: ({ useData }) => (input = {}) => { - console.log('useHook called') - // useData calls the fetcher method (above). // The difference between useHook and calling fetcher directly is // useHook accepts swrOptions. + + console.log('useSearch useHook called.') + return useData({ input: [ ['search', input.search], From 2209731d72785f3107c0dd49ad770fe0c61037b7 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 15:57:37 +0200 Subject: [PATCH 08/98] Fetch Spree Categories and Brands --- framework/spree/.env.template | 2 + .../spree/api/operations/get-site-info.ts | 113 ++++++++++++++---- framework/spree/isomorphicConfig.ts | 12 +- framework/spree/product/use-search.tsx | 26 ++-- 4 files changed, 113 insertions(+), 40 deletions(-) diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 42de48b17a..82a00d7be1 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -8,3 +8,5 @@ NEXT_PUBLIC_SPREE_DEFAULT_LOCALE=en-us NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:3000 NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost +NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID=1 +NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID=27 diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index 159fb6004c..d211968afa 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -1,6 +1,12 @@ -import { OperationContext } from '@commerce/api/operations' -import { Category } from '@commerce/types/site' -import { LocalConfig } from '../index' +import type { + OperationContext, + OperationOptions, +} from '@commerce/api/operations' +import type { Category, GetSiteInfoOperation } from '@commerce/types/site' +import type { ITaxons } from '@spree/storefront-api-v2-sdk/types/interfaces/Taxon' +import { requireConfigValue } from 'framework/spree/isomorphicConfig' +import type { SpreeSdkVariables } from 'framework/spree/types' +import type { SpreeApiConfig, SpreeApiProvider } from '..' export type GetSiteInfoResult< T extends { categories: any[]; brands: any[] } = { @@ -9,35 +15,98 @@ export type GetSiteInfoResult< } > = T -export default function getSiteInfoOperation({}: OperationContext) { - // TODO: Get Spree categories for display in React components. - function getSiteInfo({ +export default function getSiteInfoOperation({ + commerce, +}: OperationContext) { + async function getSiteInfo(opts?: { + config?: Partial + preview?: boolean + }): Promise + + async function getSiteInfo( + opts: { + config?: Partial + preview?: boolean + } & OperationOptions + ): Promise + + async function getSiteInfo({ query, - variables, - config: cfg, + variables: getSiteInfoVariables = {}, + config: userConfig, }: { query?: string variables?: any - config?: Partial + config?: Partial preview?: boolean } = {}): Promise { - return Promise.resolve({ - categories: [ - { - id: 'new-arrivals', - name: 'New Arrivals', - slug: 'new-arrivals', - path: '/new-arrivals', - }, + console.info( + 'getSiteInfo called. Configuration: ', + 'query: ', + query, + 'getSiteInfoVariables ', + getSiteInfoVariables, + 'config: ', + userConfig + ) + + // Fetch first and level taxons + + const createVariables = (parentId: string): SpreeSdkVariables => ({ + methodPath: 'taxons.list', + arguments: [ { - id: 'featured', - name: 'Featured', - slug: 'featured', - path: '/featured', + filter: { + parent_id: parentId, + }, }, ], - brands: [], }) + + const config = commerce.getConfig(userConfig) + const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. + + const { data: spreeCategoriesSuccessResponse } = await apiFetch( + '__UNUSED__', + { + variables: createVariables( + requireConfigValue('spreeCategoriesTaxonomyId') + ), + } + ) + + const { data: spreeBrandsSuccessResponse } = await apiFetch( + '__UNUSED__', + { + variables: createVariables(requireConfigValue('spreeBrandsTaxonomyId')), + } + ) + + const normalizedCategories: GetSiteInfoOperation['data']['categories'] = + spreeCategoriesSuccessResponse.data.map((spreeTaxon) => { + return { + id: spreeTaxon.id, + name: spreeTaxon.attributes.name, + slug: spreeTaxon.id, + path: spreeTaxon.id, + } + }) + + const normalizedBrands: GetSiteInfoOperation['data']['brands'] = + spreeBrandsSuccessResponse.data.map((spreeTaxon) => { + return { + node: { + entityId: spreeTaxon.id, + path: `brands/${spreeTaxon.id}`, + name: spreeTaxon.attributes.name, + }, + } + }) + + return { + categories: normalizedCategories, + brands: normalizedBrands, + } } return getSiteInfo diff --git a/framework/spree/isomorphicConfig.ts b/framework/spree/isomorphicConfig.ts index 650125842b..e472fdefeb 100644 --- a/framework/spree/isomorphicConfig.ts +++ b/framework/spree/isomorphicConfig.ts @@ -6,12 +6,22 @@ const isomorphicConfig = { defaultLocale: process.env.NEXT_PUBLIC_SPREE_DEFAULT_LOCALE, cartCookieName: process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_NAME, spreeImageHost: process.env.NEXT_PUBLIC_SPREE_IMAGE_HOST, + spreeCategoriesTaxonomyId: + process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID, + spreeBrandsTaxonomyId: process.env.NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID, } export default forceIsomorphicConfigValues( isomorphicConfig, [], - ['spreeApiHost', 'defaultLocale', 'cartCookieName', 'spreeImageHost'] + [ + 'spreeApiHost', + 'defaultLocale', + 'cartCookieName', + 'spreeImageHost', + 'spreeCategoriesTaxonomyId', + 'spreeBrandsTaxonomyId', + ] ) type IsomorphicConfig = typeof isomorphicConfig diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index f9d49df978..6b58c77f64 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -23,15 +23,16 @@ export const handler: SWRHook = { options ) - const categoryOrBrandId = input.categoryId || input.brandId + const taxons = [input.categoryId, input.brandId].filter(Boolean) - const filter = categoryOrBrandId - ? { - filter: { - taxons: categoryOrBrandId, - }, - } - : {} + const filter = + taxons.length > 0 + ? { + filter: { + taxons: taxons.join(','), + }, + } + : {} const { data: spreeSuccessResponse } = await fetch< GraphQLFetcherResult @@ -81,15 +82,6 @@ export const handler: SWRHook = { }, }) }, - // (input = {}) => { - - // return { - // data: { - // // FIXME: Use actual fetcher - // products: [], - // }, - // } - // }, } export default useSearch as UseSearch From a86bcd1d984960da26088fc4783ebc08fbc380db Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 16:06:35 +0200 Subject: [PATCH 09/98] Sort PLP --- framework/spree/product/use-search.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 6b58c77f64..6eafc6a92a 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -6,6 +6,13 @@ import normalizeProduct from '../utils/normalizeProduct' import type { GraphQLFetcherResult } from '@commerce/api' import { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +const nextToSpreeSortMap: { [key: string]: string } = { + 'trending-desc': 'updated_at', // Spree has no "trending" filter. Use updated_at. + 'latest-desc': 'updated_at', + 'price-asc': 'price', + 'price-desc': '-price', +} + export const handler: SWRHook = { fetchOptions: { url: '__UNUSED__', @@ -13,7 +20,6 @@ export const handler: SWRHook = { }, async fetcher({ input, options, fetch }) { // This method is only needed if the options need to be modified before calling the generic fetcher (created in createFetcher). - // TODO: Actually filter by input and query. console.info( 'useSearch fetcher called. Configuration: ', @@ -34,6 +40,8 @@ export const handler: SWRHook = { } : {} + const sort = input.sort ? { sort: nextToSpreeSortMap[input.sort] } : {} + const { data: spreeSuccessResponse } = await fetch< GraphQLFetcherResult >({ @@ -44,6 +52,7 @@ export const handler: SWRHook = { include: 'variants,images,option_types,variants.option_values', per_page: 50, ...filter, + ...sort, }, ], }, From c173b47ac9cebef796c509887a4c840e23ca9d8a Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 16:58:35 +0200 Subject: [PATCH 10/98] Search products by name --- framework/spree/product/use-search.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 6eafc6a92a..f9beac38c5 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -31,14 +31,12 @@ export const handler: SWRHook = { const taxons = [input.categoryId, input.brandId].filter(Boolean) - const filter = - taxons.length > 0 - ? { - filter: { - taxons: taxons.join(','), - }, - } - : {} + const filter = { + filter: { + ...(taxons.length > 0 ? { taxons: taxons.join(',') } : {}), + ...(input.search ? { name: input.search } : {}), + }, + } const sort = input.sort ? { sort: nextToSpreeSortMap[input.sort] } : {} From cfa72b080e59030fb1ffba963328c2ea7c60b32b Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 17:12:52 +0200 Subject: [PATCH 11/98] Fix option values collection --- framework/spree/utils/expandOptions.ts | 11 ++++++++++- framework/spree/utils/getMediaGallery.ts | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/framework/spree/utils/expandOptions.ts b/framework/spree/utils/expandOptions.ts index 455354608e..105f7d9471 100644 --- a/framework/spree/utils/expandOptions.ts +++ b/framework/spree/utils/expandOptions.ts @@ -73,8 +73,17 @@ const expandOptions = ( } } - const expandedOptionValues = [...option.values, optionValue] + if (existingOptionIndex === -1) { + return [ + ...accumulatedOptions, + { + ...option, + values: [optionValue], + }, + ] + } + const expandedOptionValues = [...option.values, optionValue] const expandedOptions = [...accumulatedOptions] expandedOptions[existingOptionIndex] = { diff --git a/framework/spree/utils/getMediaGallery.ts b/framework/spree/utils/getMediaGallery.ts index fe5587aa20..e697562d42 100644 --- a/framework/spree/utils/getMediaGallery.ts +++ b/framework/spree/utils/getMediaGallery.ts @@ -12,7 +12,7 @@ const getMediaGallery = ( ) => string | null ) => { return images.reduce((productImages, _, imageIndex) => { - const imageUrl = getImageUrl(images[imageIndex], 9001, 9001) + const imageUrl = getImageUrl(images[imageIndex], 800, 800) if (imageUrl) { return [ From cc0da75a601ac5fec463f6579580587e8050de40 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 17:23:56 +0200 Subject: [PATCH 12/98] Fix hasNonMasterVariants --- framework/spree/utils/normalizeProduct.ts | 34 +++++++++++------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/framework/spree/utils/normalizeProduct.ts b/framework/spree/utils/normalizeProduct.ts index 0953d85a51..a34ee25b39 100644 --- a/framework/spree/utils/normalizeProduct.ts +++ b/framework/spree/utils/normalizeProduct.ts @@ -39,27 +39,27 @@ const normalizeProduct = ( // Currently, the Spree API doesn't return it. const hasNonMasterVariants = - (spreeProduct.relationships.variants.data as RelationType[]).length > 0 + (spreeProduct.relationships.variants.data as RelationType[]).length > 1 let variants: ProductVariant[] let options: ProductOption[] = [] - if (hasNonMasterVariants) { - const spreeVariantRecords = findIncludedOfType( - spreeSuccessResponse, - spreeProduct, - 'variants' - ) + const spreeVariantRecords = findIncludedOfType( + spreeSuccessResponse, + spreeProduct, + 'variants' + ) - variants = spreeVariantRecords.map((spreeVariantRecord) => { + variants = spreeVariantRecords.map((spreeVariantRecord) => { + let variantOptions: ProductOption[] = [] + + if (hasNonMasterVariants) { const spreeOptionValues = findIncludedOfType( spreeSuccessResponse, spreeVariantRecord, 'option_values' ) - let variantOptions: ProductOption[] = [] - // Only include options which are used by variants. spreeOptionValues.forEach((spreeOptionValue) => { @@ -71,15 +71,13 @@ const normalizeProduct = ( options = expandOptions(spreeSuccessResponse, spreeOptionValue, options) }) + } - return { - id: spreeVariantRecord.id, - options: variantOptions, - } - }) - } else { - variants = [] - } + return { + id: spreeVariantRecord.id, + options: variantOptions, + } + }) const slug = spreeProduct.attributes.slug const path = `/${spreeProduct.attributes.slug}` From 2c4e2e4cb4439e0e7e13cc748513781c4cc5642b Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 28 Jul 2021 18:46:02 +0200 Subject: [PATCH 13/98] Sort Categories and Brands --- .../spree/api/operations/get-site-info.ts | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index d211968afa..7538827fed 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -3,11 +3,29 @@ import type { OperationOptions, } from '@commerce/api/operations' import type { Category, GetSiteInfoOperation } from '@commerce/types/site' -import type { ITaxons } from '@spree/storefront-api-v2-sdk/types/interfaces/Taxon' +import type { + ITaxons, + TaxonAttr, +} from '@spree/storefront-api-v2-sdk/types/interfaces/Taxon' import { requireConfigValue } from 'framework/spree/isomorphicConfig' import type { SpreeSdkVariables } from 'framework/spree/types' import type { SpreeApiConfig, SpreeApiProvider } from '..' +const taxonsSort = (spreeTaxon1: TaxonAttr, spreeTaxon2: TaxonAttr): number => { + const { left: left1, right: right1 } = spreeTaxon1.attributes + const { left: left2, right: right2 } = spreeTaxon2.attributes + + if (right1 < left2) { + return -1 + } + + if (right2 < left1) { + return 1 + } + + return 0 +} + export type GetSiteInfoResult< T extends { categories: any[]; brands: any[] } = { categories: Category[] @@ -83,7 +101,7 @@ export default function getSiteInfoOperation({ ) const normalizedCategories: GetSiteInfoOperation['data']['categories'] = - spreeCategoriesSuccessResponse.data.map((spreeTaxon) => { + spreeCategoriesSuccessResponse.data.sort(taxonsSort).map((spreeTaxon) => { return { id: spreeTaxon.id, name: spreeTaxon.attributes.name, @@ -93,7 +111,7 @@ export default function getSiteInfoOperation({ }) const normalizedBrands: GetSiteInfoOperation['data']['brands'] = - spreeBrandsSuccessResponse.data.map((spreeTaxon) => { + spreeBrandsSuccessResponse.data.sort(taxonsSort).map((spreeTaxon) => { return { node: { entityId: spreeTaxon.id, From 744a8b998e2620a08eb0f8bf45d3621bdbf38d97 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 29 Jul 2021 13:02:57 +0200 Subject: [PATCH 14/98] Add configuration to show product options when there's one variant available --- framework/spree/.env.template | 1 + .../spree/api/operations/get-all-products.ts | 7 +- framework/spree/api/operations/get-product.ts | 7 +- .../spree/api/operations/get-site-info.ts | 32 +++++---- framework/spree/api/utils/create-api-fetch.ts | 37 ++++++---- framework/spree/fetcher.ts | 51 +++++++------ framework/spree/index.tsx | 4 +- framework/spree/isomorphicConfig.ts | 3 + framework/spree/product/use-search.tsx | 6 +- .../spree/utils/createCreateFetchFetcher.ts | 72 +++++++++++++++++++ framework/spree/utils/normalizeProduct.ts | 12 ++-- package.json | 2 + yarn.lock | 7 +- 13 files changed, 167 insertions(+), 74 deletions(-) create mode 100644 framework/spree/utils/createCreateFetchFetcher.ts diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 82a00d7be1..f6e557e57b 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -10,3 +10,4 @@ NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:3000 NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID=1 NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID=27 +NEXT_PUBLIC_SHOW_SINGLE_VARIANT_OPTIONS=false diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 3572ee2d29..6ffb704b03 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -55,10 +55,9 @@ export default function getAllProductsOperation({ const config = commerce.getConfig(userConfig) const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. - const { data: spreeSuccessResponse } = await apiFetch( - '__UNUSED__', - { variables } - ) + const { + data: { data: spreeSuccessResponse }, + } = await apiFetch<{ data: IProducts }>('__UNUSED__', { variables }) const normalizedProducts: Product[] = spreeSuccessResponse.data.map( (spreeProduct) => normalizeProduct(spreeSuccessResponse, spreeProduct) diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index 87c0ff284a..42bfbb7fb9 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -61,10 +61,9 @@ export default function getProductOperation({ const config = commerce.getConfig(userConfig) const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. - const { data: spreeSuccessResponse } = await apiFetch( - '__UNUSED__', - { variables } - ) + const { + data: { data: spreeSuccessResponse }, + } = await apiFetch<{ data: IProduct }>('__UNUSED__', { variables }) return { product: normalizeProduct( diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index 7538827fed..ab3d395cf2 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -84,21 +84,25 @@ export default function getSiteInfoOperation({ const config = commerce.getConfig(userConfig) const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. - const { data: spreeCategoriesSuccessResponse } = await apiFetch( - '__UNUSED__', - { - variables: createVariables( - requireConfigValue('spreeCategoriesTaxonomyId') - ), - } - ) + const { + data: { data: spreeCategoriesSuccessResponse }, + } = await apiFetch<{ + data: ITaxons + }>('__UNUSED__', { + variables: createVariables( + requireConfigValue('spreeCategoriesTaxonomyId') as string + ), + }) - const { data: spreeBrandsSuccessResponse } = await apiFetch( - '__UNUSED__', - { - variables: createVariables(requireConfigValue('spreeBrandsTaxonomyId')), - } - ) + const { + data: { data: spreeBrandsSuccessResponse }, + } = await apiFetch<{ + data: ITaxons + }>('__UNUSED__', { + variables: createVariables( + requireConfigValue('spreeBrandsTaxonomyId') as string + ), + }) const normalizedCategories: GetSiteInfoOperation['data']['categories'] = spreeCategoriesSuccessResponse.data.sort(taxonsSort).map((spreeTaxon) => { diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index e6c1f30e80..be7e2a6d6c 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -1,4 +1,3 @@ -import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' import { SpreeApiConfig } from '..' import { errors, makeClient } from '@spree/storefront-api-v2-sdk' import { requireConfigValue } from 'framework/spree/isomorphicConfig' @@ -6,18 +5,25 @@ import convertSpreeErrorToGraphQlError from 'framework/spree/utils/convertSpreeE import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { JsonApiListResponse, - JsonApiResponse, + JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import getSpreeSdkMethodFromEndpointPath from 'framework/spree/utils/getSpreeSdkMethodFromEndpointPath' import { SpreeSdkVariables } from 'framework/spree/types' import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' +import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' +import createCreateFetchFetcher from '../../utils/createCreateFetchFetcher' +import createVercelFetch from '@vercel/fetch' const createApiFetch: ( getConfig: () => SpreeApiConfig ) => GraphQLFetcher, SpreeSdkVariables> = ( - getConfig + _getConfig ) => { - const client = makeClient({ host: requireConfigValue('spreeApiHost') }) + const client = makeClient({ + host: requireConfigValue('spreeApiHost') as string, + fetcherType: 'custom', + createFetcher: createCreateFetchFetcher({ fetch: createVercelFetch() }), + }) return async (url, queryData = {}, fetchOptions = {}) => { console.log( @@ -38,22 +44,23 @@ const createApiFetch: ( ) } - const storeResponse: ResultResponse = - await getSpreeSdkMethodFromEndpointPath( - client, - variables.methodPath - )(...variables.arguments) + const storeResponse: ResultResponse< + JsonApiSingleResponse | JsonApiListResponse + > = await getSpreeSdkMethodFromEndpointPath( + client, + variables.methodPath + )(...variables.arguments) + + if (storeResponse.isSuccess()) { + const data = storeResponse.success() + const rawFetchRespone = Object.getPrototypeOf(data).response - if (storeResponse.success()) { return { - data: storeResponse.success(), - res: storeResponse as any, //FIXME: MUST BE fetch() RESPONSE instead of axios. + data, + res: rawFetchRespone, } } - // FIXME: Allow Spree SDK to use fetch instead of axios - // (https://github.com/spree/spree-storefront-api-v2-js-sdk/issues/189) - const storeResponseError = storeResponse.fail() if (storeResponseError instanceof errors.SpreeError) { diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 882a6c2bf0..2916d40508 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -4,20 +4,26 @@ import { makeClient } from '@spree/storefront-api-v2-sdk' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { JsonApiListResponse, - JsonApiResponse, + JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import { errors } from '@spree/storefront-api-v2-sdk' import { requireConfigValue } from './isomorphicConfig' import getSpreeSdkMethodFromEndpointPath from './utils/getSpreeSdkMethodFromEndpointPath' import SpreeSdkMethodFromEndpointPathError from './errors/SpreeSdkMethodFromEndpointPathError' import type { SpreeSdkVariables } from './types' -import { GraphQLFetcherResult } from '@commerce/api' +import type { GraphQLFetcherResult } from '@commerce/api' +import createCreateFetchFetcher from './utils/createCreateFetchFetcher' -const client = makeClient({ host: requireConfigValue('spreeApiHost') }) +const client = makeClient({ + host: requireConfigValue('spreeApiHost') as string, + fetcherType: 'custom', + createFetcher: createCreateFetchFetcher({ fetch: globalThis.fetch }), +}) -const fetcher: Fetcher, SpreeSdkVariables> = async ( - requestOptions -) => { +const fetcher: Fetcher< + GraphQLFetcherResult, + SpreeSdkVariables +> = async (requestOptions) => { // url?: string // query?: string // method?: string @@ -40,22 +46,23 @@ const fetcher: Fetcher, SpreeSdkVariables> = async ( ) } - const storeResponse: ResultResponse = - await getSpreeSdkMethodFromEndpointPath( - client, - variables.methodPath - )(...variables.arguments) + const storeResponse: ResultResponse< + JsonApiSingleResponse | JsonApiListResponse + > = await getSpreeSdkMethodFromEndpointPath( + client, + variables.methodPath + )(...variables.arguments) + + if (storeResponse.isSuccess()) { + const data = storeResponse.success() + const rawFetchRespone = Object.getPrototypeOf(data).response - if (storeResponse.success()) { return { - data: storeResponse.success(), - res: storeResponse as any, //FIXME: MUST BE fetch() RESPONSE instead of axios. + data, + res: rawFetchRespone, } } - // FIXME: Allow Spree SDK to use fetch instead of axios - // (https://github.com/spree/spree-storefront-api-v2-js-sdk/issues/189) - const storeResponseError = storeResponse.fail() if (storeResponseError instanceof errors.SpreeError) { @@ -65,14 +72,4 @@ const fetcher: Fetcher, SpreeSdkVariables> = async ( throw storeResponseError } -// export const fetcher: Fetcher = async () => { -// console.log('FETCHER') -// const res = await fetch('./data.json') -// if (res.ok) { -// const { data } = await res.json() -// return data -// } -// throw res -// } - export default fetcher diff --git a/framework/spree/index.tsx b/framework/spree/index.tsx index afc1f91eb7..03f93cc1cd 100644 --- a/framework/spree/index.tsx +++ b/framework/spree/index.tsx @@ -18,8 +18,8 @@ export type SpreeProps = { } & SpreeConfig export const spreeCommerceConfigDefaults: CommerceConfig = { - locale: requireConfigValue('defaultLocale'), - cartCookie: requireConfigValue('cartCookieName'), + locale: requireConfigValue('defaultLocale') as string, + cartCookie: requireConfigValue('cartCookieName') as string, } export type SpreeConfig = CommerceConfig diff --git a/framework/spree/isomorphicConfig.ts b/framework/spree/isomorphicConfig.ts index e472fdefeb..7c5245f991 100644 --- a/framework/spree/isomorphicConfig.ts +++ b/framework/spree/isomorphicConfig.ts @@ -9,6 +9,8 @@ const isomorphicConfig = { spreeCategoriesTaxonomyId: process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID, spreeBrandsTaxonomyId: process.env.NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID, + showSingleVariantOptions: + process.env.NEXT_PUBLIC_SHOW_SINGLE_VARIANT_OPTIONS === 'true', } export default forceIsomorphicConfigValues( @@ -21,6 +23,7 @@ export default forceIsomorphicConfigValues( 'spreeImageHost', 'spreeCategoriesTaxonomyId', 'spreeBrandsTaxonomyId', + 'showSingleVariantOptions', ] ) diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index f9beac38c5..efeedc2ab2 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -40,9 +40,9 @@ export const handler: SWRHook = { const sort = input.sort ? { sort: nextToSpreeSortMap[input.sort] } : {} - const { data: spreeSuccessResponse } = await fetch< - GraphQLFetcherResult - >({ + const { + data: { data: spreeSuccessResponse }, + } = await fetch>({ variables: { methodPath: 'products.list', arguments: [ diff --git a/framework/spree/utils/createCreateFetchFetcher.ts b/framework/spree/utils/createCreateFetchFetcher.ts new file mode 100644 index 0000000000..8c6e9e0cb8 --- /dev/null +++ b/framework/spree/utils/createCreateFetchFetcher.ts @@ -0,0 +1,72 @@ +import * as qs from 'qs' +import { errors } from '@spree/storefront-api-v2-sdk' +import type { CreateFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/ClientConfig' +import { Request } from 'node-fetch' + +// TODO: Fix rawFetch any type. +const createCreateFetchFetcher = + ({ fetch: rawFetch }): CreateFetcher => + (fetcherOptions) => { + const { FetchError } = errors + const sharedHeaders = { + 'Content-Type': 'application/json', + } + + return { + fetch: async (fetchOptions) => { + // This fetcher always returns request equal null, + // because @vercel/fetch doesn't accept a Request object as argument + // and it's not used by NJC anyway. + try { + const { url, params, method, headers } = fetchOptions + const absoluteUrl = new URL(url, fetcherOptions.host) + let payload + + switch (method.toUpperCase()) { + case 'PUT': + case 'POST': + case 'DELETE': + case 'PATCH': + payload = { body: JSON.stringify(params) } + break + default: + payload = null + absoluteUrl.search = qs.stringify(params, { + arrayFormat: 'brackets', + }) + } + + try { + const response = await rawFetch(absoluteUrl.toString(), { + method, + headers: { ...sharedHeaders, ...headers }, + ...payload, + }) + + const data = await response.json() + + if (!response.ok) { + // Use the "traditional" approach and reject non 2xx responses. + throw new FetchError(response, null, data) + } + + return { + // Add response key to the prototype so it can be passed inside the GraphQLFetcherResult type. + // TODO: Search for a better solution than adding response to the prototype. + data: Object.setPrototypeOf({ data }, { response }), + } + } catch (error) { + if (error instanceof TypeError) { + throw new FetchError(null, null, null) + } + + throw error + } + } catch (error) { + throw new FetchError(null, null, null, error.message) + } + }, + } + } + +export default createCreateFetchFetcher diff --git a/framework/spree/utils/normalizeProduct.ts b/framework/spree/utils/normalizeProduct.ts index a34ee25b39..f3ace745bc 100644 --- a/framework/spree/utils/normalizeProduct.ts +++ b/framework/spree/utils/normalizeProduct.ts @@ -5,7 +5,7 @@ import type { } from '@commerce/types/product' import type { JsonApiListResponse, - JsonApiResponse, + JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' @@ -16,7 +16,7 @@ import getMediaGallery from './getMediaGallery' import { findIncludedOfType } from './jsonApi' const normalizeProduct = ( - spreeSuccessResponse: JsonApiResponse | JsonApiListResponse, + spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, spreeProduct: ProductAttr ) => { const spreeImageRecords = findIncludedOfType( @@ -27,7 +27,7 @@ const normalizeProduct = ( const images = getMediaGallery( spreeImageRecords, - createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost')) + createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost') as string) ) const price: ProductPrice = { @@ -41,6 +41,10 @@ const normalizeProduct = ( const hasNonMasterVariants = (spreeProduct.relationships.variants.data as RelationType[]).length > 1 + const showOptions = + (requireConfigValue('showSingleVariantOptions') as boolean) || + hasNonMasterVariants + let variants: ProductVariant[] let options: ProductOption[] = [] @@ -53,7 +57,7 @@ const normalizeProduct = ( variants = spreeVariantRecords.map((spreeVariantRecord) => { let variantOptions: ProductOption[] = [] - if (hasNonMasterVariants) { + if (showOptions) { const spreeOptionValues = findIncludedOfType( spreeSuccessResponse, spreeVariantRecord, diff --git a/package.json b/package.json index 832b9c8fd6..14ae7a79ba 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "@react-spring/web": "^9.2.1", "@spree/storefront-api-v2-sdk": "^4.5.4", + "@types/qs": "^6.9.7", "@vercel/fetch": "^6.1.0", "autoprefixer": "^10.2.6", "body-scroll-lock": "^3.1.5", @@ -38,6 +39,7 @@ "next-themes": "^0.0.14", "postcss": "^8.3.5", "postcss-nesting": "^8.0.1", + "qs": "^6.7.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-fast-marquee": "^1.1.4", diff --git a/yarn.lock b/yarn.lock index eb580b5654..3866458930 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1135,6 +1135,11 @@ resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/qs@^6.9.7": + version "6.9.7" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + "@types/react@^17.0.8": version "17.0.11" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.11.tgz" @@ -4960,7 +4965,7 @@ purgecss@^4.0.3: postcss "^8.2.1" postcss-selector-parser "^6.0.2" -qs@6.7.0: +qs@6.7.0, qs@^6.7.0: version "6.7.0" resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== From a27996a0881e23e533fd7a88e0d87be138b26070 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 16 Aug 2021 14:47:27 +0200 Subject: [PATCH 15/98] Enable text search for the Spree Framework --- framework/spree/.env.template | 8 +- framework/spree/cart/use-add-item.tsx | 72 ++++++- framework/spree/cart/use-cart.tsx | 116 +++++++--- framework/spree/commerce.config.json | 4 +- .../errors/MissingLineItemVariantError.ts | 1 + framework/spree/isomorphicConfig.ts | 7 +- framework/spree/utils/expandOptions.ts | 5 +- framework/spree/utils/getCartToken.ts | 7 + framework/spree/utils/normalizeCart.ts | 202 ++++++++++++++++++ framework/spree/utils/normalizeProduct.ts | 3 +- framework/spree/utils/setCartToken.ts | 16 ++ framework/spree/utils/validateCookieExpire.ts | 21 ++ 12 files changed, 417 insertions(+), 45 deletions(-) create mode 100644 framework/spree/errors/MissingLineItemVariantError.ts create mode 100644 framework/spree/utils/getCartToken.ts create mode 100644 framework/spree/utils/normalizeCart.ts create mode 100644 framework/spree/utils/setCartToken.ts create mode 100644 framework/spree/utils/validateCookieExpire.ts diff --git a/framework/spree/.env.template b/framework/spree/.env.template index f6e557e57b..60698d794c 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -2,12 +2,14 @@ COMMERCE_PROVIDER=spree -{# public (available in the web browser) #} +{# - public (available in the web browser) #} NEXT_PUBLIC_SPREE_API_HOST=http://localhost:3000 NEXT_PUBLIC_SPREE_DEFAULT_LOCALE=en-us -NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart +NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart_token +{# -- cookie expire in days #} +NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE=7 NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:3000 NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID=1 NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID=27 -NEXT_PUBLIC_SHOW_SINGLE_VARIANT_OPTIONS=false +NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS=false diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index 7f3d1061fa..f2b7bf5ea8 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -1,17 +1,75 @@ -import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' -import { MutationHook } from '@commerce/utils/types' +import useAddItem from '@commerce/cart/use-add-item' +import type { UseAddItem } from '@commerce/cart/use-add-item' +import type { MutationHook } from '@commerce/utils/types' +import { useCallback } from 'react' +import useCart from './use-cart' +import type { AddItemHook } from '@commerce/types/cart' +import normalizeCart from '@framework/utils/normalizeCart' +import type { GraphQLFetcherResult } from '@commerce/api' +import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import type { AddItem } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' +import getCartToken from '@framework/utils/getCartToken' export default useAddItem as UseAddItem -export const handler: MutationHook = { + +export const handler: MutationHook = { fetchOptions: { + url: '__UNUSED__', query: '', }, - async fetcher({ input, options, fetch }) {}, + async fetcher({ input, options, fetch }) { + console.info( + 'useAddItem fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + const { quantity, productId, variantId } = input + + const safeQuantity = quantity ?? 1 + + const token: IToken = { orderToken: getCartToken() } + const addItemParameters: AddItem = { + variant_id: variantId, + quantity: safeQuantity, + include: [ + 'line_items', + 'line_items.variant', + 'line_items.variant.product', + 'line_items.variant.product.images', + 'line_items.variant.images', + 'line_items.variant.option_values', + 'line_items.variant.product.option_types', + ].join(','), + } + + const { + data: { data: spreeSuccessResponse }, + } = await fetch>({ + variables: { + methodPath: 'cart.addItem', + arguments: [token, addItemParameters], + }, + }) + + return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + }, useHook: ({ fetch }) => () => { - return async function addItem() { - return {} - } + console.log('useAddItem useHook called.') + + const { mutate, data: cartData } = useCart() + + return useCallback(async (input) => { + const data = await fetch({ input }) + + await mutate(data, false) + + return data + }, []) }, } diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index b3e509a21b..3ded3f525b 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -1,42 +1,100 @@ import { useMemo } from 'react' -import { SWRHook } from '@commerce/utils/types' -import useCart, { UseCart } from '@commerce/cart/use-cart' +import type { SWRHook } from '@commerce/utils/types' +import useCart from '@commerce/cart/use-cart' +import type { UseCart } from '@commerce/cart/use-cart' +import type { GetCartHook } from '@commerce/types/cart' +import normalizeCart from '@framework/utils/normalizeCart' +import type { GraphQLFetcherResult } from '@commerce/api' +import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import setCartToken from '@framework/utils/setCartToken' export default useCart as UseCart -export const handler: SWRHook = { +// This handler avoids calling /api/cart. +// There doesn't seem to be a good reason to call it. +// So far, only @framework/bigcommerce uses it. +export const handler: SWRHook = { fetchOptions: { + url: '__UNUSED__', query: '', }, - async fetcher() { - return { - id: '', - createdAt: '', - currency: { code: '' }, - taxesIncluded: '', - lineItems: [], - lineItemsSubtotalPrice: '', - subtotalPrice: 0, - totalPrice: 0, + async fetcher({ input, options, fetch }) { + console.info( + 'useCart fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + const { cartId: cartToken } = input + let spreeCartResponse: IOrder | null + + if (!cartToken) { + spreeCartResponse = null + } else { + const spreeToken: IToken = { orderToken: cartToken } + const { + data: { data: spreeCartShowSuccessResponse }, + } = await fetch>({ + variables: { + methodPath: 'cart.show', + arguments: [ + spreeToken, + { + include: [ + 'line_items', + 'line_items.variant', + 'line_items.variant.product', + 'line_items.variant.product.images', + 'line_items.variant.images', + 'line_items.variant.option_values', + 'line_items.variant.product.option_types', + ].join(','), + }, + ], + }, + }) + + spreeCartResponse = spreeCartShowSuccessResponse + } + + if (!spreeCartResponse || spreeCartResponse?.data.attributes.completed_at) { + const { + data: { data: spreeCartCreateSuccessResponse }, + } = await fetch>({ + variables: { + methodPath: 'cart.create', + arguments: [], + }, + }) + + setCartToken(spreeCartCreateSuccessResponse.data.attributes.token) + + spreeCartResponse = spreeCartCreateSuccessResponse } + + return normalizeCart(spreeCartResponse, spreeCartResponse.data) }, useHook: ({ useData }) => - (input) => { - return useMemo( - () => - Object.create( - {}, - { - isEmpty: { - get() { - return true - }, - enumerable: true, - }, - } - ), - [] - ) + (input = {}) => { + console.log('useCart useHook called.') + + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + }) + + return useMemo(() => { + return Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) === 0 + }, + enumerable: true, + }, + }) + }, [response]) }, } diff --git a/framework/spree/commerce.config.json b/framework/spree/commerce.config.json index 4129d48540..e1e53245ce 100644 --- a/framework/spree/commerce.config.json +++ b/framework/spree/commerce.config.json @@ -2,8 +2,8 @@ "provider": "spree", "features": { "wishlist": false, - "cart": false, - "search": false, + "cart": true, + "search": true, "customerAuth": false } } diff --git a/framework/spree/errors/MissingLineItemVariantError.ts b/framework/spree/errors/MissingLineItemVariantError.ts new file mode 100644 index 0000000000..d9bee08039 --- /dev/null +++ b/framework/spree/errors/MissingLineItemVariantError.ts @@ -0,0 +1 @@ +export default class MissingLineItemVariantError extends Error {} diff --git a/framework/spree/isomorphicConfig.ts b/framework/spree/isomorphicConfig.ts index 7c5245f991..8806c8b7cd 100644 --- a/framework/spree/isomorphicConfig.ts +++ b/framework/spree/isomorphicConfig.ts @@ -1,16 +1,20 @@ import forceIsomorphicConfigValues from './utils/forceIsomorphicConfigValues' import requireConfig from './utils/requireConfig' +import validateCookieExpire from './utils/validateCookieExpire' const isomorphicConfig = { spreeApiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, defaultLocale: process.env.NEXT_PUBLIC_SPREE_DEFAULT_LOCALE, cartCookieName: process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_NAME, + cartCookieExpire: validateCookieExpire( + process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE + ), spreeImageHost: process.env.NEXT_PUBLIC_SPREE_IMAGE_HOST, spreeCategoriesTaxonomyId: process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID, spreeBrandsTaxonomyId: process.env.NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID, showSingleVariantOptions: - process.env.NEXT_PUBLIC_SHOW_SINGLE_VARIANT_OPTIONS === 'true', + process.env.NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS === 'true', } export default forceIsomorphicConfigValues( @@ -20,6 +24,7 @@ export default forceIsomorphicConfigValues( 'spreeApiHost', 'defaultLocale', 'cartCookieName', + 'cartCookieExpire', 'spreeImageHost', 'spreeCategoriesTaxonomyId', 'spreeBrandsTaxonomyId', diff --git a/framework/spree/utils/expandOptions.ts b/framework/spree/utils/expandOptions.ts index 105f7d9471..54b77dba7b 100644 --- a/framework/spree/utils/expandOptions.ts +++ b/framework/spree/utils/expandOptions.ts @@ -10,8 +10,9 @@ import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces import SpreeResponseContentError from '../errors/SpreeResponseContentError' import { findIncluded } from './jsonApi' -const isColorProductOption = (productOption: ProductOption) => - productOption.displayName === 'Color' +const isColorProductOption = (productOption: ProductOption) => { + return productOption.displayName === 'Color' +} const expandOptions = ( spreeSuccessResponse: JsonApiResponse, diff --git a/framework/spree/utils/getCartToken.ts b/framework/spree/utils/getCartToken.ts new file mode 100644 index 0000000000..81bb3a897e --- /dev/null +++ b/framework/spree/utils/getCartToken.ts @@ -0,0 +1,7 @@ +import { requireConfigValue } from '@framework/isomorphicConfig' +import Cookies from 'js-cookie' + +const getCartToken = () => + Cookies.get(requireConfigValue('cartCookieName') as string) + +export default getCartToken diff --git a/framework/spree/utils/normalizeCart.ts b/framework/spree/utils/normalizeCart.ts new file mode 100644 index 0000000000..0a40727381 --- /dev/null +++ b/framework/spree/utils/normalizeCart.ts @@ -0,0 +1,202 @@ +import type { + Cart, + LineItem, + ProductVariant, + SelectedOption, +} from '@commerce/types/cart' +import MissingLineItemVariantError from '@framework/errors/MissingLineItemVariantError' +import { requireConfigValue } from '@framework/isomorphicConfig' +import type { + JsonApiDocument, + JsonApiListResponse, + JsonApiSingleResponse, +} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +import type { OrderAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' +import { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' +import createGetAbsoluteImageUrl from './createGetAbsoluteImageUrl' +import getMediaGallery from './getMediaGallery' +import { findIncluded, findIncludedOfType } from './jsonApi' + +const isColorProductOption = (productOptionType: any) => { + // TODO: Fix type and merge with isColorProductOption in framework/spree/utils/expandOptions.ts + return productOptionType.attributes.presentation === 'Color' +} + +const normalizeVariant = ( + spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, + spreeVariant: JsonApiDocument +): ProductVariant => { + const productIdentifier = spreeVariant.relationships.product + .data as RelationType + const spreeProduct = findIncluded( + spreeSuccessResponse, + productIdentifier.type, + productIdentifier.id + ) + + if (spreeProduct === null) { + throw new MissingLineItemVariantError( + `Couldn't find product with id ${productIdentifier.id}.` + ) + } + + const spreeVariantImageRecords = findIncludedOfType( + spreeSuccessResponse, + spreeVariant, + 'images' + ) + + let lineItemImage + + const variantImage = getMediaGallery( + spreeVariantImageRecords, + createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost') as string) + )[0] + + if (variantImage) { + lineItemImage = variantImage + } else { + const spreeProductImageRecords = findIncludedOfType( + spreeSuccessResponse, + spreeProduct, + 'images' + ) + + const productImage = getMediaGallery( + spreeProductImageRecords, + createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost') as string) + )[0] + + lineItemImage = productImage + } + + return { + id: spreeVariant.id, + sku: spreeVariant.attributes.sku, + name: spreeProduct.attributes.name, + requiresShipping: true, + price: parseFloat(spreeVariant.attributes.price), + listPrice: parseFloat(spreeVariant.attributes.price), + image: lineItemImage, + isInStock: spreeVariant.attributes.in_stock, + availableForSale: spreeVariant.attributes.purchasable, + weight: spreeVariant.attributes.weight, + height: spreeVariant.attributes.height, + width: spreeVariant.attributes.width, + depth: spreeVariant.attributes.depth, + } +} + +const normalizeLineItem = ( + spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, + spreeLineItem: JsonApiDocument +): LineItem => { + //TODO: Replace JsonApiDocument type in spreeLineItem with more specific, new Spree line item item + const variantIdentifier = spreeLineItem.relationships.variant + .data as RelationType + const variant = findIncluded( + spreeSuccessResponse, + variantIdentifier.type, + variantIdentifier.id + ) + + if (variant === null) { + throw new MissingLineItemVariantError( + `Couldn't find variant with id ${variantIdentifier.id}.` + ) + } + + const productIdentifier = variant.relationships.product.data as RelationType + const product = findIncluded( + spreeSuccessResponse, + productIdentifier.type, + productIdentifier.id + ) + + if (product === null) { + throw new MissingLineItemVariantError( + `Couldn't find product with id ${productIdentifier.id}.` + ) + } + + const path = `/${product.attributes.slug}` + + const spreeOptionValues = findIncludedOfType( + spreeSuccessResponse, + variant, + 'option_values' + ) + + const options: SelectedOption[] = spreeOptionValues.map( + (spreeOptionValue) => { + const spreeOptionTypeIdentifier = spreeOptionValue.relationships + .option_type.data as RelationType + + const spreeOptionType = findIncluded( + spreeSuccessResponse, + spreeOptionTypeIdentifier.type, + spreeOptionTypeIdentifier.id + ) + + if (spreeOptionType === null) { + throw new MissingLineItemVariantError( + `Couldn't find option type with id ${spreeOptionTypeIdentifier.id}.` + ) + } + + const label = isColorProductOption(spreeOptionType) + ? spreeOptionValue.attributes.name + : spreeOptionValue.attributes.presentation + + return { + id: spreeOptionValue.id, + name: spreeOptionType.attributes.presentation, + value: label, + } + } + ) + + return { + id: spreeLineItem.id, + variantId: variant.id, + productId: productIdentifier.id, + name: spreeLineItem.attributes.name, + quantity: spreeLineItem.attributes.quantity, + discounts: [], // TODO: Retrieve from Spree + path, + variant: normalizeVariant(spreeSuccessResponse, variant), + options, + } +} + +const normalizeCart = ( + spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, + spreeCart: OrderAttr +): Cart => { + const lineItems = findIncludedOfType( + spreeSuccessResponse, + spreeCart, + 'line_items' + ).map((lineItem) => normalizeLineItem(spreeSuccessResponse, lineItem)) + + return { + id: spreeCart.id, + createdAt: spreeCart.attributes.created_at.toString(), + currency: { code: spreeCart.attributes.currency }, + taxesIncluded: true, + lineItems, + lineItemsSubtotalPrice: parseFloat(spreeCart.attributes.item_total), + // TODO: We need a value from Spree which includes item total and discounts in one value for subtotalPrice. + subtotalPrice: parseFloat(spreeCart.attributes.item_total), + totalPrice: parseFloat(spreeCart.attributes.total), + customerId: spreeCart.attributes.token, + email: spreeCart.attributes.email, + discounts: [], + // discounts: [{value: number}] // TODO: Retrieve from Spree + } +} + +export { normalizeLineItem } + +export default normalizeCart diff --git a/framework/spree/utils/normalizeProduct.ts b/framework/spree/utils/normalizeProduct.ts index f3ace745bc..bc02301aee 100644 --- a/framework/spree/utils/normalizeProduct.ts +++ b/framework/spree/utils/normalizeProduct.ts @@ -1,4 +1,5 @@ import type { + Product, ProductOption, ProductPrice, ProductVariant, @@ -18,7 +19,7 @@ import { findIncludedOfType } from './jsonApi' const normalizeProduct = ( spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, spreeProduct: ProductAttr -) => { +): Product => { const spreeImageRecords = findIncludedOfType( spreeSuccessResponse, spreeProduct, diff --git a/framework/spree/utils/setCartToken.ts b/framework/spree/utils/setCartToken.ts new file mode 100644 index 0000000000..68caaa19f8 --- /dev/null +++ b/framework/spree/utils/setCartToken.ts @@ -0,0 +1,16 @@ +import { requireConfigValue } from '@framework/isomorphicConfig' +import Cookies from 'js-cookie' + +const setCartToken = (cartToken: string) => { + const cookieOptions = { + expires: requireConfigValue('cartCookieExpire') as number, + } + + Cookies.set( + requireConfigValue('cartCookieName') as string, + cartToken, + cookieOptions + ) +} + +export default setCartToken diff --git a/framework/spree/utils/validateCookieExpire.ts b/framework/spree/utils/validateCookieExpire.ts new file mode 100644 index 0000000000..35e0434394 --- /dev/null +++ b/framework/spree/utils/validateCookieExpire.ts @@ -0,0 +1,21 @@ +const validateCookieExpire = (expire: unknown) => { + let expireInteger: number + + if (typeof expire === 'string') { + expireInteger = parseFloat(expire || '') + } else if (typeof expire === 'number') { + expireInteger = expire + } else { + throw new TypeError( + 'expire must be a string containing a number or an integer.' + ) + } + + if (expireInteger < 0) { + throw new RangeError('expire must be non-negative.') + } + + return expireInteger +} + +export default validateCookieExpire From 6ef9f4da8900e7eae1ac34771ae7bd9acfff0fec Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 17 Aug 2021 16:54:54 +0200 Subject: [PATCH 16/98] Allow removing line items --- framework/spree/cart/use-add-item.tsx | 2 +- framework/spree/cart/use-cart.tsx | 4 +- framework/spree/cart/use-remove-item.tsx | 65 +++++++++++++++++++++--- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index f2b7bf5ea8..a4f63ac135 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -62,7 +62,7 @@ export const handler: MutationHook = { () => { console.log('useAddItem useHook called.') - const { mutate, data: cartData } = useCart() + const { mutate } = useCart() return useCallback(async (input) => { const data = await fetch({ input }) diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index 3ded3f525b..1fd880730d 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -70,11 +70,11 @@ export const handler: SWRHook = { }, }) - setCartToken(spreeCartCreateSuccessResponse.data.attributes.token) - spreeCartResponse = spreeCartCreateSuccessResponse } + setCartToken(spreeCartResponse.data.attributes.token) + return normalizeCart(spreeCartResponse, spreeCartResponse.data) }, useHook: diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx index b4ed583b84..9ba1f45b21 100644 --- a/framework/spree/cart/use-remove-item.tsx +++ b/framework/spree/cart/use-remove-item.tsx @@ -1,18 +1,71 @@ import { MutationHook } from '@commerce/utils/types' -import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item' +import useRemoveItem from '@commerce/cart/use-remove-item' +import type { UseRemoveItem } from '@commerce/cart/use-remove-item' +import type { RemoveItemHook } from '@commerce/types/cart' +import useCart from './use-cart' +import { useCallback } from 'react' +import normalizeCart from '@framework/utils/normalizeCart' +import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' +import type { GraphQLFetcherResult } from '@commerce/api' +import type { IQuery } from '@spree/storefront-api-v2-sdk/types/interfaces/Query' +import getCartToken from '@framework/utils/getCartToken' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' export default useRemoveItem as UseRemoveItem -export const handler: MutationHook = { +export const handler: MutationHook = { fetchOptions: { + url: '__UNUSED__', query: '', }, - async fetcher({ input, options, fetch }) {}, + async fetcher({ input, options, fetch }) { + console.info( + 'useRemoveItem fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + const { itemId: lineItemId } = input + + const token: IToken = { orderToken: getCartToken() } + const removeItemParameters: IQuery = { + include: [ + 'line_items', + 'line_items.variant', + 'line_items.variant.product', + 'line_items.variant.product.images', + 'line_items.variant.images', + 'line_items.variant.option_values', + 'line_items.variant.product.option_types', + ].join(','), + } + + const { + data: { data: spreeSuccessResponse }, + } = await fetch>({ + variables: { + methodPath: 'cart.removeItem', + arguments: [token, lineItemId, removeItemParameters], + }, + }) + + return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + }, useHook: ({ fetch }) => () => { - return async function removeItem(input) { - return {} - } + console.log('useRemoveItem useHook called.') + + const { mutate } = useCart() + + return useCallback(async (input) => { + const data = await fetch({ input: { itemId: input.id } }) + + await mutate(data, false) + + return data + }, []) }, } From 28ee2eb0644d05111a9248e6da1d2062e2e3c027 Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 17 Aug 2021 18:16:51 +0200 Subject: [PATCH 17/98] Allow updating line item quantity --- framework/spree/cart/use-update-item.tsx | 99 +++++++++++++++++-- .../spree/utils/createCreateFetchFetcher.ts | 5 +- 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx index 06d703f70f..239cc6c989 100644 --- a/framework/spree/cart/use-update-item.tsx +++ b/framework/spree/cart/use-update-item.tsx @@ -1,18 +1,103 @@ -import { MutationHook } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item' +import type { UpdateItemHook } from '@commerce/types/cart' +import useCart from './use-cart' +import { useCallback } from 'react' +import { ValidationError } from '@commerce/utils/errors' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import type { SetQuantity } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' +import getCartToken from '@framework/utils/getCartToken' +import type { GraphQLFetcherResult } from '@commerce/api' +import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' +import normalizeCart from '@framework/utils/normalizeCart' +import debounce from 'lodash.debounce' export default useUpdateItem as UseUpdateItem -export const handler: MutationHook = { +export const handler: MutationHook = { fetchOptions: { + url: '__UNUSED__', query: '', }, - async fetcher({ input, options, fetch }) {}, + async fetcher({ input, options, fetch }) { + console.info( + 'useRemoveItem fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + const { itemId, item } = input + + if (!item.quantity) { + throw new ValidationError({ + message: 'Line item quantity needs to be provided.', + }) + } + + const token: IToken = { orderToken: getCartToken() } + const setQuantityParameters: SetQuantity = { + line_item_id: itemId, + quantity: item.quantity, + include: [ + 'line_items', + 'line_items.variant', + 'line_items.variant.product', + 'line_items.variant.product.images', + 'line_items.variant.images', + 'line_items.variant.option_values', + 'line_items.variant.product.option_types', + ].join(','), + } + + const { + data: { data: spreeSuccessResponse }, + } = await fetch>({ + variables: { + methodPath: 'cart.setQuantity', + arguments: [token, setQuantityParameters], + }, + }) + + return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + }, useHook: ({ fetch }) => - () => { - return async function addItem() { - return {} - } + (context) => { + console.log('useUpdateItem useHook called.') + + const { mutate } = useCart() + + return useCallback( + debounce(async (input: UpdateItemHook['actionInput']) => { + const itemId = context?.item?.id + const productId = input.productId ?? context?.item?.productId + const variantId = input.variantId ?? context?.item?.variantId + const quantity = input.quantity + + if (!itemId || !productId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fetch({ + input: { + item: { + productId, + variantId, + quantity, + }, + itemId, + }, + }) + + await mutate(data, false) + + return data + }, context?.wait ?? 500), + [] + ) }, } diff --git a/framework/spree/utils/createCreateFetchFetcher.ts b/framework/spree/utils/createCreateFetchFetcher.ts index 8c6e9e0cb8..fec6ea61d8 100644 --- a/framework/spree/utils/createCreateFetchFetcher.ts +++ b/framework/spree/utils/createCreateFetchFetcher.ts @@ -1,11 +1,10 @@ import * as qs from 'qs' import { errors } from '@spree/storefront-api-v2-sdk' import type { CreateFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/ClientConfig' -import { Request } from 'node-fetch' -// TODO: Fix rawFetch any type. const createCreateFetchFetcher = ({ fetch: rawFetch }): CreateFetcher => + // TODO: Fix rawFetch any type. (fetcherOptions) => { const { FetchError } = errors const sharedHeaders = { @@ -38,7 +37,7 @@ const createCreateFetchFetcher = try { const response = await rawFetch(absoluteUrl.toString(), { - method, + method: method.toUpperCase(), headers: { ...sharedHeaders, ...headers }, ...payload, }) From b38d15e1ee7b285280955600bf2e696ec8ef6043 Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 17 Aug 2021 18:42:57 +0200 Subject: [PATCH 18/98] Add __typename to variant options to allow adding the selected variant to the cart --- framework/spree/utils/expandOptions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/spree/utils/expandOptions.ts b/framework/spree/utils/expandOptions.ts index 54b77dba7b..0ea5a28a2e 100644 --- a/framework/spree/utils/expandOptions.ts +++ b/framework/spree/utils/expandOptions.ts @@ -42,6 +42,7 @@ const expandOptions = ( } option = { + __typename: 'MultipleChoiceOption', id: spreeOptionType.id, displayName: spreeOptionType.attributes.presentation, values: [], From 30c29f0da86365af89743c374715f74d512cba2f Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 18 Aug 2021 10:39:30 +0200 Subject: [PATCH 19/98] Use fetch and Request from node-fetch in Spree SDK --- framework/spree/api/utils/create-api-fetch.ts | 7 ++++-- framework/spree/fetcher.ts | 10 ++++----- .../spree/utils/createCreateFetchFetcher.ts | 22 ++++++++++++------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index be7e2a6d6c..de4af5d3dd 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -12,7 +12,7 @@ import { SpreeSdkVariables } from 'framework/spree/types' import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' import createCreateFetchFetcher from '../../utils/createCreateFetchFetcher' -import createVercelFetch from '@vercel/fetch' +import fetch, { Request } from 'node-fetch' const createApiFetch: ( getConfig: () => SpreeApiConfig @@ -22,7 +22,10 @@ const createApiFetch: ( const client = makeClient({ host: requireConfigValue('spreeApiHost') as string, fetcherType: 'custom', - createFetcher: createCreateFetchFetcher({ fetch: createVercelFetch() }), + createFetcher: createCreateFetchFetcher({ + fetch, + requestClass: Request, + }), }) return async (url, queryData = {}, fetchOptions = {}) => { diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 2916d40508..e16c19ca35 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -17,18 +17,16 @@ import createCreateFetchFetcher from './utils/createCreateFetchFetcher' const client = makeClient({ host: requireConfigValue('spreeApiHost') as string, fetcherType: 'custom', - createFetcher: createCreateFetchFetcher({ fetch: globalThis.fetch }), + createFetcher: createCreateFetchFetcher({ + fetch: globalThis.fetch, + requestClass: globalThis.Request, + }), }) const fetcher: Fetcher< GraphQLFetcherResult, SpreeSdkVariables > = async (requestOptions) => { - // url?: string - // query?: string - // method?: string - // variables?: any - // body?: Body const { url, method, variables, query } = requestOptions const { locale, ...vars } = variables ?? {} diff --git a/framework/spree/utils/createCreateFetchFetcher.ts b/framework/spree/utils/createCreateFetchFetcher.ts index fec6ea61d8..165c6f18f5 100644 --- a/framework/spree/utils/createCreateFetchFetcher.ts +++ b/framework/spree/utils/createCreateFetchFetcher.ts @@ -3,7 +3,7 @@ import { errors } from '@spree/storefront-api-v2-sdk' import type { CreateFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/ClientConfig' const createCreateFetchFetcher = - ({ fetch: rawFetch }): CreateFetcher => + ({ fetch: rawFetch, requestClass }): CreateFetcher => // TODO: Fix rawFetch any type. (fetcherOptions) => { const { FetchError } = errors @@ -35,18 +35,20 @@ const createCreateFetchFetcher = }) } + const request: Request = new requestClass(absoluteUrl.toString(), { + method: method.toUpperCase(), + headers: { ...sharedHeaders, ...headers }, + ...payload, + }) + try { - const response = await rawFetch(absoluteUrl.toString(), { - method: method.toUpperCase(), - headers: { ...sharedHeaders, ...headers }, - ...payload, - }) + const response: Response = await rawFetch(request) const data = await response.json() if (!response.ok) { // Use the "traditional" approach and reject non 2xx responses. - throw new FetchError(response, null, data) + throw new FetchError(response, request, data) } return { @@ -56,12 +58,16 @@ const createCreateFetchFetcher = } } catch (error) { if (error instanceof TypeError) { - throw new FetchError(null, null, null) + throw new FetchError(null, request, null) } throw error } } catch (error) { + if (error instanceof FetchError) { + throw error + } + throw new FetchError(null, null, null, error.message) } }, From 3b3a181dac3596b2048077a20ee1a57d6e442d45 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 18 Aug 2021 15:47:11 +0200 Subject: [PATCH 20/98] Update Spree SDK fetcher --- framework/spree/api/utils/create-api-fetch.ts | 13 +-- framework/spree/fetcher.ts | 13 +-- .../spree/utils/createCreateFetchFetcher.ts | 77 ------------------ .../utils/createCustomizedFetchFetcher.ts | 81 +++++++++++++++++++ 4 files changed, 97 insertions(+), 87 deletions(-) delete mode 100644 framework/spree/utils/createCreateFetchFetcher.ts create mode 100644 framework/spree/utils/createCustomizedFetchFetcher.ts diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index de4af5d3dd..d9d78bb600 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -11,7 +11,7 @@ import getSpreeSdkMethodFromEndpointPath from 'framework/spree/utils/getSpreeSdk import { SpreeSdkVariables } from 'framework/spree/types' import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' -import createCreateFetchFetcher from '../../utils/createCreateFetchFetcher' +import createCustomizedFetchFetcher from '../../utils/createCustomizedFetchFetcher' import fetch, { Request } from 'node-fetch' const createApiFetch: ( @@ -22,10 +22,13 @@ const createApiFetch: ( const client = makeClient({ host: requireConfigValue('spreeApiHost') as string, fetcherType: 'custom', - createFetcher: createCreateFetchFetcher({ - fetch, - requestClass: Request, - }), + createFetcher: (fetcherOptions) => { + return createCustomizedFetchFetcher({ + fetch, + requestConstructor: Request, + ...fetcherOptions, + }) + }, }) return async (url, queryData = {}, fetchOptions = {}) => { diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index e16c19ca35..64797ee327 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -12,15 +12,18 @@ import getSpreeSdkMethodFromEndpointPath from './utils/getSpreeSdkMethodFromEndp import SpreeSdkMethodFromEndpointPathError from './errors/SpreeSdkMethodFromEndpointPathError' import type { SpreeSdkVariables } from './types' import type { GraphQLFetcherResult } from '@commerce/api' -import createCreateFetchFetcher from './utils/createCreateFetchFetcher' +import createCustomizedFetchFetcher from './utils/createCustomizedFetchFetcher' const client = makeClient({ host: requireConfigValue('spreeApiHost') as string, fetcherType: 'custom', - createFetcher: createCreateFetchFetcher({ - fetch: globalThis.fetch, - requestClass: globalThis.Request, - }), + createFetcher: (fetcherOptions) => { + return createCustomizedFetchFetcher({ + fetch: globalThis.fetch, + requestConstructor: globalThis.Request, + ...fetcherOptions, + }) + }, }) const fetcher: Fetcher< diff --git a/framework/spree/utils/createCreateFetchFetcher.ts b/framework/spree/utils/createCreateFetchFetcher.ts deleted file mode 100644 index 165c6f18f5..0000000000 --- a/framework/spree/utils/createCreateFetchFetcher.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as qs from 'qs' -import { errors } from '@spree/storefront-api-v2-sdk' -import type { CreateFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/ClientConfig' - -const createCreateFetchFetcher = - ({ fetch: rawFetch, requestClass }): CreateFetcher => - // TODO: Fix rawFetch any type. - (fetcherOptions) => { - const { FetchError } = errors - const sharedHeaders = { - 'Content-Type': 'application/json', - } - - return { - fetch: async (fetchOptions) => { - // This fetcher always returns request equal null, - // because @vercel/fetch doesn't accept a Request object as argument - // and it's not used by NJC anyway. - try { - const { url, params, method, headers } = fetchOptions - const absoluteUrl = new URL(url, fetcherOptions.host) - let payload - - switch (method.toUpperCase()) { - case 'PUT': - case 'POST': - case 'DELETE': - case 'PATCH': - payload = { body: JSON.stringify(params) } - break - default: - payload = null - absoluteUrl.search = qs.stringify(params, { - arrayFormat: 'brackets', - }) - } - - const request: Request = new requestClass(absoluteUrl.toString(), { - method: method.toUpperCase(), - headers: { ...sharedHeaders, ...headers }, - ...payload, - }) - - try { - const response: Response = await rawFetch(request) - - const data = await response.json() - - if (!response.ok) { - // Use the "traditional" approach and reject non 2xx responses. - throw new FetchError(response, request, data) - } - - return { - // Add response key to the prototype so it can be passed inside the GraphQLFetcherResult type. - // TODO: Search for a better solution than adding response to the prototype. - data: Object.setPrototypeOf({ data }, { response }), - } - } catch (error) { - if (error instanceof TypeError) { - throw new FetchError(null, request, null) - } - - throw error - } - } catch (error) { - if (error instanceof FetchError) { - throw error - } - - throw new FetchError(null, null, null, error.message) - } - }, - } - } - -export default createCreateFetchFetcher diff --git a/framework/spree/utils/createCustomizedFetchFetcher.ts b/framework/spree/utils/createCustomizedFetchFetcher.ts new file mode 100644 index 0000000000..9ed35a82fc --- /dev/null +++ b/framework/spree/utils/createCustomizedFetchFetcher.ts @@ -0,0 +1,81 @@ +import * as qs from 'qs' +import { errors } from '@spree/storefront-api-v2-sdk' +import type { CreateCustomizedFetchFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/CreateCustomizedFetchFetcher' + +const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( + fetcherOptions +) => { + const { FetchError } = errors + const sharedHeaders = { + 'Content-Type': 'application/json', + } + + const { host, fetch, requestConstructor } = fetcherOptions + + return { + fetch: async (fetchOptions) => { + // This fetcher always returns request equal null, + // because @vercel/fetch doesn't accept a Request object as argument + // and it's not used by NJC anyway. + try { + const { url, params, method, headers } = fetchOptions + const absoluteUrl = new URL(url, host) + let payload + + switch (method.toUpperCase()) { + case 'PUT': + case 'POST': + case 'DELETE': + case 'PATCH': + payload = { body: JSON.stringify(params) } + break + default: + payload = null + absoluteUrl.search = qs.stringify(params, { + arrayFormat: 'brackets', + }) + } + + const request: Request = new requestConstructor( + absoluteUrl.toString(), + { + method: method.toUpperCase(), + headers: { ...sharedHeaders, ...headers }, + ...payload, + } + ) + + try { + const response: Response = await fetch(request) + + const data = await response.json() + + if (!response.ok) { + // Use the "traditional" approach and reject non 2xx responses. + throw new FetchError(response, request, data) + } + + return { + // Add response key to the prototype so it can be passed inside the GraphQLFetcherResult type. + // TODO: Search for a better solution than adding response to the prototype. + data: Object.setPrototypeOf({ data }, { response }), + } + } catch (error) { + if (error instanceof TypeError) { + throw new FetchError(null, request, null) + } + + throw error + } + } catch (error) { + if (error instanceof FetchError) { + throw error + } + + throw new FetchError(null, null, null, error.message) + } + }, + } +} + +export default createCustomizedFetchFetcher From 17f8d497b8bb55bd91405e3895662a1b9b8ac2d3 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 18 Aug 2021 17:53:53 +0200 Subject: [PATCH 21/98] Show placeholder message for /chechout and adjust api fetcher type --- .../spree/api/endpoints/checkout/checkout.ts | 44 +++++++++++++++++++ .../spree/api/endpoints/checkout/index.ts | 24 +++++++++- framework/spree/api/index.ts | 4 +- .../spree/api/operations/get-all-products.ts | 4 +- framework/spree/api/operations/get-product.ts | 4 +- .../spree/api/operations/get-site-info.ts | 18 +++++--- framework/spree/next.config.js | 8 ++++ 7 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 framework/spree/api/endpoints/checkout/checkout.ts diff --git a/framework/spree/api/endpoints/checkout/checkout.ts b/framework/spree/api/endpoints/checkout/checkout.ts new file mode 100644 index 0000000000..3622e16306 --- /dev/null +++ b/framework/spree/api/endpoints/checkout/checkout.ts @@ -0,0 +1,44 @@ +import type { CheckoutEndpoint } from '.' + +const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ + req: request, + res: response, + config, +}) => { + try { + const html = ` + + + + + + Checkout + + +
+ + + +

Checkout not yet implemented :(

+

+ See #64 +

+
+ + + ` + + response.status(200) + response.setHeader('Content-Type', 'text/html') + response.write(html) + response.end() + } catch (error) { + console.error(error) + + const message = 'An unexpected error ocurred' + + response.status(500).json({ data: null, errors: [{ message }] }) + } +} + +export default checkout diff --git a/framework/spree/api/endpoints/checkout/index.ts b/framework/spree/api/endpoints/checkout/index.ts index 491bf0ac93..47b3157284 100644 --- a/framework/spree/api/endpoints/checkout/index.ts +++ b/framework/spree/api/endpoints/checkout/index.ts @@ -1 +1,23 @@ -export default function noopApi(...args: any[]): void {} +import { createEndpoint } from '@commerce/api' +import type { CommerceAPI } from '@commerce/api' +import type { GetAPISchema } from '@commerce/api' +import checkoutEndpoint from '@commerce/api/endpoints/checkout' +import type { CheckoutSchema } from '@commerce/types/checkout' +import type { SpreeApiProvider } from '@framework/api' +import checkout from './checkout' + +export type CheckoutAPI = GetAPISchema< + CommerceAPI, + CheckoutSchema +> + +export type CheckoutEndpoint = CheckoutAPI['endpoint'] + +export const handlers: CheckoutEndpoint['handlers'] = { checkout } + +const checkoutApi = createEndpoint({ + handler: checkoutEndpoint, + handlers, +}) + +export default checkoutApi diff --git a/framework/spree/api/index.ts b/framework/spree/api/index.ts index c8bac3bf1e..17ad8002fb 100644 --- a/framework/spree/api/index.ts +++ b/framework/spree/api/index.ts @@ -10,7 +10,9 @@ import getAllProductPaths from './operations/get-all-product-paths' import getAllProducts from './operations/get-all-products' import getProduct from './operations/get-product' -export interface SpreeApiConfig extends CommerceAPIConfig {} +export interface SpreeApiConfig extends CommerceAPIConfig { + fetch: any // Using any type, because CommerceAPIConfig['fetch'] cannot be extended from Variables = any to SpreeSdkVariables +} const config: SpreeApiConfig = { commerceUrl: '', diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 6ffb704b03..a821fd15ed 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -57,7 +57,9 @@ export default function getAllProductsOperation({ const { data: { data: spreeSuccessResponse }, - } = await apiFetch<{ data: IProducts }>('__UNUSED__', { variables }) + } = await apiFetch<{ data: IProducts }, SpreeSdkVariables>('__UNUSED__', { + variables, + }) const normalizedProducts: Product[] = spreeSuccessResponse.data.map( (spreeProduct) => normalizeProduct(spreeSuccessResponse, spreeProduct) diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index 42bfbb7fb9..1b4bc87446 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -63,7 +63,9 @@ export default function getProductOperation({ const { data: { data: spreeSuccessResponse }, - } = await apiFetch<{ data: IProduct }>('__UNUSED__', { variables }) + } = await apiFetch<{ data: IProduct }, SpreeSdkVariables>('__UNUSED__', { + variables, + }) return { product: normalizeProduct( diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index ab3d395cf2..64e82e3580 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -86,9 +86,12 @@ export default function getSiteInfoOperation({ const { data: { data: spreeCategoriesSuccessResponse }, - } = await apiFetch<{ - data: ITaxons - }>('__UNUSED__', { + } = await apiFetch< + { + data: ITaxons + }, + SpreeSdkVariables + >('__UNUSED__', { variables: createVariables( requireConfigValue('spreeCategoriesTaxonomyId') as string ), @@ -96,9 +99,12 @@ export default function getSiteInfoOperation({ const { data: { data: spreeBrandsSuccessResponse }, - } = await apiFetch<{ - data: ITaxons - }>('__UNUSED__', { + } = await apiFetch< + { + data: ITaxons + }, + SpreeSdkVariables + >('__UNUSED__', { variables: createVariables( requireConfigValue('spreeBrandsTaxonomyId') as string ), diff --git a/framework/spree/next.config.js b/framework/spree/next.config.js index 13d530a49f..0aaa87e0a7 100644 --- a/framework/spree/next.config.js +++ b/framework/spree/next.config.js @@ -5,4 +5,12 @@ module.exports = { images: { domains: [process.env.NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN], }, + rewrites() { + return [ + { + source: '/checkout', + destination: '/api/checkout', + }, + ] + }, } From c150a79a6bfa9cf7205265a398d43f2efcb664b3 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 18 Aug 2021 18:14:49 +0200 Subject: [PATCH 22/98] Use kebab case instead of camel case --- framework/spree/api/operations/get-all-products.ts | 2 +- framework/spree/api/operations/get-product.ts | 2 +- framework/spree/api/operations/get-site-info.ts | 2 +- framework/spree/api/utils/create-api-fetch.ts | 8 ++++---- framework/spree/cart/use-add-item.tsx | 4 ++-- framework/spree/cart/use-cart.tsx | 4 ++-- framework/spree/cart/use-remove-item.tsx | 4 ++-- framework/spree/cart/use-update-item.tsx | 4 ++-- framework/spree/fetcher.ts | 8 ++++---- framework/spree/index.tsx | 2 +- .../{isomorphicConfig.ts => isomorphic-config.ts} | 6 +++--- framework/spree/product/use-search.tsx | 4 ++-- ...ror.ts => convert-spree-error-to-graph-ql-error.ts} | 0 ...chFetcher.ts => create-customized-fetch-fetcher.ts} | 0 ...uteImageUrl.ts => create-get-absolute-image-url.ts} | 2 +- .../utils/{expandOptions.ts => expand-options.ts} | 2 +- .../utils/{jsonApi.ts => find-json-api-documents.ts} | 0 ...nfigValues.ts => force-isomorphic-config-values.ts} | 2 +- .../spree/utils/{getCartToken.ts => get-cart-token.ts} | 2 +- .../spree/utils/{getImageUrl.ts => get-image-url.ts} | 0 .../utils/{getMediaGallery.ts => get-media-gallery.ts} | 0 ...h.ts => get-spree-sdk-method-from-endpoint-path.ts} | 0 framework/spree/utils/{isServer.ts => is-server.ts} | 0 .../utils/{normalizeCart.ts => normalize-cart.ts} | 8 ++++---- .../{normalizeProduct.ts => normalize-product.ts} | 10 +++++----- .../utils/{requireConfig.ts => require-config.ts} | 0 .../spree/utils/{setCartToken.ts => set-cart-token.ts} | 2 +- ...lidateCookieExpire.ts => validate-cookie-expire.ts} | 0 28 files changed, 39 insertions(+), 39 deletions(-) rename framework/spree/{isomorphicConfig.ts => isomorphic-config.ts} (84%) rename framework/spree/utils/{convertSpreeErrorToGraphQlError.ts => convert-spree-error-to-graph-ql-error.ts} (100%) rename framework/spree/utils/{createCustomizedFetchFetcher.ts => create-customized-fetch-fetcher.ts} (100%) rename framework/spree/utils/{createGetAbsoluteImageUrl.ts => create-get-absolute-image-url.ts} (90%) rename framework/spree/utils/{expandOptions.ts => expand-options.ts} (97%) rename framework/spree/utils/{jsonApi.ts => find-json-api-documents.ts} (100%) rename framework/spree/utils/{forceIsomorphicConfigValues.ts => force-isomorphic-config-values.ts} (97%) rename framework/spree/utils/{getCartToken.ts => get-cart-token.ts} (69%) rename framework/spree/utils/{getImageUrl.ts => get-image-url.ts} (100%) rename framework/spree/utils/{getMediaGallery.ts => get-media-gallery.ts} (100%) rename framework/spree/utils/{getSpreeSdkMethodFromEndpointPath.ts => get-spree-sdk-method-from-endpoint-path.ts} (100%) rename framework/spree/utils/{isServer.ts => is-server.ts} (100%) rename framework/spree/utils/{normalizeCart.ts => normalize-cart.ts} (95%) rename framework/spree/utils/{normalizeProduct.ts => normalize-product.ts} (89%) rename framework/spree/utils/{requireConfig.ts => require-config.ts} (100%) rename framework/spree/utils/{setCartToken.ts => set-cart-token.ts} (82%) rename framework/spree/utils/{validateCookieExpire.ts => validate-cookie-expire.ts} (100%) diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index a821fd15ed..df9c25bd14 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -7,7 +7,7 @@ import type { import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { SpreeApiConfig, SpreeApiProvider } from '../index' import type { SpreeSdkVariables } from 'framework/spree/types' -import normalizeProduct from 'framework/spree/utils/normalizeProduct' +import normalizeProduct from '@framework/utils/normalize-product' export default function getAllProductsOperation({ commerce, diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index 1b4bc87446..79b799d234 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -7,7 +7,7 @@ import type { import type { IProduct } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { SpreeSdkVariables } from 'framework/spree/types' import MissingSlugVariableError from 'framework/spree/errors/MissingSlugVariableError' -import normalizeProduct from 'framework/spree/utils/normalizeProduct' +import normalizeProduct from '@framework/utils/normalize-product' export default function getProductOperation({ commerce, diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index 64e82e3580..e549647441 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -7,7 +7,7 @@ import type { ITaxons, TaxonAttr, } from '@spree/storefront-api-v2-sdk/types/interfaces/Taxon' -import { requireConfigValue } from 'framework/spree/isomorphicConfig' +import { requireConfigValue } from '@framework/isomorphic-config' import type { SpreeSdkVariables } from 'framework/spree/types' import type { SpreeApiConfig, SpreeApiProvider } from '..' diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index d9d78bb600..43227ab570 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -1,17 +1,17 @@ import { SpreeApiConfig } from '..' import { errors, makeClient } from '@spree/storefront-api-v2-sdk' -import { requireConfigValue } from 'framework/spree/isomorphicConfig' -import convertSpreeErrorToGraphQlError from 'framework/spree/utils/convertSpreeErrorToGraphQlError' +import { requireConfigValue } from '@framework/isomorphic-config' +import convertSpreeErrorToGraphQlError from '@framework/utils/convert-spree-error-to-graph-ql-error' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { JsonApiListResponse, JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' -import getSpreeSdkMethodFromEndpointPath from 'framework/spree/utils/getSpreeSdkMethodFromEndpointPath' +import getSpreeSdkMethodFromEndpointPath from '@framework/utils/get-spree-sdk-method-from-endpoint-path' import { SpreeSdkVariables } from 'framework/spree/types' import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' -import createCustomizedFetchFetcher from '../../utils/createCustomizedFetchFetcher' +import createCustomizedFetchFetcher from '@framework/utils/create-customized-fetch-fetcher' import fetch, { Request } from 'node-fetch' const createApiFetch: ( diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index a4f63ac135..8144baa065 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -4,12 +4,12 @@ import type { MutationHook } from '@commerce/utils/types' import { useCallback } from 'react' import useCart from './use-cart' import type { AddItemHook } from '@commerce/types/cart' -import normalizeCart from '@framework/utils/normalizeCart' +import normalizeCart from '@framework/utils/normalize-cart' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { AddItem } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' -import getCartToken from '@framework/utils/getCartToken' +import getCartToken from '@framework/utils/get-cart-token' export default useAddItem as UseAddItem diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index 1fd880730d..a635ce0ffc 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -3,11 +3,11 @@ import type { SWRHook } from '@commerce/utils/types' import useCart from '@commerce/cart/use-cart' import type { UseCart } from '@commerce/cart/use-cart' import type { GetCartHook } from '@commerce/types/cart' -import normalizeCart from '@framework/utils/normalizeCart' +import normalizeCart from '@framework/utils/normalize-cart' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' -import setCartToken from '@framework/utils/setCartToken' +import setCartToken from '@framework/utils/set-cart-token' export default useCart as UseCart diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx index 9ba1f45b21..eaa4827278 100644 --- a/framework/spree/cart/use-remove-item.tsx +++ b/framework/spree/cart/use-remove-item.tsx @@ -4,11 +4,11 @@ import type { UseRemoveItem } from '@commerce/cart/use-remove-item' import type { RemoveItemHook } from '@commerce/types/cart' import useCart from './use-cart' import { useCallback } from 'react' -import normalizeCart from '@framework/utils/normalizeCart' +import normalizeCart from '@framework/utils/normalize-cart' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { GraphQLFetcherResult } from '@commerce/api' import type { IQuery } from '@spree/storefront-api-v2-sdk/types/interfaces/Query' -import getCartToken from '@framework/utils/getCartToken' +import getCartToken from '@framework/utils/get-cart-token' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' export default useRemoveItem as UseRemoveItem diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx index 239cc6c989..0ae82b1043 100644 --- a/framework/spree/cart/use-update-item.tsx +++ b/framework/spree/cart/use-update-item.tsx @@ -6,10 +6,10 @@ import { useCallback } from 'react' import { ValidationError } from '@commerce/utils/errors' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { SetQuantity } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' -import getCartToken from '@framework/utils/getCartToken' +import getCartToken from '@framework/utils/get-cart-token' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' -import normalizeCart from '@framework/utils/normalizeCart' +import normalizeCart from '@framework/utils/normalize-cart' import debounce from 'lodash.debounce' export default useUpdateItem as UseUpdateItem diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 64797ee327..001fdb6dd1 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -1,5 +1,5 @@ import type { Fetcher } from '@commerce/utils/types' -import convertSpreeErrorToGraphQlError from './utils/convertSpreeErrorToGraphQlError' +import convertSpreeErrorToGraphQlError from './utils/convert-spree-error-to-graph-ql-error' import { makeClient } from '@spree/storefront-api-v2-sdk' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { @@ -7,12 +7,12 @@ import type { JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import { errors } from '@spree/storefront-api-v2-sdk' -import { requireConfigValue } from './isomorphicConfig' -import getSpreeSdkMethodFromEndpointPath from './utils/getSpreeSdkMethodFromEndpointPath' +import { requireConfigValue } from './isomorphic-config' +import getSpreeSdkMethodFromEndpointPath from './utils/get-spree-sdk-method-from-endpoint-path' import SpreeSdkMethodFromEndpointPathError from './errors/SpreeSdkMethodFromEndpointPathError' import type { SpreeSdkVariables } from './types' import type { GraphQLFetcherResult } from '@commerce/api' -import createCustomizedFetchFetcher from './utils/createCustomizedFetchFetcher' +import createCustomizedFetchFetcher from './utils/create-customized-fetch-fetcher' const client = makeClient({ host: requireConfigValue('spreeApiHost') as string, diff --git a/framework/spree/index.tsx b/framework/spree/index.tsx index 03f93cc1cd..6ca5bd1985 100644 --- a/framework/spree/index.tsx +++ b/framework/spree/index.tsx @@ -9,7 +9,7 @@ import { import { provider } from './provider' import type { Provider } from './provider' -import { requireConfigValue } from './isomorphicConfig' +import { requireConfigValue } from './isomorphic-config' export type SpreeProps = { children: ReactNode diff --git a/framework/spree/isomorphicConfig.ts b/framework/spree/isomorphic-config.ts similarity index 84% rename from framework/spree/isomorphicConfig.ts rename to framework/spree/isomorphic-config.ts index 8806c8b7cd..ed6a9fa34a 100644 --- a/framework/spree/isomorphicConfig.ts +++ b/framework/spree/isomorphic-config.ts @@ -1,6 +1,6 @@ -import forceIsomorphicConfigValues from './utils/forceIsomorphicConfigValues' -import requireConfig from './utils/requireConfig' -import validateCookieExpire from './utils/validateCookieExpire' +import forceIsomorphicConfigValues from './utils/force-isomorphic-config-values' +import requireConfig from './utils/require-config' +import validateCookieExpire from './utils/validate-cookie-expire' const isomorphicConfig = { spreeApiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index efeedc2ab2..7cadca277c 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -2,12 +2,12 @@ import type { Fetcher, SWRHook } from '@commerce/utils/types' import useSearch from '@commerce/product/use-search' import type { Product, SearchProductsHook } from '@commerce/types/product' import type { UseSearch } from '@commerce/product/use-search' -import normalizeProduct from '../utils/normalizeProduct' +import normalizeProduct from '../utils/normalize-product' import type { GraphQLFetcherResult } from '@commerce/api' import { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' const nextToSpreeSortMap: { [key: string]: string } = { - 'trending-desc': 'updated_at', // Spree has no "trending" filter. Use updated_at. + 'trending-desc': 'updated_at', // FIXME: Spree has no "trending" filter. Using updated_at. 'latest-desc': 'updated_at', 'price-asc': 'price', 'price-desc': '-price', diff --git a/framework/spree/utils/convertSpreeErrorToGraphQlError.ts b/framework/spree/utils/convert-spree-error-to-graph-ql-error.ts similarity index 100% rename from framework/spree/utils/convertSpreeErrorToGraphQlError.ts rename to framework/spree/utils/convert-spree-error-to-graph-ql-error.ts diff --git a/framework/spree/utils/createCustomizedFetchFetcher.ts b/framework/spree/utils/create-customized-fetch-fetcher.ts similarity index 100% rename from framework/spree/utils/createCustomizedFetchFetcher.ts rename to framework/spree/utils/create-customized-fetch-fetcher.ts diff --git a/framework/spree/utils/createGetAbsoluteImageUrl.ts b/framework/spree/utils/create-get-absolute-image-url.ts similarity index 90% rename from framework/spree/utils/createGetAbsoluteImageUrl.ts rename to framework/spree/utils/create-get-absolute-image-url.ts index 37932f15d9..7b75aa7801 100644 --- a/framework/spree/utils/createGetAbsoluteImageUrl.ts +++ b/framework/spree/utils/create-get-absolute-image-url.ts @@ -1,5 +1,5 @@ import { SpreeProductImage } from '../types' -import getImageUrl from './getImageUrl' +import getImageUrl from './get-image-url' const createGetAbsoluteImageUrl = (host: string) => diff --git a/framework/spree/utils/expandOptions.ts b/framework/spree/utils/expand-options.ts similarity index 97% rename from framework/spree/utils/expandOptions.ts rename to framework/spree/utils/expand-options.ts index 0ea5a28a2e..f9381a7381 100644 --- a/framework/spree/utils/expandOptions.ts +++ b/framework/spree/utils/expand-options.ts @@ -8,7 +8,7 @@ import type { } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import SpreeResponseContentError from '../errors/SpreeResponseContentError' -import { findIncluded } from './jsonApi' +import { findIncluded } from './find-json-api-documents' const isColorProductOption = (productOption: ProductOption) => { return productOption.displayName === 'Color' diff --git a/framework/spree/utils/jsonApi.ts b/framework/spree/utils/find-json-api-documents.ts similarity index 100% rename from framework/spree/utils/jsonApi.ts rename to framework/spree/utils/find-json-api-documents.ts diff --git a/framework/spree/utils/forceIsomorphicConfigValues.ts b/framework/spree/utils/force-isomorphic-config-values.ts similarity index 97% rename from framework/spree/utils/forceIsomorphicConfigValues.ts rename to framework/spree/utils/force-isomorphic-config-values.ts index 359ec51670..630b6859e4 100644 --- a/framework/spree/utils/forceIsomorphicConfigValues.ts +++ b/framework/spree/utils/force-isomorphic-config-values.ts @@ -1,6 +1,6 @@ import type { NonUndefined, UnknownObjectValues } from '../types' import MisconfigurationError from '../errors/MisconfigurationError' -import isServer from './isServer' +import isServer from './is-server' const generateMisconfigurationErrorMessage = ( keys: Array diff --git a/framework/spree/utils/getCartToken.ts b/framework/spree/utils/get-cart-token.ts similarity index 69% rename from framework/spree/utils/getCartToken.ts rename to framework/spree/utils/get-cart-token.ts index 81bb3a897e..72fbe26183 100644 --- a/framework/spree/utils/getCartToken.ts +++ b/framework/spree/utils/get-cart-token.ts @@ -1,4 +1,4 @@ -import { requireConfigValue } from '@framework/isomorphicConfig' +import { requireConfigValue } from '@framework/isomorphic-config' import Cookies from 'js-cookie' const getCartToken = () => diff --git a/framework/spree/utils/getImageUrl.ts b/framework/spree/utils/get-image-url.ts similarity index 100% rename from framework/spree/utils/getImageUrl.ts rename to framework/spree/utils/get-image-url.ts diff --git a/framework/spree/utils/getMediaGallery.ts b/framework/spree/utils/get-media-gallery.ts similarity index 100% rename from framework/spree/utils/getMediaGallery.ts rename to framework/spree/utils/get-media-gallery.ts diff --git a/framework/spree/utils/getSpreeSdkMethodFromEndpointPath.ts b/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts similarity index 100% rename from framework/spree/utils/getSpreeSdkMethodFromEndpointPath.ts rename to framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts diff --git a/framework/spree/utils/isServer.ts b/framework/spree/utils/is-server.ts similarity index 100% rename from framework/spree/utils/isServer.ts rename to framework/spree/utils/is-server.ts diff --git a/framework/spree/utils/normalizeCart.ts b/framework/spree/utils/normalize-cart.ts similarity index 95% rename from framework/spree/utils/normalizeCart.ts rename to framework/spree/utils/normalize-cart.ts index 0a40727381..644e5726cf 100644 --- a/framework/spree/utils/normalizeCart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -5,7 +5,7 @@ import type { SelectedOption, } from '@commerce/types/cart' import MissingLineItemVariantError from '@framework/errors/MissingLineItemVariantError' -import { requireConfigValue } from '@framework/isomorphicConfig' +import { requireConfigValue } from '@framework/isomorphic-config' import type { JsonApiDocument, JsonApiListResponse, @@ -14,9 +14,9 @@ import type { import type { OrderAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' -import createGetAbsoluteImageUrl from './createGetAbsoluteImageUrl' -import getMediaGallery from './getMediaGallery' -import { findIncluded, findIncludedOfType } from './jsonApi' +import createGetAbsoluteImageUrl from './create-get-absolute-image-url' +import getMediaGallery from './get-media-gallery' +import { findIncluded, findIncludedOfType } from './find-json-api-documents' const isColorProductOption = (productOptionType: any) => { // TODO: Fix type and merge with isColorProductOption in framework/spree/utils/expandOptions.ts diff --git a/framework/spree/utils/normalizeProduct.ts b/framework/spree/utils/normalize-product.ts similarity index 89% rename from framework/spree/utils/normalizeProduct.ts rename to framework/spree/utils/normalize-product.ts index bc02301aee..5c38aaffc2 100644 --- a/framework/spree/utils/normalizeProduct.ts +++ b/framework/spree/utils/normalize-product.ts @@ -10,11 +10,11 @@ import type { } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' -import { requireConfigValue } from '../isomorphicConfig' -import createGetAbsoluteImageUrl from './createGetAbsoluteImageUrl' -import expandOptions from './expandOptions' -import getMediaGallery from './getMediaGallery' -import { findIncludedOfType } from './jsonApi' +import { requireConfigValue } from '@framework/isomorphic-config' +import createGetAbsoluteImageUrl from './create-get-absolute-image-url' +import expandOptions from './expand-options' +import getMediaGallery from './get-media-gallery' +import { findIncludedOfType } from './find-json-api-documents' const normalizeProduct = ( spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, diff --git a/framework/spree/utils/requireConfig.ts b/framework/spree/utils/require-config.ts similarity index 100% rename from framework/spree/utils/requireConfig.ts rename to framework/spree/utils/require-config.ts diff --git a/framework/spree/utils/setCartToken.ts b/framework/spree/utils/set-cart-token.ts similarity index 82% rename from framework/spree/utils/setCartToken.ts rename to framework/spree/utils/set-cart-token.ts index 68caaa19f8..6cffc1e79d 100644 --- a/framework/spree/utils/setCartToken.ts +++ b/framework/spree/utils/set-cart-token.ts @@ -1,4 +1,4 @@ -import { requireConfigValue } from '@framework/isomorphicConfig' +import { requireConfigValue } from '@framework/isomorphic-config' import Cookies from 'js-cookie' const setCartToken = (cartToken: string) => { diff --git a/framework/spree/utils/validateCookieExpire.ts b/framework/spree/utils/validate-cookie-expire.ts similarity index 100% rename from framework/spree/utils/validateCookieExpire.ts rename to framework/spree/utils/validate-cookie-expire.ts From 718493a6f8e8c9f3a4265c3b0048f7c0e88aa114 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 18 Aug 2021 18:17:02 +0200 Subject: [PATCH 23/98] Remove outdated comments --- framework/spree/provider.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/spree/provider.ts b/framework/spree/provider.ts index 3a96f456d8..d6511bd36c 100644 --- a/framework/spree/provider.ts +++ b/framework/spree/provider.ts @@ -1,7 +1,6 @@ import type { Provider } from '@commerce' import fetcher from './fetcher' -// TODO: Using dummy hooks to fetch static content. Based on the local framework. import { handler as useCart } from './cart/use-cart' import { handler as useAddItem } from './cart/use-add-item' import { handler as useUpdateItem } from './cart/use-update-item' @@ -16,7 +15,6 @@ const provider = { locale: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string. cartCookie: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string. fetcher, - // FIXME: Add dummy hooks for below based on framework/local EXCEPT use-product cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, customer: { useCustomer }, products: { useSearch }, From 01c702a87e6c275c7f8ea76616044322d4d34b04 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 19 Aug 2021 10:35:17 +0200 Subject: [PATCH 24/98] Remove outdated comment --- framework/spree/utils/normalize-cart.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index 644e5726cf..676ddfbd8d 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -187,7 +187,6 @@ const normalizeCart = ( taxesIncluded: true, lineItems, lineItemsSubtotalPrice: parseFloat(spreeCart.attributes.item_total), - // TODO: We need a value from Spree which includes item total and discounts in one value for subtotalPrice. subtotalPrice: parseFloat(spreeCart.attributes.item_total), totalPrice: parseFloat(spreeCart.attributes.total), customerId: spreeCart.attributes.token, From bf4adbdd2c74ab68ee9246a3adfdd95fe0244eba Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 19 Aug 2021 11:19:50 +0200 Subject: [PATCH 25/98] Resolve isColorProductOption duplication --- framework/spree/types/index.ts | 11 +++++++++++ framework/spree/utils/normalize-cart.ts | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 6b5e98b05a..1f3eb53a2d 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -33,3 +33,14 @@ export interface SpreeProductImage extends JsonApiDocument { styles: ImageStyle[] } } + +export interface OptionTypeAttr extends JsonApiDocument { + attributes: { + name: string + presentation: string + position: number + created_at: string + updated_at: string + filterable: boolean + } +} diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index 676ddfbd8d..c8e4ea69ea 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -12,14 +12,14 @@ import type { JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { OrderAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' -import { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import getMediaGallery from './get-media-gallery' import { findIncluded, findIncludedOfType } from './find-json-api-documents' +import type { OptionTypeAttr } from '@framework/types' -const isColorProductOption = (productOptionType: any) => { - // TODO: Fix type and merge with isColorProductOption in framework/spree/utils/expandOptions.ts +const isColorProductOption = (productOptionType: OptionTypeAttr) => { return productOptionType.attributes.presentation === 'Color' } From c9323443ce957b429556e263a47c85091a5131d9 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 19 Aug 2021 11:34:33 +0200 Subject: [PATCH 26/98] Type Spree variants and line items and temporarily remove height, width and depth --- framework/spree/types/index.ts | 44 +++++++++++++++++++++++++ framework/spree/utils/normalize-cart.ts | 25 +++++++++----- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 1f3eb53a2d..3ff71eaa12 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -44,3 +44,47 @@ export interface OptionTypeAttr extends JsonApiDocument { filterable: boolean } } + +export interface LineItemAttr extends JsonApiDocument { + attributes: { + name: string + quantity: number + slug: string + options_text: string + price: string + currency: string + display_price: string + total: string + display_total: string + adjustment_total: string + display_adjustment_total: string + additional_tax_total: string + display_additional_tax_total: string + discounted_amount: string + display_discounted_amount: string + pre_tax_amount: string + display_pre_tax_amount: string + promo_total: string + display_promo_total: string + included_tax_total: string + display_inluded_tax_total: string + } +} + +export interface VariantAttr extends JsonApiDocument { + attributes: { + sku: string + price: string + currency: string + display_price: string + weight: string + height: string + width: string + depth: string + is_master: boolean + options_text: string + purchasable: boolean + in_stock: boolean + backorderable: boolean + } +} diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index c8e4ea69ea..23cd772673 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -7,7 +7,6 @@ import type { import MissingLineItemVariantError from '@framework/errors/MissingLineItemVariantError' import { requireConfigValue } from '@framework/isomorphic-config' import type { - JsonApiDocument, JsonApiListResponse, JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' @@ -17,7 +16,11 @@ import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import getMediaGallery from './get-media-gallery' import { findIncluded, findIncludedOfType } from './find-json-api-documents' -import type { OptionTypeAttr } from '@framework/types' +import type { + LineItemAttr, + OptionTypeAttr, + VariantAttr, +} from '@framework/types' const isColorProductOption = (productOptionType: OptionTypeAttr) => { return productOptionType.attributes.presentation === 'Color' @@ -25,7 +28,7 @@ const isColorProductOption = (productOptionType: OptionTypeAttr) => { const normalizeVariant = ( spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, - spreeVariant: JsonApiDocument + spreeVariant: VariantAttr ): ProductVariant => { const productIdentifier = spreeVariant.relationships.product .data as RelationType @@ -81,18 +84,22 @@ const normalizeVariant = ( image: lineItemImage, isInStock: spreeVariant.attributes.in_stock, availableForSale: spreeVariant.attributes.purchasable, - weight: spreeVariant.attributes.weight, - height: spreeVariant.attributes.height, - width: spreeVariant.attributes.width, - depth: spreeVariant.attributes.depth, + ...(spreeVariant.attributes.weight === '0.0' + ? {} + : { + weight: { + value: parseFloat(spreeVariant.attributes.weight), + unit: 'KILOGRAMS', + }, + }), + // TODO: Add height, width and depth when Measurement type allows distance measurements. } } const normalizeLineItem = ( spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, - spreeLineItem: JsonApiDocument + spreeLineItem: LineItemAttr ): LineItem => { - //TODO: Replace JsonApiDocument type in spreeLineItem with more specific, new Spree line item item const variantIdentifier = spreeLineItem.relationships.variant .data as RelationType const variant = findIncluded( From 8f95d76dcd7e0fa86a064ca60601d72524ec3abc Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 19 Aug 2021 11:45:12 +0200 Subject: [PATCH 27/98] Remove outdated comment --- framework/spree/utils/normalize-product.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 5c38aaffc2..4f27818516 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -36,9 +36,6 @@ const normalizeProduct = ( currencyCode: spreeProduct.attributes.currency, } - // TODO: Add sku to product object equal to master SKU from Spree. - // Currently, the Spree API doesn't return it. - const hasNonMasterVariants = (spreeProduct.relationships.variants.data as RelationType[]).length > 1 From 7d5a63bbf4e34dc1d08deaa4e7e43c3f0a5e6420 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 19 Aug 2021 11:49:27 +0200 Subject: [PATCH 28/98] Update comments about cart discounts --- framework/spree/utils/normalize-cart.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index 23cd772673..f4106a7eb5 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -170,7 +170,7 @@ const normalizeLineItem = ( productId: productIdentifier.id, name: spreeLineItem.attributes.name, quantity: spreeLineItem.attributes.quantity, - discounts: [], // TODO: Retrieve from Spree + discounts: [], // TODO: Implement when the template starts displaying them. path, variant: normalizeVariant(spreeSuccessResponse, variant), options, @@ -198,8 +198,7 @@ const normalizeCart = ( totalPrice: parseFloat(spreeCart.attributes.total), customerId: spreeCart.attributes.token, email: spreeCart.attributes.email, - discounts: [], - // discounts: [{value: number}] // TODO: Retrieve from Spree + discounts: [], // TODO: Implement when the template starts displaying them. } } From ed49ac8833da48390634fd72ee48912133a74b1e Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 19 Aug 2021 12:24:07 +0200 Subject: [PATCH 29/98] Remove 'spree' prefix from isomorphicConfig and add lastUpdatedProductsPrerenderCount --- framework/spree/.env.template | 1 + .../spree/api/operations/get-site-info.ts | 4 ++-- framework/spree/api/utils/create-api-fetch.ts | 2 +- framework/spree/fetcher.ts | 2 +- framework/spree/isomorphic-config.ts | 22 +++++++++++-------- framework/spree/utils/normalize-cart.ts | 4 ++-- framework/spree/utils/normalize-product.ts | 2 +- .../spree/utils/validate-cookie-expire.ts | 4 ++-- 8 files changed, 23 insertions(+), 18 deletions(-) diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 60698d794c..7f584f8bf7 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -13,3 +13,4 @@ NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID=1 NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID=27 NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS=false +NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT=10 diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index e549647441..b094916c9d 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -93,7 +93,7 @@ export default function getSiteInfoOperation({ SpreeSdkVariables >('__UNUSED__', { variables: createVariables( - requireConfigValue('spreeCategoriesTaxonomyId') as string + requireConfigValue('categoriesTaxonomyId') as string ), }) @@ -106,7 +106,7 @@ export default function getSiteInfoOperation({ SpreeSdkVariables >('__UNUSED__', { variables: createVariables( - requireConfigValue('spreeBrandsTaxonomyId') as string + requireConfigValue('brandsTaxonomyId') as string ), }) diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index 43227ab570..c4babcc83a 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -20,7 +20,7 @@ const createApiFetch: ( _getConfig ) => { const client = makeClient({ - host: requireConfigValue('spreeApiHost') as string, + host: requireConfigValue('apiHost') as string, fetcherType: 'custom', createFetcher: (fetcherOptions) => { return createCustomizedFetchFetcher({ diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 001fdb6dd1..3b6766b385 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -15,7 +15,7 @@ import type { GraphQLFetcherResult } from '@commerce/api' import createCustomizedFetchFetcher from './utils/create-customized-fetch-fetcher' const client = makeClient({ - host: requireConfigValue('spreeApiHost') as string, + host: requireConfigValue('apiHost') as string, fetcherType: 'custom', createFetcher: (fetcherOptions) => { return createCustomizedFetchFetcher({ diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index ed6a9fa34a..6255b1535d 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -1,34 +1,38 @@ import forceIsomorphicConfigValues from './utils/force-isomorphic-config-values' import requireConfig from './utils/require-config' import validateCookieExpire from './utils/validate-cookie-expire' +import validateProductsPrerenderCount from './utils/validate-products-prerender-count' const isomorphicConfig = { - spreeApiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, + apiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, defaultLocale: process.env.NEXT_PUBLIC_SPREE_DEFAULT_LOCALE, cartCookieName: process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_NAME, cartCookieExpire: validateCookieExpire( process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE ), - spreeImageHost: process.env.NEXT_PUBLIC_SPREE_IMAGE_HOST, - spreeCategoriesTaxonomyId: - process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID, - spreeBrandsTaxonomyId: process.env.NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID, + imageHost: process.env.NEXT_PUBLIC_SPREE_IMAGE_HOST, + categoriesTaxonomyId: process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID, + brandsTaxonomyId: process.env.NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID, showSingleVariantOptions: process.env.NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS === 'true', + lastUpdatedProductsPrerenderCount: validateProductsPrerenderCount( + process.env.NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT + ), } export default forceIsomorphicConfigValues( isomorphicConfig, [], [ - 'spreeApiHost', + 'apiHost', 'defaultLocale', 'cartCookieName', 'cartCookieExpire', - 'spreeImageHost', - 'spreeCategoriesTaxonomyId', - 'spreeBrandsTaxonomyId', + 'imageHost', + 'categoriesTaxonomyId', + 'brandsTaxonomyId', 'showSingleVariantOptions', + 'lastUpdatedProductsPrerenderCount', ] ) diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index f4106a7eb5..47489193cd 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -54,7 +54,7 @@ const normalizeVariant = ( const variantImage = getMediaGallery( spreeVariantImageRecords, - createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost') as string) + createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string) )[0] if (variantImage) { @@ -68,7 +68,7 @@ const normalizeVariant = ( const productImage = getMediaGallery( spreeProductImageRecords, - createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost') as string) + createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string) )[0] lineItemImage = productImage diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 4f27818516..f01fea0897 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -28,7 +28,7 @@ const normalizeProduct = ( const images = getMediaGallery( spreeImageRecords, - createGetAbsoluteImageUrl(requireConfigValue('spreeImageHost') as string) + createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string) ) const price: ProductPrice = { diff --git a/framework/spree/utils/validate-cookie-expire.ts b/framework/spree/utils/validate-cookie-expire.ts index 35e0434394..1bd9872738 100644 --- a/framework/spree/utils/validate-cookie-expire.ts +++ b/framework/spree/utils/validate-cookie-expire.ts @@ -1,8 +1,8 @@ -const validateCookieExpire = (expire: unknown) => { +const validateCookieExpire = (expire: unknown): number => { let expireInteger: number if (typeof expire === 'string') { - expireInteger = parseFloat(expire || '') + expireInteger = parseFloat(expire) } else if (typeof expire === 'number') { expireInteger = expire } else { From a191e28df7e90a808db52a36400957a2da2682d6 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 19 Aug 2021 14:02:13 +0200 Subject: [PATCH 30/98] Implement getAllProductPaths to prerender some products during build time --- .../api/operations/get-all-product-paths.ts | 96 ++++++++++++++++--- framework/spree/types/index.ts | 9 ++ framework/spree/utils/get-product-path.ts | 7 ++ framework/spree/utils/normalize-product.ts | 3 +- .../validate-products-prerender-count.ts | 21 ++++ 5 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 framework/spree/utils/get-product-path.ts create mode 100644 framework/spree/utils/validate-products-prerender-count.ts diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts index b96f7008a5..9dab1e2a30 100644 --- a/framework/spree/api/operations/get-all-product-paths.ts +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -1,18 +1,90 @@ -// import data from '../../data.json' +import type { + OperationContext, + OperationOptions, +} from '@commerce/api/operations' +import type { Product } from '@commerce/types/product' +import type { GetAllProductPathsOperation } from '@commerce/types/product' +import { requireConfigValue } from '@framework/isomorphic-config' +import type { IProductsSlugs, SpreeSdkVariables } from '@framework/types' +import getProductPath from '@framework/utils/get-product-path' +import type { SpreeApiConfig, SpreeApiProvider } from '..' -export type GetAllProductPathsResult = { - products: Array<{ path: string }> -} +export default function getAllProductPathsOperation({ + commerce, +}: OperationContext) { + async function getAllProductPaths< + T extends GetAllProductPathsOperation + >(opts?: { + variables?: T['variables'] + config?: Partial + }): Promise + + async function getAllProductPaths( + opts: { + variables?: T['variables'] + config?: Partial + } & OperationOptions + ): Promise + + async function getAllProductPaths({ + query, + variables: getAllProductPathsVariables = {}, + config: userConfig, + }: { + query?: string + variables?: T['variables'] + config?: Partial + } = {}): Promise { + console.info( + 'getAllProductPaths called. Configuration: ', + 'query: ', + query, + 'getAllProductPathsVariables: ', + getAllProductPathsVariables, + 'config: ', + userConfig + ) + + const productsCount = requireConfigValue( + 'lastUpdatedProductsPrerenderCount' + ) + + if (productsCount === 0) { + return { + products: [], + } + } + + const variables: SpreeSdkVariables = { + methodPath: 'products.list', + arguments: [ + { + fields: { + product: 'slug', + }, + per_page: productsCount, + }, + ], + } + + const config = commerce.getConfig(userConfig) + const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. + + const { + data: { data: spreeSuccessResponse }, + } = await apiFetch<{ data: IProductsSlugs }, SpreeSdkVariables>( + '__UNUSED__', + { + variables, + } + ) -export default function getAllProductPathsOperation() { - function getAllProductPaths(): Promise { - console.log('getAllProductPaths called.') + const normalizedProductsPaths: Pick[] = + spreeSuccessResponse.data.map((spreeProduct) => ({ + path: getProductPath(spreeProduct), + })) - return Promise.resolve({ - // products: data.products.map(({ path }) => ({ path })), - // TODO: Return Storefront [{ path: '/long-sleeve-shirt' }, ...] from Spree products. Paths using product IDs are fine too. - products: [], - }) + return { products: normalizedProductsPaths } } return getAllProductPaths diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 3ff71eaa12..86f5bcd97d 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -88,3 +88,12 @@ export interface VariantAttr extends JsonApiDocument { backorderable: boolean } } + +export interface ProductSlugAttr extends JsonApiDocument { + attributes: { + slug: string + } +} +export interface IProductsSlugs extends JsonApiListResponse { + data: ProductSlugAttr[] +} diff --git a/framework/spree/utils/get-product-path.ts b/framework/spree/utils/get-product-path.ts new file mode 100644 index 0000000000..ee5d0b0fec --- /dev/null +++ b/framework/spree/utils/get-product-path.ts @@ -0,0 +1,7 @@ +import type { ProductSlugAttr } from '@framework/types' + +const getProductPath = (partialSpreeProduct: ProductSlugAttr): string => { + return `/${partialSpreeProduct.attributes.slug}` +} + +export default getProductPath diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index f01fea0897..83234e5664 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -15,6 +15,7 @@ import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import expandOptions from './expand-options' import getMediaGallery from './get-media-gallery' import { findIncludedOfType } from './find-json-api-documents' +import getProductPath from './get-product-path' const normalizeProduct = ( spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, @@ -82,7 +83,7 @@ const normalizeProduct = ( }) const slug = spreeProduct.attributes.slug - const path = `/${spreeProduct.attributes.slug}` + const path = getProductPath(spreeProduct) return { id: spreeProduct.id, diff --git a/framework/spree/utils/validate-products-prerender-count.ts b/framework/spree/utils/validate-products-prerender-count.ts new file mode 100644 index 0000000000..024db1ea64 --- /dev/null +++ b/framework/spree/utils/validate-products-prerender-count.ts @@ -0,0 +1,21 @@ +const validateProductsPrerenderCount = (prerenderCount: unknown): number => { + let prerenderCountInteger: number + + if (typeof prerenderCount === 'string') { + prerenderCountInteger = parseInt(prerenderCount) + } else if (typeof prerenderCount === 'number') { + prerenderCountInteger = prerenderCount + } else { + throw new TypeError( + 'prerenderCount count must be a string containing a number or an integer.' + ) + } + + if (prerenderCountInteger < 0) { + throw new RangeError('prerenderCount must be non-negative.') + } + + return prerenderCountInteger +} + +export default validateProductsPrerenderCount From 3d2ee58c4d722c1595d6491eedfeac1b28a6a15a Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 20 Aug 2021 12:29:27 +0200 Subject: [PATCH 31/98] Adjust fetchers to the latest Spree SDK interface --- framework/spree/api/operations/get-all-product-paths.ts | 1 + framework/spree/api/operations/get-all-products.ts | 1 + framework/spree/api/operations/get-product.ts | 1 + framework/spree/product/use-search.tsx | 1 + framework/spree/utils/create-customized-fetch-fetcher.ts | 7 +------ 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts index 9dab1e2a30..6ea081bb3f 100644 --- a/framework/spree/api/operations/get-all-product-paths.ts +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -58,6 +58,7 @@ export default function getAllProductPathsOperation({ const variables: SpreeSdkVariables = { methodPath: 'products.list', arguments: [ + {}, { fields: { product: 'slug', diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index df9c25bd14..5ad76cad14 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -45,6 +45,7 @@ export default function getAllProductsOperation({ const variables: SpreeSdkVariables = { methodPath: 'products.list', arguments: [ + {}, { include: 'variants,images,option_types,variants.option_values', per_page: first, diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index 79b799d234..57d43a8f05 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -52,6 +52,7 @@ export default function getProductOperation({ methodPath: 'products.show', arguments: [ getProductVariables.slug, + {}, { include: 'variants,images,option_types,variants.option_values', }, diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 7cadca277c..2945592be3 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -46,6 +46,7 @@ export const handler: SWRHook = { variables: { methodPath: 'products.list', arguments: [ + {}, { include: 'variants,images,option_types,variants.option_values', per_page: 50, diff --git a/framework/spree/utils/create-customized-fetch-fetcher.ts b/framework/spree/utils/create-customized-fetch-fetcher.ts index 9ed35a82fc..4adb64a45d 100644 --- a/framework/spree/utils/create-customized-fetch-fetcher.ts +++ b/framework/spree/utils/create-customized-fetch-fetcher.ts @@ -47,7 +47,6 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( try { const response: Response = await fetch(request) - const data = await response.json() if (!response.ok) { @@ -61,11 +60,7 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( data: Object.setPrototypeOf({ data }, { response }), } } catch (error) { - if (error instanceof TypeError) { - throw new FetchError(null, request, null) - } - - throw error + throw new FetchError(null, request, null, error.message) } } catch (error) { if (error instanceof FetchError) { From eba8c28cc5d9a5b2038cac3b5b09f9fc9963b6f0 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 20 Aug 2021 13:19:27 +0200 Subject: [PATCH 32/98] Add types to Spree taxons mapping --- .../spree/api/operations/get-site-info.ts | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index b094916c9d..456c624650 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -111,25 +111,29 @@ export default function getSiteInfoOperation({ }) const normalizedCategories: GetSiteInfoOperation['data']['categories'] = - spreeCategoriesSuccessResponse.data.sort(taxonsSort).map((spreeTaxon) => { - return { - id: spreeTaxon.id, - name: spreeTaxon.attributes.name, - slug: spreeTaxon.id, - path: spreeTaxon.id, - } - }) + spreeCategoriesSuccessResponse.data + .sort(taxonsSort) + .map((spreeTaxon: TaxonAttr) => { + return { + id: spreeTaxon.id, + name: spreeTaxon.attributes.name, + slug: spreeTaxon.id, + path: spreeTaxon.id, + } + }) const normalizedBrands: GetSiteInfoOperation['data']['brands'] = - spreeBrandsSuccessResponse.data.sort(taxonsSort).map((spreeTaxon) => { - return { - node: { - entityId: spreeTaxon.id, - path: `brands/${spreeTaxon.id}`, - name: spreeTaxon.attributes.name, - }, - } - }) + spreeBrandsSuccessResponse.data + .sort(taxonsSort) + .map((spreeTaxon: TaxonAttr) => { + return { + node: { + entityId: spreeTaxon.id, + path: `brands/${spreeTaxon.id}`, + name: spreeTaxon.attributes.name, + }, + } + }) return { categories: normalizedCategories, From 464c8cae7c79e5698944e50e2078998d0cf1c1de Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 20 Aug 2021 13:41:07 +0200 Subject: [PATCH 33/98] Revert port change in package.json scripts --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 14ae7a79ba..9806f37142 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "nextjs-commerce", "version": "1.0.0", "scripts": { - "dev": "NODE_OPTIONS='--inspect' next dev -p 4000", + "dev": "NODE_OPTIONS='--inspect' next dev", "build": "next build", - "start": "next start -p 4000", + "start": "next start", "analyze": "BUNDLE_ANALYZE=both yarn build", "prettier-fix": "prettier --write .", "find:unused": "npx next-unused", From 4a007cab93b83dbe0d9c2f5e52072c03b40ee770 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 20 Aug 2021 14:02:18 +0200 Subject: [PATCH 34/98] Add basic README describing Spree installation --- framework/spree/README.md | 51 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/framework/spree/README.md b/framework/spree/README.md index 1d2f12f870..1fc5a46fe6 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -1,2 +1,49 @@ -TODO: Base README on other Framework READMEs. -TODO: Link to demo site running NextJS Commerce communicating with Spree. +# [Spree Commerce][1] Provider + +A preview integration of Spree Commerce within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart as a guest user. + +## Installation + +Start by following the [instructions for setting up NextJS Commerce][2]. + +Next, setup Spree. The easiest way to run Spree locally is to follow the installation tutorial available at [the Spree Starter GitHub repository][3]. + +You may have to adjust Spree Starter to allow `localhost` and [CORS][4] requests. Run `docker-compose run web bundle add rack-cors` and: + +```ruby +# In config/application.rb add a configuration for CORS: + +module SpreeStarter + class Application < Rails::Application + config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' + resource '*', headers: :any, methods: :any + end + end + end +end + +# In config/environments/development.rb add 'localhost': + +config.hosts << 'localhost' +``` + +By default, Spree Starter and NextJS Commerce both run on port `3000`. Avoid collisions by running NextJS Commerce on port `4000`: + +```json +// In package.json, modify two scripts: +{ + "scripts": { + "dev": "NODE_OPTIONS='--inspect' next dev -p 4000", + "start": "next start -p 4000", + ... + } + ... +} +``` + +[1]: https://spreecommerce.org/ +[2]: https://github.com/vercel/commerce +[3]: https://github.com/spree/spree_starter +[4]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS From f16b6b8188258655a1841bf34d8446d72bb25aae Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 20 Aug 2021 14:18:14 +0200 Subject: [PATCH 35/98] Expand README's installation section --- framework/spree/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/framework/spree/README.md b/framework/spree/README.md index 1fc5a46fe6..d83b61dcde 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -1,4 +1,4 @@ -# [Spree Commerce][1] Provider +# [Spree Commerce][1] Framework A preview integration of Spree Commerce within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart as a guest user. @@ -8,7 +8,7 @@ Start by following the [instructions for setting up NextJS Commerce][2]. Next, setup Spree. The easiest way to run Spree locally is to follow the installation tutorial available at [the Spree Starter GitHub repository][3]. -You may have to adjust Spree Starter to allow `localhost` and [CORS][4] requests. Run `docker-compose run web bundle add rack-cors` and: +You have to adjust Spree Starter to allow `localhost` and [CORS][4] requests. Run `docker-compose run web bundle add rack-cors` and: ```ruby # In config/application.rb add a configuration for CORS: @@ -43,6 +43,12 @@ By default, Spree Starter and NextJS Commerce both run on port `3000`. Avoid col } ``` +Third, supply NextJS Commerce with custom environment variables required by the Spree Framework. Create a `.env.local` file in the root of NJC with contents based on `framework/spree/.env.template`. + +`NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID` rely on IDs generated by Spree. Go to the Spree admin panel and create Categories and Brands taxonomies if they don't exist and copy their IDs into `.env.local`. The values of the other environment variables can be copied from `framework/spree/.env.template` as is. + +Lastly, run `yarn dev` :tada: + [1]: https://spreecommerce.org/ [2]: https://github.com/vercel/commerce [3]: https://github.com/spree/spree_starter From df7e3791e20d19ad1ac823836640d728a69514c7 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 20 Aug 2021 16:13:47 +0200 Subject: [PATCH 36/98] Upgrade Spree SDK to 4.7.0 and add node-fetch to dependencies --- package.json | 3 ++- yarn.lock | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9806f37142..bd6cd21ee6 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@react-spring/web": "^9.2.1", - "@spree/storefront-api-v2-sdk": "^4.5.4", + "@spree/storefront-api-v2-sdk": "^4.7.0", "@types/qs": "^6.9.7", "@vercel/fetch": "^6.1.0", "autoprefixer": "^10.2.6", @@ -37,6 +37,7 @@ "next": "^11.0.0", "next-seo": "^4.26.0", "next-themes": "^0.0.14", + "node-fetch": "^2.6.1", "postcss": "^8.3.5", "postcss-nesting": "^8.0.1", "qs": "^6.7.0", diff --git a/yarn.lock b/yarn.lock index 3866458930..3939116985 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1012,14 +1012,16 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@spree/storefront-api-v2-sdk@^4.5.4": - version "4.5.4" - resolved "https://registry.yarnpkg.com/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-4.5.4.tgz#ad0a414139064df3c5ec7c85147597c8fa716f35" - integrity sha512-GlpCvOhOmM0X1Of8rQaBU/HuX9FKX0ZkO+EmTfucC//MDgwuxkUt9vHpv+JRPLdR8+88AA+CwwSVtLIGcLo9yQ== +"@spree/storefront-api-v2-sdk@^4.7.0": + version "4.7.0" + resolved "https://registry.yarnpkg.com/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-4.7.0.tgz#d6f3c4757de630ed75ce1b94f1919fc76d7a8a0b" + integrity sha512-n7GD1Bku/il7Hk9wviy7nngLeRMIDHAvyK3ZNWmNoCKWEQMt/1Y1lYeY1DQrnyWcbMTT5pf2MeZoVi7SVTd5uQ== dependencies: axios "^0.21.1" lodash "^4.17.21" qs "^6.10.1" + optionalDependencies: + node-fetch "^2.6.1" "@szmarczak/http-timer@^1.1.2": version "1.1.2" From 0540258522b6cf0b0af60c455f470a726d694516 Mon Sep 17 00:00:00 2001 From: Tomek Niezgoda <1410097+tniezg@users.noreply.github.com> Date: Mon, 23 Aug 2021 14:41:07 +0200 Subject: [PATCH 37/98] Order providers alphanumerically Co-authored-by: Damian Legawiec --- framework/commerce/config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/commerce/config.js b/framework/commerce/config.js index d629b45e3e..7eefd26d81 100644 --- a/framework/commerce/config.js +++ b/framework/commerce/config.js @@ -11,10 +11,10 @@ const PROVIDERS = [ 'bigcommerce', 'saleor', 'shopify', + 'spree', 'swell', 'vendure', - 'local', - 'spree', + 'local' ] function getProviderName() { From 57c176d9d7a32678760fe42677fa1d3f79a92152 Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 24 Aug 2021 15:10:10 +0200 Subject: [PATCH 38/98] Sort products by available_on when using the Trending sorting in useSearch --- framework/spree/product/use-search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 2945592be3..7411d73635 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -7,7 +7,7 @@ import type { GraphQLFetcherResult } from '@commerce/api' import { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' const nextToSpreeSortMap: { [key: string]: string } = { - 'trending-desc': 'updated_at', // FIXME: Spree has no "trending" filter. Using updated_at. + 'trending-desc': 'available_on', 'latest-desc': 'updated_at', 'price-asc': 'price', 'price-desc': '-price', From c3bd932d69fa1c3bd11d7d084bdab0494555d61c Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 24 Aug 2021 15:29:44 +0200 Subject: [PATCH 39/98] Change the default Spree port to 4000 and update README in sync with Spree Starter changes --- framework/spree/.env.template | 4 +-- framework/spree/README.md | 48 ++++++++++------------------------- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 7f584f8bf7..665fefba05 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -3,12 +3,12 @@ COMMERCE_PROVIDER=spree {# - public (available in the web browser) #} -NEXT_PUBLIC_SPREE_API_HOST=http://localhost:3000 +NEXT_PUBLIC_SPREE_API_HOST=http://localhost:4000 NEXT_PUBLIC_SPREE_DEFAULT_LOCALE=en-us NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart_token {# -- cookie expire in days #} NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE=7 -NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:3000 +NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:4000 NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID=1 NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID=27 diff --git a/framework/spree/README.md b/framework/spree/README.md index d83b61dcde..a623735f05 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -1,4 +1,4 @@ -# [Spree Commerce][1] Framework +# [Spree Commerce][1] Provider A preview integration of Spree Commerce within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart as a guest user. @@ -6,48 +6,28 @@ A preview integration of Spree Commerce within NextJS Commerce. It supports brow Start by following the [instructions for setting up NextJS Commerce][2]. -Next, setup Spree. The easiest way to run Spree locally is to follow the installation tutorial available at [the Spree Starter GitHub repository][3]. +Afterwards, configure NextJS Commerce to use the Spree Provider. Create a `.env.local` file in the root of the project. Its contents must be based on `framework/spree/.env.template`. -You have to adjust Spree Starter to allow `localhost` and [CORS][4] requests. Run `docker-compose run web bundle add rack-cors` and: +`NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID` rely on IDs generated by Spree and need to be changed from the defaults. Go to the Spree admin panel and create Categories and Brands taxonomies if they don't exist and copy their IDs into `.env.local` in NextJS Commerce. The values of the other environment variables can be copied from `framework/spree/.env.template` as is. -```ruby -# In config/application.rb add a configuration for CORS: +--- -module SpreeStarter - class Application < Rails::Application - config.middleware.insert_before 0, Rack::Cors do - allow do - origins '*' - resource '*', headers: :any, methods: :any - end - end - end -end +Setup Spree next. The easiest way to run Spree locally is to follow the installation tutorial available at [the Spree Starter GitHub repository][3]. -# In config/environments/development.rb add 'localhost': +You have to adjust Spree Starter to allow local [CORS][4] requests and have Spree run on port `4000` instead of the default port (NextJS Commerce and Spree both use port `3000` by default). To do this, add two environment variables inside `.env` in the Spree Starter project: -config.hosts << 'localhost' -``` - -By default, Spree Starter and NextJS Commerce both run on port `3000`. Avoid collisions by running NextJS Commerce on port `4000`: - -```json -// In package.json, modify two scripts: -{ - "scripts": { - "dev": "NODE_OPTIONS='--inspect' next dev -p 4000", - "start": "next start -p 4000", - ... - } - ... -} +```shell +DOCKER_HOST_WEB_PORT=4000 +ALLOWED_ORIGIN_HOSTS=* ``` -Third, supply NextJS Commerce with custom environment variables required by the Spree Framework. Create a `.env.local` file in the root of NJC with contents based on `framework/spree/.env.template`. +Also, add the following line inside `config/environments/development.rb` to allow HTTP requests to Spree from NextJS: -`NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID` rely on IDs generated by Spree. Go to the Spree admin panel and create Categories and Brands taxonomies if they don't exist and copy their IDs into `.env.local`. The values of the other environment variables can be copied from `framework/spree/.env.template` as is. +``` +config.hosts << 'localhost' +``` -Lastly, run `yarn dev` :tada: +Finally, run `yarn dev` :tada: [1]: https://spreecommerce.org/ [2]: https://github.com/vercel/commerce From d98a75503d02a8ca62cabb3efa3d21861779b958 Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 24 Aug 2021 15:45:32 +0200 Subject: [PATCH 40/98] Save primary variant's SKU when normalizing a product from Spree --- .../spree/api/operations/get-all-products.ts | 3 ++- framework/spree/api/operations/get-product.ts | 3 ++- framework/spree/product/use-search.tsx | 3 ++- framework/spree/utils/normalize-product.ts | 20 ++++++++++++++++++- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 5ad76cad14..3d03a62768 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -47,7 +47,8 @@ export default function getAllProductsOperation({ arguments: [ {}, { - include: 'variants,images,option_types,variants.option_values', + include: + 'primary_variant,variants,images,option_types,variants.option_values', per_page: first, }, ], diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index 57d43a8f05..a88ac479ef 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -54,7 +54,8 @@ export default function getProductOperation({ getProductVariables.slug, {}, { - include: 'variants,images,option_types,variants.option_values', + include: + 'primary_variant,variants,images,option_types,variants.option_values', }, ], } diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 7411d73635..d261b6add8 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -48,7 +48,8 @@ export const handler: SWRHook = { arguments: [ {}, { - include: 'variants,images,option_types,variants.option_values', + include: + 'primary_variant,variants,images,option_types,variants.option_values', per_page: 50, ...filter, ...sort, diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 83234e5664..d390e8d9a3 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -14,13 +14,30 @@ import { requireConfigValue } from '@framework/isomorphic-config' import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import expandOptions from './expand-options' import getMediaGallery from './get-media-gallery' -import { findIncludedOfType } from './find-json-api-documents' +import { findIncluded, findIncludedOfType } from './find-json-api-documents' import getProductPath from './get-product-path' +import MissingPrimaryVariantError from '@framework/errors/MissingPrimaryVariantError' const normalizeProduct = ( spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, spreeProduct: ProductAttr ): Product => { + const primaryVariantIdentifier = spreeProduct.relationships.primary_variant + .data as RelationType + const primaryVariant = findIncluded( + spreeSuccessResponse, + primaryVariantIdentifier.type, + primaryVariantIdentifier.id + ) + + if (primaryVariant === null) { + throw new MissingPrimaryVariantError( + `Couldn't find primary variant with id ${primaryVariantIdentifier.id}.` + ) + } + + const sku = primaryVariant.attributes.sku + const spreeImageRecords = findIncludedOfType( spreeSuccessResponse, spreeProduct, @@ -95,6 +112,7 @@ const normalizeProduct = ( price, slug, path, + sku, } } From 5e743f0e305d498ab9d5b35e7784749801b644d5 Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 24 Aug 2021 16:25:05 +0200 Subject: [PATCH 41/98] Create a new cart if Spree can't find the current using a token --- framework/spree/cart/use-cart.tsx | 57 ++++++++++++------- .../utils/create-customized-fetch-fetcher.ts | 4 ++ 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index a635ce0ffc..e51e01ffd4 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -8,6 +8,7 @@ import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import setCartToken from '@framework/utils/set-cart-token' +import { FetcherError } from '@commerce/utils/errors' export default useCart as UseCart @@ -35,29 +36,41 @@ export const handler: SWRHook = { spreeCartResponse = null } else { const spreeToken: IToken = { orderToken: cartToken } - const { - data: { data: spreeCartShowSuccessResponse }, - } = await fetch>({ - variables: { - methodPath: 'cart.show', - arguments: [ - spreeToken, - { - include: [ - 'line_items', - 'line_items.variant', - 'line_items.variant.product', - 'line_items.variant.product.images', - 'line_items.variant.images', - 'line_items.variant.option_values', - 'line_items.variant.product.option_types', - ].join(','), - }, - ], - }, - }) - spreeCartResponse = spreeCartShowSuccessResponse + try { + const { + data: { data: spreeCartShowSuccessResponse }, + } = await fetch>({ + variables: { + methodPath: 'cart.show', + arguments: [ + spreeToken, + { + include: [ + 'line_items', + 'line_items.variant', + 'line_items.variant.product', + 'line_items.variant.product.images', + 'line_items.variant.images', + 'line_items.variant.option_values', + 'line_items.variant.product.option_types', + ].join(','), + }, + ], + }, + }) + + spreeCartResponse = spreeCartShowSuccessResponse + } catch (fetchCartError) { + if ( + !(fetchCartError instanceof FetcherError) || + fetchCartError.status !== 404 + ) { + throw fetchCartError + } + + spreeCartResponse = null + } } if (!spreeCartResponse || spreeCartResponse?.data.attributes.completed_at) { diff --git a/framework/spree/utils/create-customized-fetch-fetcher.ts b/framework/spree/utils/create-customized-fetch-fetcher.ts index 4adb64a45d..eeeb10caea 100644 --- a/framework/spree/utils/create-customized-fetch-fetcher.ts +++ b/framework/spree/utils/create-customized-fetch-fetcher.ts @@ -60,6 +60,10 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( data: Object.setPrototypeOf({ data }, { response }), } } catch (error) { + if (error instanceof FetchError) { + throw error + } + throw new FetchError(null, request, null, error.message) } } catch (error) { From 894f18fe78bfbd0ed6452909aa01d6d62e93edd0 Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 24 Aug 2021 17:06:27 +0200 Subject: [PATCH 42/98] Add separator to README --- framework/spree/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/spree/README.md b/framework/spree/README.md index a623735f05..389c53de98 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -27,6 +27,8 @@ Also, add the following line inside `config/environments/development.rb` to allo config.hosts << 'localhost' ``` +--- + Finally, run `yarn dev` :tada: [1]: https://spreecommerce.org/ From 448100290d8e83a8a4ce6f38b9e2a02a2f5bb087 Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 24 Aug 2021 20:51:48 +0200 Subject: [PATCH 43/98] Add missing Error subclass --- framework/spree/errors/MissingPrimaryVariantError.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 framework/spree/errors/MissingPrimaryVariantError.ts diff --git a/framework/spree/errors/MissingPrimaryVariantError.ts b/framework/spree/errors/MissingPrimaryVariantError.ts new file mode 100644 index 0000000000..f9af41b035 --- /dev/null +++ b/framework/spree/errors/MissingPrimaryVariantError.ts @@ -0,0 +1 @@ +export default class MissingPrimaryVariantError extends Error {} From be0e246699d83f8f856a8f126d3972b309c7af27 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 25 Aug 2021 11:34:22 +0200 Subject: [PATCH 44/98] Allow placeholder images for products and line items without images --- framework/spree/.env.template | 2 ++ framework/spree/isomorphic-config.ts | 9 +++++++++ framework/spree/utils/normalize-cart.ts | 11 ++++++++++- framework/spree/utils/normalize-product.ts | 14 +++++++++++++- .../spree/utils/validate-placeholder-image-url.ts | 15 +++++++++++++++ 5 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 framework/spree/utils/validate-placeholder-image-url.ts diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 665fefba05..7d2b520266 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -14,3 +14,5 @@ NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID=1 NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID=27 NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS=false NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT=10 +NEXT_PUBLIC_SPREE_PRODUCT_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg +NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index 6255b1535d..b665fcd4fe 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -1,6 +1,7 @@ import forceIsomorphicConfigValues from './utils/force-isomorphic-config-values' import requireConfig from './utils/require-config' import validateCookieExpire from './utils/validate-cookie-expire' +import validatePlaceholderImageUrl from './utils/validate-placeholder-image-url' import validateProductsPrerenderCount from './utils/validate-products-prerender-count' const isomorphicConfig = { @@ -18,6 +19,12 @@ const isomorphicConfig = { lastUpdatedProductsPrerenderCount: validateProductsPrerenderCount( process.env.NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT ), + productPlaceholderImageUrl: validatePlaceholderImageUrl( + process.env.NEXT_PUBLIC_SPREE_PRODUCT_PLACEHOLDER_IMAGE_URL + ), + lineItemPlaceholderImageUrl: validatePlaceholderImageUrl( + process.env.NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL + ), } export default forceIsomorphicConfigValues( @@ -33,6 +40,8 @@ export default forceIsomorphicConfigValues( 'brandsTaxonomyId', 'showSingleVariantOptions', 'lastUpdatedProductsPrerenderCount', + 'productPlaceholderImageUrl', + 'lineItemPlaceholderImageUrl', ] ) diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index 47489193cd..3d0f8f9ebe 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -21,6 +21,11 @@ import type { OptionTypeAttr, VariantAttr, } from '@framework/types' +import type { Image } from '@commerce/types/common' + +const placeholderImage = requireConfigValue('lineItemPlaceholderImageUrl') as + | string + | false const isColorProductOption = (productOptionType: OptionTypeAttr) => { return productOptionType.attributes.presentation === 'Color' @@ -74,6 +79,10 @@ const normalizeVariant = ( lineItemImage = productImage } + const image: Image = + lineItemImage ?? + (placeholderImage === false ? undefined : { url: placeholderImage }) + return { id: spreeVariant.id, sku: spreeVariant.attributes.sku, @@ -81,7 +90,7 @@ const normalizeVariant = ( requiresShipping: true, price: parseFloat(spreeVariant.attributes.price), listPrice: parseFloat(spreeVariant.attributes.price), - image: lineItemImage, + image, isInStock: spreeVariant.attributes.in_stock, availableForSale: spreeVariant.attributes.purchasable, ...(spreeVariant.attributes.weight === '0.0' diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index d390e8d9a3..28325943e3 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -1,5 +1,6 @@ import type { Product, + ProductImage, ProductOption, ProductPrice, ProductVariant, @@ -18,6 +19,10 @@ import { findIncluded, findIncludedOfType } from './find-json-api-documents' import getProductPath from './get-product-path' import MissingPrimaryVariantError from '@framework/errors/MissingPrimaryVariantError' +const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as + | string + | false + const normalizeProduct = ( spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, spreeProduct: ProductAttr @@ -44,11 +49,18 @@ const normalizeProduct = ( 'images' ) - const images = getMediaGallery( + const productImages = getMediaGallery( spreeImageRecords, createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string) ) + const images: ProductImage[] = + productImages.length === 0 + ? placeholderImage === false + ? [] + : [{ url: placeholderImage }] + : productImages + const price: ProductPrice = { value: parseFloat(spreeProduct.attributes.price), currencyCode: spreeProduct.attributes.currency, diff --git a/framework/spree/utils/validate-placeholder-image-url.ts b/framework/spree/utils/validate-placeholder-image-url.ts new file mode 100644 index 0000000000..cce2e27da0 --- /dev/null +++ b/framework/spree/utils/validate-placeholder-image-url.ts @@ -0,0 +1,15 @@ +const validatePlaceholderImageUrl = ( + placeholderUrlOrFalse: unknown +): string | false => { + if (!placeholderUrlOrFalse || placeholderUrlOrFalse === 'false') { + return false + } + + if (typeof placeholderUrlOrFalse === 'string') { + return placeholderUrlOrFalse + } + + throw new TypeError('placeholderUrlOrFalse must be a string or falsy.') +} + +export default validatePlaceholderImageUrl From 0fa7d9755b44dcdc256edcb8cf5aaee8e3d45e87 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 25 Aug 2021 15:22:53 +0200 Subject: [PATCH 45/98] Add image --- framework/spree/README-assets/screenshots.png | Bin 0 -> 117099 bytes framework/spree/README.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 framework/spree/README-assets/screenshots.png diff --git a/framework/spree/README-assets/screenshots.png b/framework/spree/README-assets/screenshots.png new file mode 100644 index 0000000000000000000000000000000000000000..93c133e06d4038d15d9a42ff0667328338085684 GIT binary patch literal 117099 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

6OhHIR@}zzq{xTSt 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 Date: Wed, 25 Aug 2021 16:25:41 +0200 Subject: [PATCH 46/98] Reset tsconfig.json paths to originla values --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index b06e78b379..340929669f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,8 +23,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/spree"], - "@framework/*": ["framework/spree/*"] + "@framework": ["framework/local"], + "@framework/*": ["framework/local/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 2f9e90ed32f5d68cc99498e2ca697bb8d46f252d Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 26 Aug 2021 15:50:52 +0200 Subject: [PATCH 47/98] Search taxonomies by permalinks instead of IDs --- framework/spree/.env.template | 4 ++-- framework/spree/README.md | 2 +- framework/spree/api/operations/get-site-info.ts | 10 ++++------ framework/spree/isomorphic-config.ts | 10 ++++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 7d2b520266..a17269657e 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -10,8 +10,8 @@ NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart_token NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE=7 NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:4000 NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost -NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID=1 -NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID=27 +NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK=categories +NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK=brands NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS=false NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT=10 NEXT_PUBLIC_SPREE_PRODUCT_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg diff --git a/framework/spree/README.md b/framework/spree/README.md index 2a47b9e16c..305ef53f5c 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -10,7 +10,7 @@ Start by following the [instructions for setting up NextJS Commerce][2]. Afterwards, configure NextJS Commerce to use the Spree Provider. Create a `.env.local` file in the root of the project. Its contents must be based on `framework/spree/.env.template`. -`NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID` rely on IDs generated by Spree and need to be changed from the defaults. Go to the Spree admin panel and create Categories and Brands taxonomies if they don't exist and copy their IDs into `.env.local` in NextJS Commerce. The values of the other environment variables can be copied from `framework/spree/.env.template` as is. +`NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK` rely on taxonomies' permalinks in Spree. Go to the Spree admin panel and create Categories and Brands taxonomies if they don't exist and copy their permalinks into `.env.local` in NextJS Commerce. The values of the other environment variables can be copied from `framework/spree/.env.template` as is. --- diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index 456c624650..5b4ba4bf74 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -68,14 +68,12 @@ export default function getSiteInfoOperation({ userConfig ) - // Fetch first and level taxons - - const createVariables = (parentId: string): SpreeSdkVariables => ({ + const createVariables = (parentPermalink: string): SpreeSdkVariables => ({ methodPath: 'taxons.list', arguments: [ { filter: { - parent_id: parentId, + parent_permalink: parentPermalink, }, }, ], @@ -93,7 +91,7 @@ export default function getSiteInfoOperation({ SpreeSdkVariables >('__UNUSED__', { variables: createVariables( - requireConfigValue('categoriesTaxonomyId') as string + requireConfigValue('categoriesTaxonomyPermalink') as string ), }) @@ -106,7 +104,7 @@ export default function getSiteInfoOperation({ SpreeSdkVariables >('__UNUSED__', { variables: createVariables( - requireConfigValue('brandsTaxonomyId') as string + requireConfigValue('brandsTaxonomyPermalink') as string ), }) diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index b665fcd4fe..1fb5f3485f 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -12,8 +12,10 @@ const isomorphicConfig = { process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE ), imageHost: process.env.NEXT_PUBLIC_SPREE_IMAGE_HOST, - categoriesTaxonomyId: process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_ID, - brandsTaxonomyId: process.env.NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_ID, + categoriesTaxonomyPermalink: + process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK, + brandsTaxonomyPermalink: + process.env.NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK, showSingleVariantOptions: process.env.NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS === 'true', lastUpdatedProductsPrerenderCount: validateProductsPrerenderCount( @@ -36,8 +38,8 @@ export default forceIsomorphicConfigValues( 'cartCookieName', 'cartCookieExpire', 'imageHost', - 'categoriesTaxonomyId', - 'brandsTaxonomyId', + 'categoriesTaxonomyPermalink', + 'brandsTaxonomyPermalink', 'showSingleVariantOptions', 'lastUpdatedProductsPrerenderCount', 'productPlaceholderImageUrl', From 2953678b24af7947120328e5a6aad62d7de52c2c Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 26 Aug 2021 15:52:21 +0200 Subject: [PATCH 48/98] Upgrade Spree SDK to version 4.7.1 --- package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 599bf3b42b..2a6a053887 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@react-spring/web": "^9.2.1", - "@spree/storefront-api-v2-sdk": "^4.7.0", + "@spree/storefront-api-v2-sdk": "^4.7.1", "@types/qs": "^6.9.7", "@vercel/fetch": "^6.1.0", "autoprefixer": "^10.2.6", diff --git a/yarn.lock b/yarn.lock index 4ac754334b..a647414d74 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1066,7 +1066,7 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@spree/storefront-api-v2-sdk@^4.7.0": +"@spree/storefront-api-v2-sdk@^4.7.1": version "4.7.1" resolved "https://registry.yarnpkg.com/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-4.7.1.tgz#af83c0d0de9ed9d3dade89ef96b251161c789b64" integrity sha512-Z4vt1UwTf7rYfo6UIpfnsidyOsiw043FTwLfyVHDqFnXKXaTG4E6uV75gKrM7+d5SScTWj5qlq6YRAfwL/WacA== From 490f94b515abd3999b1d803e3be2d505733bd0de Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 30 Aug 2021 12:27:15 +0200 Subject: [PATCH 49/98] Remove references to @framework and use relative paths instead --- framework/spree/api/endpoints/checkout/index.ts | 2 +- .../spree/api/operations/get-all-product-paths.ts | 6 +++--- framework/spree/api/operations/get-all-products.ts | 2 +- framework/spree/api/operations/get-product.ts | 2 +- framework/spree/api/operations/get-site-info.ts | 2 +- framework/spree/api/utils/create-api-fetch.ts | 8 ++++---- framework/spree/cart/use-add-item.tsx | 4 ++-- framework/spree/cart/use-cart.tsx | 4 ++-- framework/spree/cart/use-remove-item.tsx | 4 ++-- framework/spree/cart/use-update-item.tsx | 4 ++-- framework/spree/utils/get-cart-token.ts | 2 +- framework/spree/utils/get-product-path.ts | 2 +- framework/spree/utils/normalize-cart.ts | 10 +++------- framework/spree/utils/normalize-product.ts | 4 ++-- framework/spree/utils/set-cart-token.ts | 2 +- 15 files changed, 27 insertions(+), 31 deletions(-) diff --git a/framework/spree/api/endpoints/checkout/index.ts b/framework/spree/api/endpoints/checkout/index.ts index 47b3157284..57831789c0 100644 --- a/framework/spree/api/endpoints/checkout/index.ts +++ b/framework/spree/api/endpoints/checkout/index.ts @@ -3,8 +3,8 @@ import type { CommerceAPI } from '@commerce/api' import type { GetAPISchema } from '@commerce/api' import checkoutEndpoint from '@commerce/api/endpoints/checkout' import type { CheckoutSchema } from '@commerce/types/checkout' -import type { SpreeApiProvider } from '@framework/api' import checkout from './checkout' +import { SpreeApiProvider } from '../..' export type CheckoutAPI = GetAPISchema< CommerceAPI, diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts index 6ea081bb3f..12ebd35f77 100644 --- a/framework/spree/api/operations/get-all-product-paths.ts +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -4,9 +4,9 @@ import type { } from '@commerce/api/operations' import type { Product } from '@commerce/types/product' import type { GetAllProductPathsOperation } from '@commerce/types/product' -import { requireConfigValue } from '@framework/isomorphic-config' -import type { IProductsSlugs, SpreeSdkVariables } from '@framework/types' -import getProductPath from '@framework/utils/get-product-path' +import { requireConfigValue } from '../../isomorphic-config' +import type { IProductsSlugs, SpreeSdkVariables } from '../../types' +import getProductPath from '../../utils/get-product-path' import type { SpreeApiConfig, SpreeApiProvider } from '..' export default function getAllProductPathsOperation({ diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 3d03a62768..8ccfe81c2c 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -7,7 +7,7 @@ import type { import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { SpreeApiConfig, SpreeApiProvider } from '../index' import type { SpreeSdkVariables } from 'framework/spree/types' -import normalizeProduct from '@framework/utils/normalize-product' +import normalizeProduct from '../../utils/normalize-product' export default function getAllProductsOperation({ commerce, diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index a88ac479ef..12419f8a84 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -7,7 +7,7 @@ import type { import type { IProduct } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { SpreeSdkVariables } from 'framework/spree/types' import MissingSlugVariableError from 'framework/spree/errors/MissingSlugVariableError' -import normalizeProduct from '@framework/utils/normalize-product' +import normalizeProduct from '../../utils/normalize-product' export default function getProductOperation({ commerce, diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index 5b4ba4bf74..1ac83d46be 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -7,7 +7,7 @@ import type { ITaxons, TaxonAttr, } from '@spree/storefront-api-v2-sdk/types/interfaces/Taxon' -import { requireConfigValue } from '@framework/isomorphic-config' +import { requireConfigValue } from '../../isomorphic-config' import type { SpreeSdkVariables } from 'framework/spree/types' import type { SpreeApiConfig, SpreeApiProvider } from '..' diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index c4babcc83a..e79537eabb 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -1,17 +1,17 @@ import { SpreeApiConfig } from '..' import { errors, makeClient } from '@spree/storefront-api-v2-sdk' -import { requireConfigValue } from '@framework/isomorphic-config' -import convertSpreeErrorToGraphQlError from '@framework/utils/convert-spree-error-to-graph-ql-error' +import { requireConfigValue } from '../../isomorphic-config' +import convertSpreeErrorToGraphQlError from '../../utils/convert-spree-error-to-graph-ql-error' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { JsonApiListResponse, JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' -import getSpreeSdkMethodFromEndpointPath from '@framework/utils/get-spree-sdk-method-from-endpoint-path' +import getSpreeSdkMethodFromEndpointPath from '../../utils/get-spree-sdk-method-from-endpoint-path' import { SpreeSdkVariables } from 'framework/spree/types' import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' -import createCustomizedFetchFetcher from '@framework/utils/create-customized-fetch-fetcher' +import createCustomizedFetchFetcher from '../../utils/create-customized-fetch-fetcher' import fetch, { Request } from 'node-fetch' const createApiFetch: ( diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index 8144baa065..7996e99a6b 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -4,12 +4,12 @@ import type { MutationHook } from '@commerce/utils/types' import { useCallback } from 'react' import useCart from './use-cart' import type { AddItemHook } from '@commerce/types/cart' -import normalizeCart from '@framework/utils/normalize-cart' +import normalizeCart from '../utils/normalize-cart' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { AddItem } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' -import getCartToken from '@framework/utils/get-cart-token' +import getCartToken from '../utils/get-cart-token' export default useAddItem as UseAddItem diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index e51e01ffd4..eb04d6334e 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -3,11 +3,11 @@ import type { SWRHook } from '@commerce/utils/types' import useCart from '@commerce/cart/use-cart' import type { UseCart } from '@commerce/cart/use-cart' import type { GetCartHook } from '@commerce/types/cart' -import normalizeCart from '@framework/utils/normalize-cart' +import normalizeCart from '../utils/normalize-cart' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' -import setCartToken from '@framework/utils/set-cart-token' +import setCartToken from '../utils/set-cart-token' import { FetcherError } from '@commerce/utils/errors' export default useCart as UseCart diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx index eaa4827278..627a693cc4 100644 --- a/framework/spree/cart/use-remove-item.tsx +++ b/framework/spree/cart/use-remove-item.tsx @@ -4,11 +4,11 @@ import type { UseRemoveItem } from '@commerce/cart/use-remove-item' import type { RemoveItemHook } from '@commerce/types/cart' import useCart from './use-cart' import { useCallback } from 'react' -import normalizeCart from '@framework/utils/normalize-cart' +import normalizeCart from '../utils/normalize-cart' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { GraphQLFetcherResult } from '@commerce/api' import type { IQuery } from '@spree/storefront-api-v2-sdk/types/interfaces/Query' -import getCartToken from '@framework/utils/get-cart-token' +import getCartToken from '../utils/get-cart-token' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' export default useRemoveItem as UseRemoveItem diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx index 0ae82b1043..ccbd9dc71a 100644 --- a/framework/spree/cart/use-update-item.tsx +++ b/framework/spree/cart/use-update-item.tsx @@ -6,10 +6,10 @@ import { useCallback } from 'react' import { ValidationError } from '@commerce/utils/errors' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { SetQuantity } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' -import getCartToken from '@framework/utils/get-cart-token' +import getCartToken from '../utils/get-cart-token' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' -import normalizeCart from '@framework/utils/normalize-cart' +import normalizeCart from '../utils/normalize-cart' import debounce from 'lodash.debounce' export default useUpdateItem as UseUpdateItem diff --git a/framework/spree/utils/get-cart-token.ts b/framework/spree/utils/get-cart-token.ts index 72fbe26183..b38337cb13 100644 --- a/framework/spree/utils/get-cart-token.ts +++ b/framework/spree/utils/get-cart-token.ts @@ -1,4 +1,4 @@ -import { requireConfigValue } from '@framework/isomorphic-config' +import { requireConfigValue } from '../isomorphic-config' import Cookies from 'js-cookie' const getCartToken = () => diff --git a/framework/spree/utils/get-product-path.ts b/framework/spree/utils/get-product-path.ts index ee5d0b0fec..b4c03096fa 100644 --- a/framework/spree/utils/get-product-path.ts +++ b/framework/spree/utils/get-product-path.ts @@ -1,4 +1,4 @@ -import type { ProductSlugAttr } from '@framework/types' +import type { ProductSlugAttr } from '../types' const getProductPath = (partialSpreeProduct: ProductSlugAttr): string => { return `/${partialSpreeProduct.attributes.slug}` diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index 3d0f8f9ebe..668fe70a03 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -4,8 +4,8 @@ import type { ProductVariant, SelectedOption, } from '@commerce/types/cart' -import MissingLineItemVariantError from '@framework/errors/MissingLineItemVariantError' -import { requireConfigValue } from '@framework/isomorphic-config' +import MissingLineItemVariantError from '../errors/MissingLineItemVariantError' +import { requireConfigValue } from '../isomorphic-config' import type { JsonApiListResponse, JsonApiSingleResponse, @@ -16,11 +16,7 @@ import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import getMediaGallery from './get-media-gallery' import { findIncluded, findIncludedOfType } from './find-json-api-documents' -import type { - LineItemAttr, - OptionTypeAttr, - VariantAttr, -} from '@framework/types' +import type { LineItemAttr, OptionTypeAttr, VariantAttr } from '../types' import type { Image } from '@commerce/types/common' const placeholderImage = requireConfigValue('lineItemPlaceholderImageUrl') as diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 28325943e3..d63b94d30f 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -11,13 +11,13 @@ import type { } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' -import { requireConfigValue } from '@framework/isomorphic-config' +import { requireConfigValue } from '../isomorphic-config' import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import expandOptions from './expand-options' import getMediaGallery from './get-media-gallery' import { findIncluded, findIncludedOfType } from './find-json-api-documents' import getProductPath from './get-product-path' -import MissingPrimaryVariantError from '@framework/errors/MissingPrimaryVariantError' +import MissingPrimaryVariantError from '../errors/MissingPrimaryVariantError' const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as | string diff --git a/framework/spree/utils/set-cart-token.ts b/framework/spree/utils/set-cart-token.ts index 6cffc1e79d..7463aea69a 100644 --- a/framework/spree/utils/set-cart-token.ts +++ b/framework/spree/utils/set-cart-token.ts @@ -1,4 +1,4 @@ -import { requireConfigValue } from '@framework/isomorphic-config' +import { requireConfigValue } from '../isomorphic-config' import Cookies from 'js-cookie' const setCartToken = (cartToken: string) => { From 320fc0645d2431d33925334976625782b7204405 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 30 Aug 2021 14:38:23 +0200 Subject: [PATCH 50/98] Generalize TypeScript and add typings to getPage --- .../spree/api/endpoints/checkout/index.ts | 5 ++- framework/spree/api/index.ts | 20 +++++++----- framework/spree/api/operations/get-page.ts | 31 ++++++++++++++++++- framework/spree/api/utils/create-api-fetch.ts | 12 ++++--- framework/spree/index.tsx | 7 ++--- 5 files changed, 54 insertions(+), 21 deletions(-) diff --git a/framework/spree/api/endpoints/checkout/index.ts b/framework/spree/api/endpoints/checkout/index.ts index 57831789c0..ffa1bb04f5 100644 --- a/framework/spree/api/endpoints/checkout/index.ts +++ b/framework/spree/api/endpoints/checkout/index.ts @@ -1,10 +1,9 @@ import { createEndpoint } from '@commerce/api' -import type { CommerceAPI } from '@commerce/api' -import type { GetAPISchema } from '@commerce/api' +import type { GetAPISchema, CommerceAPI } from '@commerce/api' import checkoutEndpoint from '@commerce/api/endpoints/checkout' import type { CheckoutSchema } from '@commerce/types/checkout' import checkout from './checkout' -import { SpreeApiProvider } from '../..' +import type { SpreeApiProvider } from '../..' export type CheckoutAPI = GetAPISchema< CommerceAPI, diff --git a/framework/spree/api/index.ts b/framework/spree/api/index.ts index 17ad8002fb..d9ef79e1af 100644 --- a/framework/spree/api/index.ts +++ b/framework/spree/api/index.ts @@ -1,4 +1,4 @@ -import type { APIProvider, CommerceAPIConfig } from '@commerce/api' +import type { CommerceAPI, CommerceAPIConfig } from '@commerce/api' import { getCommerceApi as commerceApi } from '@commerce/api' import createApiFetch from './utils/create-api-fetch' @@ -10,9 +10,7 @@ import getAllProductPaths from './operations/get-all-product-paths' import getAllProducts from './operations/get-all-products' import getProduct from './operations/get-product' -export interface SpreeApiConfig extends CommerceAPIConfig { - fetch: any // Using any type, because CommerceAPIConfig['fetch'] cannot be extended from Variables = any to SpreeSdkVariables -} +export interface SpreeApiConfig extends CommerceAPIConfig {} const config: SpreeApiConfig = { commerceUrl: '', @@ -33,9 +31,15 @@ const operations = { getProduct, } -export const provider: APIProvider = { config, operations } +export const provider = { config, operations } + +export type SpreeApiProvider = typeof provider -export type SpreeApiProvider = APIProvider +export type SpreeApi

= + CommerceAPI

-export const getCommerceApi = (customProvider: APIProvider = provider) => - commerceApi(customProvider) +export function getCommerceApi

( + customProvider: P = provider as any +): SpreeApi

{ + return commerceApi(customProvider) +} diff --git a/framework/spree/api/operations/get-page.ts b/framework/spree/api/operations/get-page.ts index b0cfdf58f1..2bfbb330af 100644 --- a/framework/spree/api/operations/get-page.ts +++ b/framework/spree/api/operations/get-page.ts @@ -1,3 +1,7 @@ +import type { OperationOptions } from '@commerce/api/operations' +import type { GetPageOperation } from '@commerce/types/page' +import type { SpreeApiConfig } from '..' + export type Page = any export type GetPageResult = { page?: Page } @@ -6,8 +10,33 @@ export type PageVariables = { } export default function getPageOperation() { - function getPage(): Promise { + async function getPage(opts: { + variables: T['variables'] + config?: Partial + preview?: boolean + }): Promise + + async function getPage( + opts: { + variables: T['variables'] + config?: Partial + preview?: boolean + } & OperationOptions + ): Promise + + async function getPage({ + url, + variables, + config, + preview, + }: { + url?: string + variables: T['variables'] + config?: Partial + preview?: boolean + }): Promise { return Promise.resolve({}) } + return getPage } diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index e79537eabb..22a043453b 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -8,17 +8,19 @@ import type { JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import getSpreeSdkMethodFromEndpointPath from '../../utils/get-spree-sdk-method-from-endpoint-path' -import { SpreeSdkVariables } from 'framework/spree/types' import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' import createCustomizedFetchFetcher from '../../utils/create-customized-fetch-fetcher' import fetch, { Request } from 'node-fetch' -const createApiFetch: ( +export type CreateApiFetch = ( getConfig: () => SpreeApiConfig -) => GraphQLFetcher, SpreeSdkVariables> = ( - _getConfig -) => { +) => GraphQLFetcher, any> + +// TODO: GraphQLFetcher, any> should be GraphQLFetcher, SpreeSdkVariables>. +// But CommerceAPIConfig['fetch'] cannot be extended from Variables = any to SpreeSdkVariables. + +const createApiFetch: CreateApiFetch = (_getConfig) => { const client = makeClient({ host: requireConfigValue('apiHost') as string, fetcherType: 'custom', diff --git a/framework/spree/index.tsx b/framework/spree/index.tsx index 6ca5bd1985..2dc370e432 100644 --- a/framework/spree/index.tsx +++ b/framework/spree/index.tsx @@ -13,9 +13,8 @@ import { requireConfigValue } from './isomorphic-config' export type SpreeProps = { children: ReactNode - provider: Provider - config: SpreeConfig -} & SpreeConfig + locale: string +} export const spreeCommerceConfigDefaults: CommerceConfig = { locale: requireConfigValue('defaultLocale') as string, @@ -26,7 +25,7 @@ export type SpreeConfig = CommerceConfig export function CommerceProvider({ children, ...restProps }: SpreeProps) { return ( - provider={provider} config={{ ...spreeCommerceConfigDefaults, ...restProps }} > From 92bb7bcf510574d3671bea73836f5930b568863e Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 30 Aug 2021 16:26:57 +0200 Subject: [PATCH 51/98] Update fetcher to avoid parsing non-JSON responses --- .../spree/utils/create-customized-fetch-fetcher.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/framework/spree/utils/create-customized-fetch-fetcher.ts b/framework/spree/utils/create-customized-fetch-fetcher.ts index eeeb10caea..241d8880b1 100644 --- a/framework/spree/utils/create-customized-fetch-fetcher.ts +++ b/framework/spree/utils/create-customized-fetch-fetcher.ts @@ -47,7 +47,18 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( try { const response: Response = await fetch(request) - const data = await response.json() + const responseContentType = response.headers.get('content-type') + let data + + if ( + !responseContentType || + (!responseContentType.includes('application/json') && + !responseContentType.includes('application/vnd.api+json')) + ) { + data = await response.text() + } else { + data = await response.json() + } if (!response.ok) { // Use the "traditional" approach and reject non 2xx responses. From e9397bcb7220cc4d17b48b0df8bc117e4a4a63a8 Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 31 Aug 2021 13:35:24 +0200 Subject: [PATCH 52/98] Use original product image by default instead of resized --- framework/spree/types/index.ts | 4 ++++ framework/spree/utils/create-get-absolute-image-url.ts | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 86f5bcd97d..c4d846a3e2 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -26,10 +26,14 @@ export interface ImageStyle { url: string width: string height: string + size: string } export interface SpreeProductImage extends JsonApiDocument { attributes: { + position: number + alt: string + original_url: string styles: ImageStyle[] } } diff --git a/framework/spree/utils/create-get-absolute-image-url.ts b/framework/spree/utils/create-get-absolute-image-url.ts index 7b75aa7801..09a31c54cd 100644 --- a/framework/spree/utils/create-get-absolute-image-url.ts +++ b/framework/spree/utils/create-get-absolute-image-url.ts @@ -2,13 +2,19 @@ import { SpreeProductImage } from '../types' import getImageUrl from './get-image-url' const createGetAbsoluteImageUrl = - (host: string) => + (host: string, useOriginalImageSize: boolean = true) => ( image: SpreeProductImage, minWidth: number, minHeight: number ): string | null => { - const url = getImageUrl(image, minWidth, minHeight) + let url + + if (useOriginalImageSize) { + url = image.attributes.original_url || null + } else { + url = getImageUrl(image, minWidth, minHeight) + } if (url === null) { return null From b9f8ed659d3bf566a198659c4877ac26ee0c95aa Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 1 Sep 2021 11:03:12 +0200 Subject: [PATCH 53/98] Link to an online demo of the Spree integration in the README --- framework/spree/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/spree/README.md b/framework/spree/README.md index 305ef53f5c..d65fd0bebe 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -2,7 +2,7 @@ ![Screenshots of Spree Commerce and NextJS Commerce][5] -A preview integration of Spree Commerce within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart as a guest user. +A preview integration of Spree Commerce within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart as a guest user. [You can see it in action online.][6] ## Installation @@ -38,3 +38,4 @@ Finally, run `yarn dev` :tada: [3]: https://github.com/spree/spree_starter [4]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS [5]: ./README-assets/screenshots.png +[6]: https://spree-x-nextjscommerce-demo.vercel.app/ From 60894d32c5cdd9e1417deee199ef221a2f27c66d Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 3 Sep 2021 09:52:59 +0200 Subject: [PATCH 54/98] Flatten fetcher responses --- .../api/operations/get-all-product-paths.ts | 14 ++++----- .../spree/api/operations/get-all-products.ts | 7 +++-- framework/spree/api/operations/get-product.ts | 7 +++-- .../spree/api/operations/get-site-info.ts | 16 +++------- framework/spree/api/utils/create-api-fetch.ts | 24 +++++++------- framework/spree/cart/use-add-item.tsx | 6 ++-- framework/spree/cart/use-cart.tsx | 12 +++---- framework/spree/cart/use-remove-item.tsx | 6 ++-- framework/spree/cart/use-update-item.tsx | 6 ++-- framework/spree/fetcher.ts | 31 ++++++++++--------- framework/spree/product/use-search.tsx | 6 ++-- framework/spree/types/index.ts | 10 +++++- .../utils/create-customized-fetch-fetcher.ts | 10 +++--- framework/spree/utils/normalize-cart.ts | 13 +++++--- framework/spree/utils/normalize-product.ts | 3 +- 15 files changed, 88 insertions(+), 83 deletions(-) diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts index 12ebd35f77..9e59284650 100644 --- a/framework/spree/api/operations/get-all-product-paths.ts +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -71,14 +71,12 @@ export default function getAllProductPathsOperation({ const config = commerce.getConfig(userConfig) const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. - const { - data: { data: spreeSuccessResponse }, - } = await apiFetch<{ data: IProductsSlugs }, SpreeSdkVariables>( - '__UNUSED__', - { - variables, - } - ) + const { data: spreeSuccessResponse } = await apiFetch< + IProductsSlugs, + SpreeSdkVariables + >('__UNUSED__', { + variables, + }) const normalizedProductsPaths: Pick[] = spreeSuccessResponse.data.map((spreeProduct) => ({ diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 8ccfe81c2c..833ff70f3c 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -57,9 +57,10 @@ export default function getAllProductsOperation({ const config = commerce.getConfig(userConfig) const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. - const { - data: { data: spreeSuccessResponse }, - } = await apiFetch<{ data: IProducts }, SpreeSdkVariables>('__UNUSED__', { + const { data: spreeSuccessResponse } = await apiFetch< + IProducts, + SpreeSdkVariables + >('__UNUSED__', { variables, }) diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index 12419f8a84..b665538340 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -63,9 +63,10 @@ export default function getProductOperation({ const config = commerce.getConfig(userConfig) const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. - const { - data: { data: spreeSuccessResponse }, - } = await apiFetch<{ data: IProduct }, SpreeSdkVariables>('__UNUSED__', { + const { data: spreeSuccessResponse } = await apiFetch< + IProduct, + SpreeSdkVariables + >('__UNUSED__', { variables, }) diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index 1ac83d46be..1e826ee56a 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -82,12 +82,8 @@ export default function getSiteInfoOperation({ const config = commerce.getConfig(userConfig) const { fetch: apiFetch } = config // TODO: Send config.locale to Spree. - const { - data: { data: spreeCategoriesSuccessResponse }, - } = await apiFetch< - { - data: ITaxons - }, + const { data: spreeCategoriesSuccessResponse } = await apiFetch< + ITaxons, SpreeSdkVariables >('__UNUSED__', { variables: createVariables( @@ -95,12 +91,8 @@ export default function getSiteInfoOperation({ ), }) - const { - data: { data: spreeBrandsSuccessResponse }, - } = await apiFetch< - { - data: ITaxons - }, + const { data: spreeBrandsSuccessResponse } = await apiFetch< + ITaxons, SpreeSdkVariables >('__UNUSED__', { variables: createVariables( diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index 22a043453b..0fe90cfe21 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -3,15 +3,14 @@ import { errors, makeClient } from '@spree/storefront-api-v2-sdk' import { requireConfigValue } from '../../isomorphic-config' import convertSpreeErrorToGraphQlError from '../../utils/convert-spree-error-to-graph-ql-error' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' -import type { - JsonApiListResponse, - JsonApiSingleResponse, -} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import getSpreeSdkMethodFromEndpointPath from '../../utils/get-spree-sdk-method-from-endpoint-path' import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' -import createCustomizedFetchFetcher from '../../utils/create-customized-fetch-fetcher' +import createCustomizedFetchFetcher, { + fetchResponseKey, +} from '../../utils/create-customized-fetch-fetcher' import fetch, { Request } from 'node-fetch' +import type { SpreeSdkResponseWithRawResponse } from '@framework/types' export type CreateApiFetch = ( getConfig: () => SpreeApiConfig @@ -52,20 +51,19 @@ const createApiFetch: CreateApiFetch = (_getConfig) => { ) } - const storeResponse: ResultResponse< - JsonApiSingleResponse | JsonApiListResponse - > = await getSpreeSdkMethodFromEndpointPath( - client, - variables.methodPath - )(...variables.arguments) + const storeResponse: ResultResponse = + await getSpreeSdkMethodFromEndpointPath( + client, + variables.methodPath + )(...variables.arguments) if (storeResponse.isSuccess()) { const data = storeResponse.success() - const rawFetchRespone = Object.getPrototypeOf(data).response + const rawFetchResponse = data[fetchResponseKey] return { data, - res: rawFetchRespone, + res: rawFetchResponse, } } diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index 7996e99a6b..b048028eca 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -46,9 +46,9 @@ export const handler: MutationHook = { ].join(','), } - const { - data: { data: spreeSuccessResponse }, - } = await fetch>({ + const { data: spreeSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ variables: { methodPath: 'cart.addItem', arguments: [token, addItemParameters], diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index eb04d6334e..8852970fae 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -38,9 +38,9 @@ export const handler: SWRHook = { const spreeToken: IToken = { orderToken: cartToken } try { - const { - data: { data: spreeCartShowSuccessResponse }, - } = await fetch>({ + const { data: spreeCartShowSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ variables: { methodPath: 'cart.show', arguments: [ @@ -74,9 +74,9 @@ export const handler: SWRHook = { } if (!spreeCartResponse || spreeCartResponse?.data.attributes.completed_at) { - const { - data: { data: spreeCartCreateSuccessResponse }, - } = await fetch>({ + const { data: spreeCartCreateSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ variables: { methodPath: 'cart.create', arguments: [], diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx index 627a693cc4..87d0b533df 100644 --- a/framework/spree/cart/use-remove-item.tsx +++ b/framework/spree/cart/use-remove-item.tsx @@ -42,9 +42,9 @@ export const handler: MutationHook = { ].join(','), } - const { - data: { data: spreeSuccessResponse }, - } = await fetch>({ + const { data: spreeSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ variables: { methodPath: 'cart.removeItem', arguments: [token, lineItemId, removeItemParameters], diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx index ccbd9dc71a..86c6e0e609 100644 --- a/framework/spree/cart/use-update-item.tsx +++ b/framework/spree/cart/use-update-item.tsx @@ -51,9 +51,9 @@ export const handler: MutationHook = { ].join(','), } - const { - data: { data: spreeSuccessResponse }, - } = await fetch>({ + const { data: spreeSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ variables: { methodPath: 'cart.setQuantity', arguments: [token, setQuantityParameters], diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 3b6766b385..18040da68e 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -2,17 +2,19 @@ import type { Fetcher } from '@commerce/utils/types' import convertSpreeErrorToGraphQlError from './utils/convert-spree-error-to-graph-ql-error' import { makeClient } from '@spree/storefront-api-v2-sdk' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' -import type { - JsonApiListResponse, - JsonApiSingleResponse, -} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import { errors } from '@spree/storefront-api-v2-sdk' import { requireConfigValue } from './isomorphic-config' import getSpreeSdkMethodFromEndpointPath from './utils/get-spree-sdk-method-from-endpoint-path' import SpreeSdkMethodFromEndpointPathError from './errors/SpreeSdkMethodFromEndpointPathError' -import type { SpreeSdkVariables } from './types' +import type { + SpreeSdkResponse, + SpreeSdkResponseWithRawResponse, + SpreeSdkVariables, +} from './types' import type { GraphQLFetcherResult } from '@commerce/api' -import createCustomizedFetchFetcher from './utils/create-customized-fetch-fetcher' +import createCustomizedFetchFetcher, { + fetchResponseKey, +} from './utils/create-customized-fetch-fetcher' const client = makeClient({ host: requireConfigValue('apiHost') as string, @@ -27,7 +29,7 @@ const client = makeClient({ }) const fetcher: Fetcher< - GraphQLFetcherResult, + GraphQLFetcherResult, SpreeSdkVariables > = async (requestOptions) => { const { url, method, variables, query } = requestOptions @@ -47,20 +49,19 @@ const fetcher: Fetcher< ) } - const storeResponse: ResultResponse< - JsonApiSingleResponse | JsonApiListResponse - > = await getSpreeSdkMethodFromEndpointPath( - client, - variables.methodPath - )(...variables.arguments) + const storeResponse: ResultResponse = + await getSpreeSdkMethodFromEndpointPath( + client, + variables.methodPath + )(...variables.arguments) if (storeResponse.isSuccess()) { const data = storeResponse.success() - const rawFetchRespone = Object.getPrototypeOf(data).response + const rawFetchResponse = data[fetchResponseKey] return { data, - res: rawFetchRespone, + res: rawFetchResponse, } } diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index d261b6add8..cd941f5b34 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -40,9 +40,9 @@ export const handler: SWRHook = { const sort = input.sort ? { sort: nextToSpreeSortMap[input.sort] } : {} - const { - data: { data: spreeSuccessResponse }, - } = await fetch>({ + const { data: spreeSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ variables: { methodPath: 'products.list', arguments: [ diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index c4d846a3e2..1cdaa2b210 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -1,9 +1,11 @@ +import type { fetchResponseKey } from '@framework/utils/create-customized-fetch-fetcher' import type { JsonApiDocument, JsonApiListResponse, JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' +import type { Response } from '@vercel/fetch' export type UnknownObjectValues = Record @@ -11,8 +13,14 @@ export type NonUndefined = T extends undefined ? never : T export type ValueOf = T[keyof T] +export type SpreeSdkResponse = JsonApiSingleResponse | JsonApiListResponse + +export type SpreeSdkResponseWithRawResponse = SpreeSdkResponse & { + [fetchResponseKey]: Response +} + export type SpreeSdkMethodReturnType = Promise< - ResultResponse + ResultResponse > export type SpreeSdkMethod = (...args: any[]) => SpreeSdkMethodReturnType diff --git a/framework/spree/utils/create-customized-fetch-fetcher.ts b/framework/spree/utils/create-customized-fetch-fetcher.ts index 241d8880b1..3dd9ba54a0 100644 --- a/framework/spree/utils/create-customized-fetch-fetcher.ts +++ b/framework/spree/utils/create-customized-fetch-fetcher.ts @@ -2,6 +2,8 @@ import * as qs from 'qs' import { errors } from '@spree/storefront-api-v2-sdk' import type { CreateCustomizedFetchFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/CreateCustomizedFetchFetcher' +export const fetchResponseKey = Symbol('fetch-response-key') + const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( fetcherOptions ) => { @@ -65,11 +67,9 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( throw new FetchError(response, request, data) } - return { - // Add response key to the prototype so it can be passed inside the GraphQLFetcherResult type. - // TODO: Search for a better solution than adding response to the prototype. - data: Object.setPrototypeOf({ data }, { response }), - } + data[fetchResponseKey] = response + + return { data } } catch (error) { if (error instanceof FetchError) { throw error diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index 668fe70a03..ce4098b63c 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -16,7 +16,12 @@ import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import getMediaGallery from './get-media-gallery' import { findIncluded, findIncludedOfType } from './find-json-api-documents' -import type { LineItemAttr, OptionTypeAttr, VariantAttr } from '../types' +import type { + LineItemAttr, + OptionTypeAttr, + SpreeSdkResponse, + VariantAttr, +} from '../types' import type { Image } from '@commerce/types/common' const placeholderImage = requireConfigValue('lineItemPlaceholderImageUrl') as @@ -28,7 +33,7 @@ const isColorProductOption = (productOptionType: OptionTypeAttr) => { } const normalizeVariant = ( - spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, + spreeSuccessResponse: SpreeSdkResponse, spreeVariant: VariantAttr ): ProductVariant => { const productIdentifier = spreeVariant.relationships.product @@ -102,7 +107,7 @@ const normalizeVariant = ( } const normalizeLineItem = ( - spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, + spreeSuccessResponse: SpreeSdkResponse, spreeLineItem: LineItemAttr ): LineItem => { const variantIdentifier = spreeLineItem.relationships.variant @@ -183,7 +188,7 @@ const normalizeLineItem = ( } const normalizeCart = ( - spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, + spreeSuccessResponse: SpreeSdkResponse, spreeCart: OrderAttr ): Cart => { const lineItems = findIncludedOfType( diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index d63b94d30f..5fa466c51d 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -18,13 +18,14 @@ import getMediaGallery from './get-media-gallery' import { findIncluded, findIncludedOfType } from './find-json-api-documents' import getProductPath from './get-product-path' import MissingPrimaryVariantError from '../errors/MissingPrimaryVariantError' +import type { SpreeSdkResponse } from '@framework/types' const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as | string | false const normalizeProduct = ( - spreeSuccessResponse: JsonApiSingleResponse | JsonApiListResponse, + spreeSuccessResponse: SpreeSdkResponse, spreeProduct: ProductAttr ): Product => { const primaryVariantIdentifier = spreeProduct.relationships.primary_variant From c0f1a66afe34df90dfe4afecb4dd1cd9dc5e6003 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 9 Sep 2021 15:57:44 +0200 Subject: [PATCH 55/98] Include Spree in the list of supported ecommerce backends in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2eeea375b2..6568e4e74a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/) ## Integrations -Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Saleor and Vendure. We plan to support all major ecommerce backends. +Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Saleor, Spree and Vendure. We plan to support all major ecommerce backends. ## Considerations From 632c2c63a5cb9acfab646015201a838129b22504 Mon Sep 17 00:00:00 2001 From: Damian Legawiec Date: Mon, 13 Sep 2021 12:30:19 +0200 Subject: [PATCH 56/98] Update README.md --- framework/spree/README.md | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/framework/spree/README.md b/framework/spree/README.md index d65fd0bebe..1aab7e3b39 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -2,32 +2,26 @@ ![Screenshots of Spree Commerce and NextJS Commerce][5] -A preview integration of Spree Commerce within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart as a guest user. [You can see it in action online.][6] +An integration of [Spree Commerce](https://spreecommerce.org/) within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart. -## Installation - -Start by following the [instructions for setting up NextJS Commerce][2]. - -Afterwards, configure NextJS Commerce to use the Spree Provider. Create a `.env.local` file in the root of the project. Its contents must be based on `framework/spree/.env.template`. +**Demo**: [https://spree-x-nextjscommerce-demo.vercel.app/][6] -`NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK` rely on taxonomies' permalinks in Spree. Go to the Spree admin panel and create Categories and Brands taxonomies if they don't exist and copy their permalinks into `.env.local` in NextJS Commerce. The values of the other environment variables can be copied from `framework/spree/.env.template` as is. - ---- +## Installation -Setup Spree next. The easiest way to run Spree locally is to follow the installation tutorial available at [the Spree Starter GitHub repository][3]. +1. Setup Spree [following Getting Started guide](https://dev-docs.spreecommerce.org/getting-started/installation) -You have to adjust Spree Starter to allow local [CORS][4] requests and have Spree run on port `4000` instead of the default port (NextJS Commerce and Spree both use port `3000` by default). To do this, add two environment variables inside `.env` in the Spree Starter project: +1. Setup Nextjs Commerce - [instructions for setting up NextJS Commerce][2]. -```shell -DOCKER_HOST_WEB_PORT=4000 -ALLOWED_ORIGIN_HOSTS=* -``` +2. Copy the `.env.template` file in this directory to `.env.local` in the main directory -Also, add the following line inside `config/environments/development.rb` to allow HTTP requests to Spree from NextJS: + ```bash + cp framework/spree/.env.template .env.local + ``` -``` -config.hosts << 'localhost' -``` +3. Set `NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK` Env variables + + * They rely on [taxonomies'](https://dev-docs.spreecommerce.org/internals/products#taxons-and-taxonomies) permalinks in Spree. + * Go to the Spree admin panel and create `Categories` and `Brands` taxonomies if they don't exist and copy their permalinks into `.env.local` in NextJS Commerce. --- From 10408e36e63708617f44d5c951a4d850e9812950 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 13 Sep 2021 13:53:05 +0200 Subject: [PATCH 57/98] Format Spree's README --- framework/spree/README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/framework/spree/README.md b/framework/spree/README.md index 1aab7e3b39..b1c7711818 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -2,30 +2,28 @@ ![Screenshots of Spree Commerce and NextJS Commerce][5] -An integration of [Spree Commerce](https://spreecommerce.org/) within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart. +An integration of [Spree Commerce](https://spreecommerce.org/) within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart. **Demo**: [https://spree-x-nextjscommerce-demo.vercel.app/][6] ## Installation -1. Setup Spree [following Getting Started guide](https://dev-docs.spreecommerce.org/getting-started/installation) +1. Setup Spree - [follow the Getting Started guide](https://dev-docs.spreecommerce.org/getting-started/installation). 1. Setup Nextjs Commerce - [instructions for setting up NextJS Commerce][2]. -2. Copy the `.env.template` file in this directory to `.env.local` in the main directory +1. Copy the `.env.template` file in this directory (`/framework/spree`) to `.env.local` in the main directory ```bash cp framework/spree/.env.template .env.local ``` -3. Set `NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK` Env variables - - * They rely on [taxonomies'](https://dev-docs.spreecommerce.org/internals/products#taxons-and-taxonomies) permalinks in Spree. - * Go to the Spree admin panel and create `Categories` and `Brands` taxonomies if they don't exist and copy their permalinks into `.env.local` in NextJS Commerce. +1. Set `NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK` and `NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK` environment variables: ---- + - They rely on [taxonomies'](https://dev-docs.spreecommerce.org/internals/products#taxons-and-taxonomies) permalinks in Spree. + - Go to the Spree admin panel and create `Categories` and `Brands` taxonomies if they don't exist and copy their permalinks into `.env.local` in NextJS Commerce. -Finally, run `yarn dev` :tada: +1. Finally, run `yarn dev` :tada: [1]: https://spreecommerce.org/ [2]: https://github.com/vercel/commerce From 964f04a3c9927758e743f767589bc3ad4a46eab0 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 13 Sep 2021 14:04:42 +0200 Subject: [PATCH 58/98] Add link to the Spree demo site in the main README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6568e4e74a..0e2e3f52cc 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/) - BigCommerce Demo: https://bigcommerce.vercel.store/ - Vendure Demo: https://vendure.vercel.store - Saleor Demo: https://saleor.vercel.store/ +- Spree Demo: https://spree-x-nextjscommerce-demo.vercel.app/ ## Features @@ -27,7 +28,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/) ## Integrations -Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Saleor, Spree and Vendure. We plan to support all major ecommerce backends. +Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Saleor, Vendure and Spree. We plan to support all major ecommerce backends. ## Considerations From 75165df24e84a5aac82aeba3662e8eb6e8bf0ff6 Mon Sep 17 00:00:00 2001 From: Damian Legawiec Date: Mon, 13 Sep 2021 14:11:11 +0200 Subject: [PATCH 59/98] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e2e3f52cc..0f93c23b93 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/) - BigCommerce Demo: https://bigcommerce.vercel.store/ - Vendure Demo: https://vendure.vercel.store - Saleor Demo: https://saleor.vercel.store/ -- Spree Demo: https://spree-x-nextjscommerce-demo.vercel.app/ +- Spree Demo: https://spree.vercel.app/ ## Features From 6c3c94b9c097dbdbcd611ac7a79f8744421e5070 Mon Sep 17 00:00:00 2001 From: Damian Legawiec Date: Mon, 13 Sep 2021 14:11:56 +0200 Subject: [PATCH 60/98] Update README.md --- framework/spree/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/spree/README.md b/framework/spree/README.md index b1c7711818..b80e4d0960 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -4,7 +4,7 @@ An integration of [Spree Commerce](https://spreecommerce.org/) within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart. -**Demo**: [https://spree-x-nextjscommerce-demo.vercel.app/][6] +**Demo**: [https://spree.vercel.app/][6] ## Installation @@ -30,4 +30,4 @@ An integration of [Spree Commerce](https://spreecommerce.org/) within NextJS Com [3]: https://github.com/spree/spree_starter [4]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS [5]: ./README-assets/screenshots.png -[6]: https://spree-x-nextjscommerce-demo.vercel.app/ +[6]: https://spree.vercel.app/ From 5b241d036bac0eec6bc898cfd3376ebd97cdebee Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 13 Sep 2021 15:04:32 +0200 Subject: [PATCH 61/98] Allow setting a taxon id for getAllProducts --- framework/spree/.env.template | 1 + framework/spree/api/operations/get-all-products.ts | 10 ++++++++++ framework/spree/isomorphic-config.ts | 5 +++++ .../utils/validate-all-products-taxonomy-id.ts | 13 +++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 framework/spree/utils/validate-all-products-taxonomy-id.ts diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 4f73b3898a..5c4f05184d 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -12,6 +12,7 @@ NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:4000 NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK=categories NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK=brands +NEXT_PUBLIC_SPREE_ALL_PRODUCTS_TAXONOMY_ID=false NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS=false NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT=10 NEXT_PUBLIC_SPREE_PRODUCT_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 833ff70f3c..ca6023dc31 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -8,6 +8,7 @@ import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Pr import type { SpreeApiConfig, SpreeApiProvider } from '../index' import type { SpreeSdkVariables } from 'framework/spree/types' import normalizeProduct from '../../utils/normalize-product' +import { requireConfigValue } from '@framework/isomorphic-config' export default function getAllProductsOperation({ commerce, @@ -41,7 +42,15 @@ export default function getAllProductsOperation({ userConfig ) + const defaultProductsTaxonomyId = requireConfigValue( + 'allProductsTaxonomyId' + ) as string | false + const first = getAllProductsVariables.first + const filter = !defaultProductsTaxonomyId + ? {} + : { filter: { taxons: defaultProductsTaxonomyId } } + const variables: SpreeSdkVariables = { methodPath: 'products.list', arguments: [ @@ -50,6 +59,7 @@ export default function getAllProductsOperation({ include: 'primary_variant,variants,images,option_types,variants.option_values', per_page: first, + ...filter, }, ], } diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index 1fb5f3485f..ed1bb4a552 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -1,5 +1,6 @@ import forceIsomorphicConfigValues from './utils/force-isomorphic-config-values' import requireConfig from './utils/require-config' +import validateAllProductsTaxonomyId from './utils/validate-all-products-taxonomy-id' import validateCookieExpire from './utils/validate-cookie-expire' import validatePlaceholderImageUrl from './utils/validate-placeholder-image-url' import validateProductsPrerenderCount from './utils/validate-products-prerender-count' @@ -16,6 +17,9 @@ const isomorphicConfig = { process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK, brandsTaxonomyPermalink: process.env.NEXT_PUBLIC_SPREE_BRANDS_TAXONOMY_PERMALINK, + allProductsTaxonomyId: validateAllProductsTaxonomyId( + process.env.NEXT_PUBLIC_SPREE_ALL_PRODUCTS_TAXONOMY_ID + ), showSingleVariantOptions: process.env.NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS === 'true', lastUpdatedProductsPrerenderCount: validateProductsPrerenderCount( @@ -40,6 +44,7 @@ export default forceIsomorphicConfigValues( 'imageHost', 'categoriesTaxonomyPermalink', 'brandsTaxonomyPermalink', + 'allProductsTaxonomyId', 'showSingleVariantOptions', 'lastUpdatedProductsPrerenderCount', 'productPlaceholderImageUrl', diff --git a/framework/spree/utils/validate-all-products-taxonomy-id.ts b/framework/spree/utils/validate-all-products-taxonomy-id.ts new file mode 100644 index 0000000000..5eaaa0b4bc --- /dev/null +++ b/framework/spree/utils/validate-all-products-taxonomy-id.ts @@ -0,0 +1,13 @@ +const validateAllProductsTaxonomyId = (taxonomyId: unknown): string | false => { + if (!taxonomyId || taxonomyId === 'false') { + return false + } + + if (typeof taxonomyId === 'string') { + return taxonomyId + } + + throw new TypeError('taxonomyId must be a string or falsy.') +} + +export default validateAllProductsTaxonomyId From c8ee3ef2931f8ab601b4f19ff0ed0ee6a05d2623 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 13 Sep 2021 16:06:15 +0200 Subject: [PATCH 62/98] Use Spree SDK's JSON:API helpers --- framework/spree/utils/expand-options.ts | 8 +- .../spree/utils/find-json-api-documents.ts | 46 ---------- framework/spree/utils/normalize-cart.ts | 91 +++++++++---------- framework/spree/utils/normalize-product.ts | 24 ++--- package.json | 2 +- yarn.lock | 28 +++--- 6 files changed, 70 insertions(+), 129 deletions(-) delete mode 100644 framework/spree/utils/find-json-api-documents.ts diff --git a/framework/spree/utils/expand-options.ts b/framework/spree/utils/expand-options.ts index f9381a7381..3b7b31b7be 100644 --- a/framework/spree/utils/expand-options.ts +++ b/framework/spree/utils/expand-options.ts @@ -6,9 +6,10 @@ import type { JsonApiDocument, JsonApiResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +import { jsonApi } from '@spree/storefront-api-v2-sdk' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import SpreeResponseContentError from '../errors/SpreeResponseContentError' -import { findIncluded } from './find-json-api-documents' +import type { OptionTypeAttr } from '@framework/types' const isColorProductOption = (productOption: ProductOption) => { return productOption.displayName === 'Color' @@ -29,10 +30,9 @@ const expandOptions = ( let option: ProductOption if (existingOptionIndex === -1) { - const spreeOptionType = findIncluded( + const spreeOptionType = jsonApi.findDocument( spreeSuccessResponse, - spreeOptionTypeIdentifier.type, - spreeOptionTypeIdentifier.id + spreeOptionTypeIdentifier ) if (!spreeOptionType) { diff --git a/framework/spree/utils/find-json-api-documents.ts b/framework/spree/utils/find-json-api-documents.ts deleted file mode 100644 index 6dc30f89f8..0000000000 --- a/framework/spree/utils/find-json-api-documents.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Based on https://github.com/spark-solutions/spree2vuestorefront - -import type { - JsonApiResponse, - JsonApiDocument, -} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' - -export const findIncluded = ( - response: JsonApiResponse, - objectType: string, - objectId: string -): T | null => { - if (!response.included) { - return null - } - - return ( - (response.included.find( - (includedObject) => - includedObject.type === objectType && includedObject.id === objectId - ) as T) || null - ) -} - -export const findIncludedOfType = ( - response: JsonApiResponse, - singlePrimaryRecord: JsonApiDocument, - objectRelationshipType: string -): T[] => { - if (!response.included) { - return [] - } - - const typeRelationships = - singlePrimaryRecord.relationships[objectRelationshipType] - - if (!typeRelationships) { - return [] - } - - return typeRelationships.data - .map((typeObject: JsonApiDocument) => - findIncluded(response, typeObject.type, typeObject.id) - ) - .filter((typeRecord: JsonApiDocument | null) => !!typeRecord) -} diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index ce4098b63c..6aa6197abc 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -6,23 +6,19 @@ import type { } from '@commerce/types/cart' import MissingLineItemVariantError from '../errors/MissingLineItemVariantError' import { requireConfigValue } from '../isomorphic-config' -import type { - JsonApiListResponse, - JsonApiSingleResponse, -} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { OrderAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' -import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import getMediaGallery from './get-media-gallery' -import { findIncluded, findIncludedOfType } from './find-json-api-documents' import type { LineItemAttr, OptionTypeAttr, + SpreeProductImage, SpreeSdkResponse, VariantAttr, } from '../types' import type { Image } from '@commerce/types/common' +import { jsonApi } from '@spree/storefront-api-v2-sdk' const placeholderImage = requireConfigValue('lineItemPlaceholderImageUrl') as | string @@ -36,25 +32,24 @@ const normalizeVariant = ( spreeSuccessResponse: SpreeSdkResponse, spreeVariant: VariantAttr ): ProductVariant => { - const productIdentifier = spreeVariant.relationships.product - .data as RelationType - const spreeProduct = findIncluded( + const spreeProduct = jsonApi.findSingleRelationshipDocument( spreeSuccessResponse, - productIdentifier.type, - productIdentifier.id + spreeVariant, + 'product' ) if (spreeProduct === null) { throw new MissingLineItemVariantError( - `Couldn't find product with id ${productIdentifier.id}.` + `Couldn't find product for variant with id ${spreeVariant.id}.` ) } - const spreeVariantImageRecords = findIncludedOfType( - spreeSuccessResponse, - spreeVariant, - 'images' - ) + const spreeVariantImageRecords = + jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeVariant, + 'images' + ) let lineItemImage @@ -66,11 +61,12 @@ const normalizeVariant = ( if (variantImage) { lineItemImage = variantImage } else { - const spreeProductImageRecords = findIncludedOfType( - spreeSuccessResponse, - spreeProduct, - 'images' - ) + const spreeProductImageRecords = + jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeProduct, + 'images' + ) const productImage = getMediaGallery( spreeProductImageRecords, @@ -110,36 +106,33 @@ const normalizeLineItem = ( spreeSuccessResponse: SpreeSdkResponse, spreeLineItem: LineItemAttr ): LineItem => { - const variantIdentifier = spreeLineItem.relationships.variant - .data as RelationType - const variant = findIncluded( + const variant = jsonApi.findSingleRelationshipDocument( spreeSuccessResponse, - variantIdentifier.type, - variantIdentifier.id + spreeLineItem, + 'variant' ) if (variant === null) { throw new MissingLineItemVariantError( - `Couldn't find variant with id ${variantIdentifier.id}.` + `Couldn't find variant for line item with id ${spreeLineItem.id}.` ) } - const productIdentifier = variant.relationships.product.data as RelationType - const product = findIncluded( + const product = jsonApi.findSingleRelationshipDocument( spreeSuccessResponse, - productIdentifier.type, - productIdentifier.id + variant, + 'product' ) if (product === null) { throw new MissingLineItemVariantError( - `Couldn't find product with id ${productIdentifier.id}.` + `Couldn't find product for variant with id ${variant.id}.` ) } const path = `/${product.attributes.slug}` - const spreeOptionValues = findIncludedOfType( + const spreeOptionValues = jsonApi.findRelationshipDocuments( spreeSuccessResponse, variant, 'option_values' @@ -147,18 +140,16 @@ const normalizeLineItem = ( const options: SelectedOption[] = spreeOptionValues.map( (spreeOptionValue) => { - const spreeOptionTypeIdentifier = spreeOptionValue.relationships - .option_type.data as RelationType - - const spreeOptionType = findIncluded( - spreeSuccessResponse, - spreeOptionTypeIdentifier.type, - spreeOptionTypeIdentifier.id - ) + const spreeOptionType = + jsonApi.findSingleRelationshipDocument( + spreeSuccessResponse, + spreeOptionValue, + 'option_type' + ) if (spreeOptionType === null) { throw new MissingLineItemVariantError( - `Couldn't find option type with id ${spreeOptionTypeIdentifier.id}.` + `Couldn't find option type of option value with id ${spreeOptionValue.id}.` ) } @@ -177,7 +168,7 @@ const normalizeLineItem = ( return { id: spreeLineItem.id, variantId: variant.id, - productId: productIdentifier.id, + productId: product.id, name: spreeLineItem.attributes.name, quantity: spreeLineItem.attributes.quantity, discounts: [], // TODO: Implement when the template starts displaying them. @@ -191,11 +182,13 @@ const normalizeCart = ( spreeSuccessResponse: SpreeSdkResponse, spreeCart: OrderAttr ): Cart => { - const lineItems = findIncludedOfType( - spreeSuccessResponse, - spreeCart, - 'line_items' - ).map((lineItem) => normalizeLineItem(spreeSuccessResponse, lineItem)) + const lineItems = jsonApi + .findRelationshipDocuments( + spreeSuccessResponse, + spreeCart, + 'line_items' + ) + .map((lineItem) => normalizeLineItem(spreeSuccessResponse, lineItem)) return { id: spreeCart.id, diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 5fa466c51d..6ef53f8a5c 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -5,20 +5,16 @@ import type { ProductPrice, ProductVariant, } from '@commerce/types/product' -import type { - JsonApiListResponse, - JsonApiSingleResponse, -} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import { requireConfigValue } from '../isomorphic-config' import createGetAbsoluteImageUrl from './create-get-absolute-image-url' import expandOptions from './expand-options' import getMediaGallery from './get-media-gallery' -import { findIncluded, findIncludedOfType } from './find-json-api-documents' import getProductPath from './get-product-path' import MissingPrimaryVariantError from '../errors/MissingPrimaryVariantError' -import type { SpreeSdkResponse } from '@framework/types' +import type { SpreeSdkResponse, VariantAttr } from '@framework/types' +import { jsonApi } from '@spree/storefront-api-v2-sdk' const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as | string @@ -28,23 +24,21 @@ const normalizeProduct = ( spreeSuccessResponse: SpreeSdkResponse, spreeProduct: ProductAttr ): Product => { - const primaryVariantIdentifier = spreeProduct.relationships.primary_variant - .data as RelationType - const primaryVariant = findIncluded( + const primaryVariant = jsonApi.findSingleRelationshipDocument( spreeSuccessResponse, - primaryVariantIdentifier.type, - primaryVariantIdentifier.id + spreeProduct, + 'primary_variant' ) if (primaryVariant === null) { throw new MissingPrimaryVariantError( - `Couldn't find primary variant with id ${primaryVariantIdentifier.id}.` + `Couldn't find primary variant for product with id ${spreeProduct.id}.` ) } const sku = primaryVariant.attributes.sku - const spreeImageRecords = findIncludedOfType( + const spreeImageRecords = jsonApi.findRelationshipDocuments( spreeSuccessResponse, spreeProduct, 'images' @@ -77,7 +71,7 @@ const normalizeProduct = ( let variants: ProductVariant[] let options: ProductOption[] = [] - const spreeVariantRecords = findIncludedOfType( + const spreeVariantRecords = jsonApi.findRelationshipDocuments( spreeSuccessResponse, spreeProduct, 'variants' @@ -87,7 +81,7 @@ const normalizeProduct = ( let variantOptions: ProductOption[] = [] if (showOptions) { - const spreeOptionValues = findIncludedOfType( + const spreeOptionValues = jsonApi.findRelationshipDocuments( spreeSuccessResponse, spreeVariantRecord, 'option_values' diff --git a/package.json b/package.json index 2a6a053887..466371f537 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@react-spring/web": "^9.2.1", - "@spree/storefront-api-v2-sdk": "^4.7.1", + "@spree/storefront-api-v2-sdk": "^4.9.0", "@types/qs": "^6.9.7", "@vercel/fetch": "^6.1.0", "autoprefixer": "^10.2.6", diff --git a/yarn.lock b/yarn.lock index a647414d74..ee68089571 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1066,12 +1066,12 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@spree/storefront-api-v2-sdk@^4.7.1": - version "4.7.1" - resolved "https://registry.yarnpkg.com/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-4.7.1.tgz#af83c0d0de9ed9d3dade89ef96b251161c789b64" - integrity sha512-Z4vt1UwTf7rYfo6UIpfnsidyOsiw043FTwLfyVHDqFnXKXaTG4E6uV75gKrM7+d5SScTWj5qlq6YRAfwL/WacA== +"@spree/storefront-api-v2-sdk@^4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-4.9.0.tgz#57aa40b5b880e039c6810c4be17043b6b6ec2a2a" + integrity sha512-2PrwXx9VKnrB3G6lNbLJlHvwuQn52IxShys3wN413dHQ742w8uOh5yPWvnG/EifJTZs2ejB41Yb0XhqmPHAeXA== dependencies: - axios "^0.21.1" + axios "^0.21.4" lodash "^4.17.21" qs "^6.10.1" optionalDependencies: @@ -1636,12 +1636,12 @@ axe-core@^4.0.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.2.tgz#fcf8777b82c62cfc69c7e9f32c0d2226287680e7" integrity sha512-5LMaDRWm8ZFPAEdzTYmgjjEdj1YnQcpfrVajO/sn/LhbpGp0Y0H64c2hLZI1gRMxfA+w1S71Uc/nHaOXgcCvGg== -axios@^0.21.1: - version "0.21.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== +axios@^0.21.4: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== dependencies: - follow-redirects "^1.10.0" + follow-redirects "^1.14.0" axobject-query@^2.2.0: version "2.2.0" @@ -3197,10 +3197,10 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== -follow-redirects@^1.10.0: - version "1.14.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.2.tgz#cecb825047c00f5e66b142f90fed4f515dec789b" - integrity sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA== +follow-redirects@^1.14.0: + version "1.14.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e" + integrity sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw== foreach@^2.0.5: version "2.0.5" From fed88c8485db0e95b93045677a5b037aa4f2be77 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 13 Sep 2021 16:06:49 +0200 Subject: [PATCH 63/98] Sort getAllProducts by -updated_at when using a taxonomy --- framework/spree/api/operations/get-all-products.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index ca6023dc31..40407cd40b 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -49,7 +49,7 @@ export default function getAllProductsOperation({ const first = getAllProductsVariables.first const filter = !defaultProductsTaxonomyId ? {} - : { filter: { taxons: defaultProductsTaxonomyId } } + : { filter: { taxons: defaultProductsTaxonomyId }, sort: '-updated_at' } const variables: SpreeSdkVariables = { methodPath: 'products.list', From 76596fd7656f91f6ba16154cd3ca3c5c4eaf3cce Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 13 Sep 2021 18:01:29 +0200 Subject: [PATCH 64/98] Remove slash '/' from line item's paths --- framework/spree/utils/normalize-cart.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalize-cart.ts index 6aa6197abc..a0d7dd622b 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalize-cart.ts @@ -130,7 +130,8 @@ const normalizeLineItem = ( ) } - const path = `/${product.attributes.slug}` + // CartItem.tsx expects path without a '/' prefix unlike pages/product/[slug].tsx and others. + const path = `${product.attributes.slug}` const spreeOptionValues = jsonApi.findRelationshipDocuments( spreeSuccessResponse, From ed7e110188a9f71c5074158ae56af74ca5fddc95 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 22 Sep 2021 14:30:00 +0200 Subject: [PATCH 65/98] Allow filtering variant images by option type --- framework/spree/.env.template | 1 + .../spree/errors/MissingOptionTypeError.ts | 1 + .../spree/errors/MissingOptionValueError.ts | 1 + framework/spree/isomorphic-config.ts | 13 +- framework/spree/utils/normalize-product.ts | 132 +++++++++++++++--- .../validate-all-products-taxonomy-id.ts | 0 .../validate-cookie-expire.ts | 0 .../validate-images-option-filter.ts | 15 ++ .../validate-placeholder-image-url.ts | 0 .../validate-products-prerender-count.ts | 0 10 files changed, 141 insertions(+), 22 deletions(-) create mode 100644 framework/spree/errors/MissingOptionTypeError.ts create mode 100644 framework/spree/errors/MissingOptionValueError.ts rename framework/spree/utils/{ => validations}/validate-all-products-taxonomy-id.ts (100%) rename framework/spree/utils/{ => validations}/validate-cookie-expire.ts (100%) create mode 100644 framework/spree/utils/validations/validate-images-option-filter.ts rename framework/spree/utils/{ => validations}/validate-placeholder-image-url.ts (100%) rename framework/spree/utils/{ => validations}/validate-products-prerender-count.ts (100%) diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 5c4f05184d..4fd558569b 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -17,3 +17,4 @@ NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS=false NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT=10 NEXT_PUBLIC_SPREE_PRODUCT_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg +NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER=false diff --git a/framework/spree/errors/MissingOptionTypeError.ts b/framework/spree/errors/MissingOptionTypeError.ts new file mode 100644 index 0000000000..14c51aecac --- /dev/null +++ b/framework/spree/errors/MissingOptionTypeError.ts @@ -0,0 +1 @@ +export default class MissingOptionTypeError extends Error {} diff --git a/framework/spree/errors/MissingOptionValueError.ts b/framework/spree/errors/MissingOptionValueError.ts new file mode 100644 index 0000000000..04457ac5e3 --- /dev/null +++ b/framework/spree/errors/MissingOptionValueError.ts @@ -0,0 +1 @@ +export default class MissingOptionValueError extends Error {} diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index ed1bb4a552..9e77452918 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -1,9 +1,10 @@ import forceIsomorphicConfigValues from './utils/force-isomorphic-config-values' import requireConfig from './utils/require-config' -import validateAllProductsTaxonomyId from './utils/validate-all-products-taxonomy-id' -import validateCookieExpire from './utils/validate-cookie-expire' -import validatePlaceholderImageUrl from './utils/validate-placeholder-image-url' -import validateProductsPrerenderCount from './utils/validate-products-prerender-count' +import validateAllProductsTaxonomyId from './utils/validations/validate-all-products-taxonomy-id' +import validateCookieExpire from './utils/validations/validate-cookie-expire' +import validateImagesOptionFilter from './utils/validations/validate-images-option-filter' +import validatePlaceholderImageUrl from './utils/validations/validate-placeholder-image-url' +import validateProductsPrerenderCount from './utils/validations/validate-products-prerender-count' const isomorphicConfig = { apiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, @@ -31,6 +32,9 @@ const isomorphicConfig = { lineItemPlaceholderImageUrl: validatePlaceholderImageUrl( process.env.NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL ), + imagesOptionFilter: validateImagesOptionFilter( + process.env.NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER + ), } export default forceIsomorphicConfigValues( @@ -49,6 +53,7 @@ export default forceIsomorphicConfigValues( 'lastUpdatedProductsPrerenderCount', 'productPlaceholderImageUrl', 'lineItemPlaceholderImageUrl', + 'imagesOptionFilter', ] ) diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 6ef53f8a5c..07205cd7fb 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -13,13 +13,20 @@ import expandOptions from './expand-options' import getMediaGallery from './get-media-gallery' import getProductPath from './get-product-path' import MissingPrimaryVariantError from '../errors/MissingPrimaryVariantError' +import MissingOptionTypeError from '../errors/MissingOptionTypeError' +import MissingOptionValueError from '../errors/MissingOptionValueError' import type { SpreeSdkResponse, VariantAttr } from '@framework/types' import { jsonApi } from '@spree/storefront-api-v2-sdk' +import { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as | string | false +const imagesOptionFilter = requireConfigValue('imagesOptionFilter') as + | string + | false + const normalizeProduct = ( spreeSuccessResponse: SpreeSdkResponse, spreeProduct: ProductAttr @@ -38,24 +45,6 @@ const normalizeProduct = ( const sku = primaryVariant.attributes.sku - const spreeImageRecords = jsonApi.findRelationshipDocuments( - spreeSuccessResponse, - spreeProduct, - 'images' - ) - - const productImages = getMediaGallery( - spreeImageRecords, - createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string) - ) - - const images: ProductImage[] = - productImages.length === 0 - ? placeholderImage === false - ? [] - : [{ url: placeholderImage }] - : productImages - const price: ProductPrice = { value: parseFloat(spreeProduct.attributes.price), currencyCode: spreeProduct.attributes.currency, @@ -106,6 +95,113 @@ const normalizeProduct = ( } }) + const spreePrimaryVariantImageRecords = jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + primaryVariant, + 'images' + ) + + let spreeVariantImageRecords: JsonApiDocument[] + + if (imagesOptionFilter === false) { + spreeVariantImageRecords = spreeVariantRecords.reduce( + (accumulatedImageRecords, spreeVariantRecord) => { + return [ + ...accumulatedImageRecords, + ...jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeVariantRecord, + 'images' + ), + ] + }, + [] + ) + } else { + const spreeOptionTypes = jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeProduct, + 'option_types' + ) + + const imagesFilterOptionType = spreeOptionTypes.find( + (spreeOptionType) => + spreeOptionType.attributes.name === imagesOptionFilter + ) + + if (!imagesFilterOptionType) { + throw new MissingOptionTypeError( + `Couldn't find option type having name ${imagesOptionFilter}.` + ) + } + + const imagesOptionTypeFilterId = imagesFilterOptionType.id + const includedOptionValuesImagesIds: string[] = [] + + spreeVariantImageRecords = spreeVariantRecords.reduce( + (accumulatedImageRecords, spreeVariantRecord) => { + const spreeVariantOptionValuesIdentifiers: RelationType[] = + spreeVariantRecord.relationships.option_values.data + + const spreeOptionValueOfFilterTypeIdentifier = + spreeVariantOptionValuesIdentifiers.find( + (spreeVariantOptionValuesIdentifier: RelationType) => + imagesFilterOptionType.relationships.option_values.data.some( + (filterOptionTypeValueIdentifier: RelationType) => + filterOptionTypeValueIdentifier.id === + spreeVariantOptionValuesIdentifier.id + ) + ) + + if (!spreeOptionValueOfFilterTypeIdentifier) { + throw new MissingOptionValueError( + `Couldn't find option value related to option type with id ${imagesOptionTypeFilterId}.` + ) + } + + const optionValueImagesAlreadyIncluded = + includedOptionValuesImagesIds.includes( + spreeOptionValueOfFilterTypeIdentifier.id + ) + + if (optionValueImagesAlreadyIncluded) { + return accumulatedImageRecords + } + + includedOptionValuesImagesIds.push( + spreeOptionValueOfFilterTypeIdentifier.id + ) + + return [ + ...accumulatedImageRecords, + ...jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeVariantRecord, + 'images' + ), + ] + }, + [] + ) + } + + const spreeImageRecords = [ + ...spreePrimaryVariantImageRecords, + ...spreeVariantImageRecords, + ] + + const productImages = getMediaGallery( + spreeImageRecords, + createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string) + ) + + const images: ProductImage[] = + productImages.length === 0 + ? placeholderImage === false + ? [] + : [{ url: placeholderImage }] + : productImages + const slug = spreeProduct.attributes.slug const path = getProductPath(spreeProduct) diff --git a/framework/spree/utils/validate-all-products-taxonomy-id.ts b/framework/spree/utils/validations/validate-all-products-taxonomy-id.ts similarity index 100% rename from framework/spree/utils/validate-all-products-taxonomy-id.ts rename to framework/spree/utils/validations/validate-all-products-taxonomy-id.ts diff --git a/framework/spree/utils/validate-cookie-expire.ts b/framework/spree/utils/validations/validate-cookie-expire.ts similarity index 100% rename from framework/spree/utils/validate-cookie-expire.ts rename to framework/spree/utils/validations/validate-cookie-expire.ts diff --git a/framework/spree/utils/validations/validate-images-option-filter.ts b/framework/spree/utils/validations/validate-images-option-filter.ts new file mode 100644 index 0000000000..8b6ef9892c --- /dev/null +++ b/framework/spree/utils/validations/validate-images-option-filter.ts @@ -0,0 +1,15 @@ +const validateImagesOptionFilter = ( + optionTypeNameOrFalse: unknown +): string | false => { + if (!optionTypeNameOrFalse || optionTypeNameOrFalse === 'false') { + return false + } + + if (typeof optionTypeNameOrFalse === 'string') { + return optionTypeNameOrFalse + } + + throw new TypeError('optionTypeNameOrFalse must be a string or falsy.') +} + +export default validateImagesOptionFilter diff --git a/framework/spree/utils/validate-placeholder-image-url.ts b/framework/spree/utils/validations/validate-placeholder-image-url.ts similarity index 100% rename from framework/spree/utils/validate-placeholder-image-url.ts rename to framework/spree/utils/validations/validate-placeholder-image-url.ts diff --git a/framework/spree/utils/validate-products-prerender-count.ts b/framework/spree/utils/validations/validate-products-prerender-count.ts similarity index 100% rename from framework/spree/utils/validate-products-prerender-count.ts rename to framework/spree/utils/validations/validate-products-prerender-count.ts From 73c5e9df1003420a13b9ebebd6430e0e6263fc3d Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 24 Sep 2021 14:25:02 +0200 Subject: [PATCH 66/98] Upgrade checkout behavior in line with core NextJS Commerce changes --- .../checkout/{checkout.ts => get-checkout.ts} | 8 ++++---- framework/spree/api/endpoints/checkout/index.ts | 7 +++++-- .../spree/api/endpoints/customer/address.ts | 1 + framework/spree/api/endpoints/customer/card.ts | 1 + framework/spree/checkout/use-checkout.tsx | 14 ++++++++++++++ framework/spree/commerce.config.json | 3 ++- .../spree/customer/address/use-add-item.tsx | 16 ++++++++++++++++ framework/spree/customer/card/use-add-item.tsx | 16 ++++++++++++++++ framework/spree/provider.ts | 2 ++ 9 files changed, 61 insertions(+), 7 deletions(-) rename framework/spree/api/endpoints/checkout/{checkout.ts => get-checkout.ts} (90%) create mode 100644 framework/spree/api/endpoints/customer/address.ts create mode 100644 framework/spree/api/endpoints/customer/card.ts create mode 100644 framework/spree/checkout/use-checkout.tsx create mode 100644 framework/spree/customer/address/use-add-item.tsx create mode 100644 framework/spree/customer/card/use-add-item.tsx diff --git a/framework/spree/api/endpoints/checkout/checkout.ts b/framework/spree/api/endpoints/checkout/get-checkout.ts similarity index 90% rename from framework/spree/api/endpoints/checkout/checkout.ts rename to framework/spree/api/endpoints/checkout/get-checkout.ts index 3622e16306..985239678c 100644 --- a/framework/spree/api/endpoints/checkout/checkout.ts +++ b/framework/spree/api/endpoints/checkout/get-checkout.ts @@ -1,9 +1,9 @@ import type { CheckoutEndpoint } from '.' -const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ - req: request, +const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ + req: _request, res: response, - config, + config: _config, }) => { try { const html = ` @@ -41,4 +41,4 @@ const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({ } } -export default checkout +export default getCheckout diff --git a/framework/spree/api/endpoints/checkout/index.ts b/framework/spree/api/endpoints/checkout/index.ts index ffa1bb04f5..cd4ffea5dd 100644 --- a/framework/spree/api/endpoints/checkout/index.ts +++ b/framework/spree/api/endpoints/checkout/index.ts @@ -2,7 +2,7 @@ import { createEndpoint } from '@commerce/api' import type { GetAPISchema, CommerceAPI } from '@commerce/api' import checkoutEndpoint from '@commerce/api/endpoints/checkout' import type { CheckoutSchema } from '@commerce/types/checkout' -import checkout from './checkout' +import getCheckout from './get-checkout' import type { SpreeApiProvider } from '../..' export type CheckoutAPI = GetAPISchema< @@ -12,7 +12,10 @@ export type CheckoutAPI = GetAPISchema< export type CheckoutEndpoint = CheckoutAPI['endpoint'] -export const handlers: CheckoutEndpoint['handlers'] = { checkout } +export const handlers: CheckoutEndpoint['handlers'] = { + getCheckout, + submitCheckout: () => {}, +} const checkoutApi = createEndpoint({ handler: checkoutEndpoint, diff --git a/framework/spree/api/endpoints/customer/address.ts b/framework/spree/api/endpoints/customer/address.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/customer/address.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/api/endpoints/customer/card.ts b/framework/spree/api/endpoints/customer/card.ts new file mode 100644 index 0000000000..491bf0ac93 --- /dev/null +++ b/framework/spree/api/endpoints/customer/card.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/spree/checkout/use-checkout.tsx b/framework/spree/checkout/use-checkout.tsx new file mode 100644 index 0000000000..942f85b830 --- /dev/null +++ b/framework/spree/checkout/use-checkout.tsx @@ -0,0 +1,14 @@ +import { SWRHook } from '@commerce/utils/types' +import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout' + +export default useCheckout as UseCheckout + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ useData }) => + async (input) => ({}), +} diff --git a/framework/spree/commerce.config.json b/framework/spree/commerce.config.json index e1e53245ce..a27eb30c8f 100644 --- a/framework/spree/commerce.config.json +++ b/framework/spree/commerce.config.json @@ -4,6 +4,7 @@ "wishlist": false, "cart": true, "search": true, - "customerAuth": false + "customerAuth": false, + "customCheckout": false } } diff --git a/framework/spree/customer/address/use-add-item.tsx b/framework/spree/customer/address/use-add-item.tsx new file mode 100644 index 0000000000..2394bbd3cf --- /dev/null +++ b/framework/spree/customer/address/use-add-item.tsx @@ -0,0 +1,16 @@ +import useAddItem from '@commerce/customer/address/use-add-item' +import type { UseAddItem } from '@commerce/customer/address/use-add-item' +import type { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/spree/customer/card/use-add-item.tsx b/framework/spree/customer/card/use-add-item.tsx new file mode 100644 index 0000000000..2394bbd3cf --- /dev/null +++ b/framework/spree/customer/card/use-add-item.tsx @@ -0,0 +1,16 @@ +import useAddItem from '@commerce/customer/address/use-add-item' +import type { UseAddItem } from '@commerce/customer/address/use-add-item' +import type { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => + async () => ({}), +} diff --git a/framework/spree/provider.ts b/framework/spree/provider.ts index a39f1201d7..4adda3bbd9 100644 --- a/framework/spree/provider.ts +++ b/framework/spree/provider.ts @@ -8,6 +8,7 @@ import { handler as useSearch } from './product/use-search' import { handler as useLogin } from './auth/use-login' import { handler as useLogout } from './auth/use-logout' import { handler as useSignup } from './auth/use-signup' +import { handler as useCheckout } from './checkout/use-checkout' import { requireConfigValue } from './isomorphic-config' const spreeProvider = { @@ -18,6 +19,7 @@ const spreeProvider = { customer: { useCustomer }, products: { useSearch }, auth: { useLogin, useLogout, useSignup }, + checkout: { useCheckout }, } export { spreeProvider } From 0fc678b260e9cbf76ebf1b427b32f4b7c3fdcc99 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 24 Sep 2021 18:52:39 +0200 Subject: [PATCH 67/98] Remove dummy submitCheckout function --- framework/spree/api/endpoints/checkout/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/framework/spree/api/endpoints/checkout/index.ts b/framework/spree/api/endpoints/checkout/index.ts index cd4ffea5dd..0a5ee9e722 100644 --- a/framework/spree/api/endpoints/checkout/index.ts +++ b/framework/spree/api/endpoints/checkout/index.ts @@ -12,10 +12,7 @@ export type CheckoutAPI = GetAPISchema< export type CheckoutEndpoint = CheckoutAPI['endpoint'] -export const handlers: CheckoutEndpoint['handlers'] = { - getCheckout, - submitCheckout: () => {}, -} +export const handlers: CheckoutEndpoint['handlers'] = { getCheckout } const checkoutApi = createEndpoint({ handler: checkoutEndpoint, From 42318f47142dda6392b3f2041c4294e4893e88ef Mon Sep 17 00:00:00 2001 From: Robert Nowakowski Date: Wed, 29 Sep 2021 15:20:57 +0200 Subject: [PATCH 68/98] [NX-24] Display PDP option types sorted by position from Spree --- framework/spree/types/index.ts | 3 +++ framework/spree/utils/expand-options.ts | 19 +++++++++++-------- framework/spree/utils/normalize-product.ts | 6 +++--- framework/spree/utils/sort-option-types.ts | 9 +++++++++ 4 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 framework/spree/utils/sort-option-types.ts diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 1cdaa2b210..68844f4f83 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -6,6 +6,7 @@ import type { } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { Response } from '@vercel/fetch' +import type { ProductOption } from '@commerce/types/product' export type UnknownObjectValues = Record @@ -109,3 +110,5 @@ export interface ProductSlugAttr extends JsonApiDocument { export interface IProductsSlugs extends JsonApiListResponse { data: ProductSlugAttr[] } + +export type ExpandedProductOption = ProductOption & { position: number } diff --git a/framework/spree/utils/expand-options.ts b/framework/spree/utils/expand-options.ts index 3b7b31b7be..8bada03de9 100644 --- a/framework/spree/utils/expand-options.ts +++ b/framework/spree/utils/expand-options.ts @@ -1,6 +1,5 @@ import type { - ProductOption, - ProductOptionValues, + ProductOptionValues } from '@commerce/types/product' import type { JsonApiDocument, @@ -9,17 +8,18 @@ import type { import { jsonApi } from '@spree/storefront-api-v2-sdk' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import SpreeResponseContentError from '../errors/SpreeResponseContentError' -import type { OptionTypeAttr } from '@framework/types' +import type { OptionTypeAttr, ExpandedProductOption } from '@framework/types' +import sortOptionsByPosition from '@framework/utils/sort-option-types' -const isColorProductOption = (productOption: ProductOption) => { +const isColorProductOption = (productOption: ExpandedProductOption) => { return productOption.displayName === 'Color' } const expandOptions = ( spreeSuccessResponse: JsonApiResponse, spreeOptionValue: JsonApiDocument, - accumulatedOptions: ProductOption[] -): ProductOption[] => { + accumulatedOptions: ExpandedProductOption[] +): ExpandedProductOption[] => { const spreeOptionTypeIdentifier = spreeOptionValue.relationships.option_type .data as RelationType @@ -27,7 +27,7 @@ const expandOptions = ( (option) => option.id == spreeOptionTypeIdentifier.id ) - let option: ProductOption + let option: ExpandedProductOption if (existingOptionIndex === -1) { const spreeOptionType = jsonApi.findDocument( @@ -45,6 +45,7 @@ const expandOptions = ( __typename: 'MultipleChoiceOption', id: spreeOptionType.id, displayName: spreeOptionType.attributes.presentation, + position: spreeOptionType.attributes.position, values: [], } } else { @@ -93,7 +94,9 @@ const expandOptions = ( values: expandedOptionValues, } - return expandedOptions + const sortedOptions = sortOptionsByPosition(expandedOptions) + + return sortedOptions } return accumulatedOptions diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 07205cd7fb..59d8f536a7 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -1,7 +1,6 @@ import type { Product, ProductImage, - ProductOption, ProductPrice, ProductVariant, } from '@commerce/types/product' @@ -18,6 +17,7 @@ import MissingOptionValueError from '../errors/MissingOptionValueError' import type { SpreeSdkResponse, VariantAttr } from '@framework/types' import { jsonApi } from '@spree/storefront-api-v2-sdk' import { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' +import type { ExpandedProductOption } from '@framework/types' const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as | string @@ -58,7 +58,7 @@ const normalizeProduct = ( hasNonMasterVariants let variants: ProductVariant[] - let options: ProductOption[] = [] + let options: ExpandedProductOption[] = [] const spreeVariantRecords = jsonApi.findRelationshipDocuments( spreeSuccessResponse, @@ -67,7 +67,7 @@ const normalizeProduct = ( ) variants = spreeVariantRecords.map((spreeVariantRecord) => { - let variantOptions: ProductOption[] = [] + let variantOptions: ExpandedProductOption[] = [] if (showOptions) { const spreeOptionValues = jsonApi.findRelationshipDocuments( diff --git a/framework/spree/utils/sort-option-types.ts b/framework/spree/utils/sort-option-types.ts new file mode 100644 index 0000000000..1217579303 --- /dev/null +++ b/framework/spree/utils/sort-option-types.ts @@ -0,0 +1,9 @@ +import type { ExpandedProductOption } from '@framework/types' + +const sortOptionsByPosition = (options: ExpandedProductOption[]): ExpandedProductOption[] => { + return options.sort((firstOption, secondOption) => { + return firstOption.position - secondOption.position + }) +} + +export default sortOptionsByPosition From 669cc8d06f8e9ef6dd533c9d676c73be7dda24f3 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 11 Oct 2021 15:36:39 +0200 Subject: [PATCH 69/98] Supply Spree primary variant if a product has no option variants --- framework/spree/utils/normalize-product.ts | 73 +++++++++++++--------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 59d8f536a7..f39e92ab27 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -31,19 +31,20 @@ const normalizeProduct = ( spreeSuccessResponse: SpreeSdkResponse, spreeProduct: ProductAttr ): Product => { - const primaryVariant = jsonApi.findSingleRelationshipDocument( - spreeSuccessResponse, - spreeProduct, - 'primary_variant' - ) + const spreePrimaryVariant = + jsonApi.findSingleRelationshipDocument( + spreeSuccessResponse, + spreeProduct, + 'primary_variant' + ) - if (primaryVariant === null) { + if (spreePrimaryVariant === null) { throw new MissingPrimaryVariantError( `Couldn't find primary variant for product with id ${spreeProduct.id}.` ) } - const sku = primaryVariant.attributes.sku + const sku = spreePrimaryVariant.attributes.sku const price: ProductPrice = { value: parseFloat(spreeProduct.attributes.price), @@ -57,7 +58,6 @@ const normalizeProduct = ( (requireConfigValue('showSingleVariantOptions') as boolean) || hasNonMasterVariants - let variants: ProductVariant[] let options: ExpandedProductOption[] = [] const spreeVariantRecords = jsonApi.findRelationshipDocuments( @@ -66,38 +66,51 @@ const normalizeProduct = ( 'variants' ) - variants = spreeVariantRecords.map((spreeVariantRecord) => { - let variantOptions: ExpandedProductOption[] = [] - - if (showOptions) { - const spreeOptionValues = jsonApi.findRelationshipDocuments( - spreeSuccessResponse, - spreeVariantRecord, - 'option_values' - ) + // Use variants with option values if available. Fall back to + // Spree primary_variant if no explicit variants are present. + const spreeOptionsVariantsOrPrimary = + spreeVariantRecords.length === 0 + ? [spreePrimaryVariant] + : spreeVariantRecords - // Only include options which are used by variants. + const variants: ProductVariant[] = spreeOptionsVariantsOrPrimary.map( + (spreeVariantRecord) => { + let variantOptions: ExpandedProductOption[] = [] - spreeOptionValues.forEach((spreeOptionValue) => { - variantOptions = expandOptions( + if (showOptions) { + const spreeOptionValues = jsonApi.findRelationshipDocuments( spreeSuccessResponse, - spreeOptionValue, - variantOptions + spreeVariantRecord, + 'option_values' ) - options = expandOptions(spreeSuccessResponse, spreeOptionValue, options) - }) - } + // Only include options which are used by variants. + + spreeOptionValues.forEach((spreeOptionValue) => { + variantOptions = expandOptions( + spreeSuccessResponse, + spreeOptionValue, + variantOptions + ) - return { - id: spreeVariantRecord.id, - options: variantOptions, + options = expandOptions( + spreeSuccessResponse, + spreeOptionValue, + options + ) + }) + } + + return { + id: spreeVariantRecord.id, + options: variantOptions, + } } - }) + ) const spreePrimaryVariantImageRecords = jsonApi.findRelationshipDocuments( spreeSuccessResponse, - primaryVariant, + spreePrimaryVariant, 'images' ) From 528831314007e270cef01cf0a06d47d464119129 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 11 Oct 2021 16:07:28 +0200 Subject: [PATCH 70/98] Do not throw an error if a product doesn't have NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER --- .../spree/errors/MissingOptionTypeError.ts | 1 - framework/spree/utils/normalize-product.ts | 100 +++++++++--------- 2 files changed, 51 insertions(+), 50 deletions(-) delete mode 100644 framework/spree/errors/MissingOptionTypeError.ts diff --git a/framework/spree/errors/MissingOptionTypeError.ts b/framework/spree/errors/MissingOptionTypeError.ts deleted file mode 100644 index 14c51aecac..0000000000 --- a/framework/spree/errors/MissingOptionTypeError.ts +++ /dev/null @@ -1 +0,0 @@ -export default class MissingOptionTypeError extends Error {} diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index f39e92ab27..6da2b6f0a1 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -12,7 +12,6 @@ import expandOptions from './expand-options' import getMediaGallery from './get-media-gallery' import getProductPath from './get-product-path' import MissingPrimaryVariantError from '../errors/MissingPrimaryVariantError' -import MissingOptionTypeError from '../errors/MissingOptionTypeError' import MissingOptionValueError from '../errors/MissingOptionValueError' import type { SpreeSdkResponse, VariantAttr } from '@framework/types' import { jsonApi } from '@spree/storefront-api-v2-sdk' @@ -143,59 +142,62 @@ const normalizeProduct = ( ) if (!imagesFilterOptionType) { - throw new MissingOptionTypeError( - `Couldn't find option type having name ${imagesOptionFilter}.` + console.warn( + `Couldn't find option type having name ${imagesOptionFilter} for product with id ${spreeProduct.id}.` + + ' Showing no images for this product.' ) - } - - const imagesOptionTypeFilterId = imagesFilterOptionType.id - const includedOptionValuesImagesIds: string[] = [] - - spreeVariantImageRecords = spreeVariantRecords.reduce( - (accumulatedImageRecords, spreeVariantRecord) => { - const spreeVariantOptionValuesIdentifiers: RelationType[] = - spreeVariantRecord.relationships.option_values.data - - const spreeOptionValueOfFilterTypeIdentifier = - spreeVariantOptionValuesIdentifiers.find( - (spreeVariantOptionValuesIdentifier: RelationType) => - imagesFilterOptionType.relationships.option_values.data.some( - (filterOptionTypeValueIdentifier: RelationType) => - filterOptionTypeValueIdentifier.id === - spreeVariantOptionValuesIdentifier.id - ) - ) - if (!spreeOptionValueOfFilterTypeIdentifier) { - throw new MissingOptionValueError( - `Couldn't find option value related to option type with id ${imagesOptionTypeFilterId}.` - ) - } - - const optionValueImagesAlreadyIncluded = - includedOptionValuesImagesIds.includes( + spreeVariantImageRecords = [] + } else { + const imagesOptionTypeFilterId = imagesFilterOptionType.id + const includedOptionValuesImagesIds: string[] = [] + + spreeVariantImageRecords = spreeVariantRecords.reduce( + (accumulatedImageRecords, spreeVariantRecord) => { + const spreeVariantOptionValuesIdentifiers: RelationType[] = + spreeVariantRecord.relationships.option_values.data + + const spreeOptionValueOfFilterTypeIdentifier = + spreeVariantOptionValuesIdentifiers.find( + (spreeVariantOptionValuesIdentifier: RelationType) => + imagesFilterOptionType.relationships.option_values.data.some( + (filterOptionTypeValueIdentifier: RelationType) => + filterOptionTypeValueIdentifier.id === + spreeVariantOptionValuesIdentifier.id + ) + ) + + if (!spreeOptionValueOfFilterTypeIdentifier) { + throw new MissingOptionValueError( + `Couldn't find option value related to option type with id ${imagesOptionTypeFilterId}.` + ) + } + + const optionValueImagesAlreadyIncluded = + includedOptionValuesImagesIds.includes( + spreeOptionValueOfFilterTypeIdentifier.id + ) + + if (optionValueImagesAlreadyIncluded) { + return accumulatedImageRecords + } + + includedOptionValuesImagesIds.push( spreeOptionValueOfFilterTypeIdentifier.id ) - if (optionValueImagesAlreadyIncluded) { - return accumulatedImageRecords - } - - includedOptionValuesImagesIds.push( - spreeOptionValueOfFilterTypeIdentifier.id - ) - - return [ - ...accumulatedImageRecords, - ...jsonApi.findRelationshipDocuments( - spreeSuccessResponse, - spreeVariantRecord, - 'images' - ), - ] - }, - [] - ) + return [ + ...accumulatedImageRecords, + ...jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeVariantRecord, + 'images' + ), + ] + }, + [] + ) + } } const spreeImageRecords = [ From d12f1b1c2c10a47e5972d126b140f9b38a41a6f4 Mon Sep 17 00:00:00 2001 From: Robert Nowakowski Date: Wed, 20 Oct 2021 15:55:43 +0200 Subject: [PATCH 71/98] [NX-43] Uses image transformations when fetching products images --- framework/spree/.env.template | 2 ++ .../api/operations/get-all-product-paths.ts | 7 ++++++ .../spree/api/operations/get-all-products.ts | 7 ++++++ framework/spree/api/operations/get-product.ts | 8 ++++++ framework/spree/isomorphic-config.ts | 10 ++++++++ framework/spree/product/use-search.tsx | 8 ++++++ framework/spree/types/index.ts | 1 + .../utils/create-get-absolute-image-url.ts | 2 +- .../validations/validate-images-quality.ts | 25 +++++++++++++++++++ .../utils/validations/validate-images-size.ts | 15 +++++++++++ 10 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 framework/spree/utils/validations/validate-images-quality.ts create mode 100644 framework/spree/utils/validations/validate-images-size.ts diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 4fd558569b..3e1e93954e 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -18,3 +18,5 @@ NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT=10 NEXT_PUBLIC_SPREE_PRODUCT_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER=false +NEXT_PUBLIC_SPREE_IMAGES_SIZE=1000x1000 +NEXT_PUBLIC_SPREE_IMAGES_QUALITY=100 diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts index 9e59284650..518ccfde24 100644 --- a/framework/spree/api/operations/get-all-product-paths.ts +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -9,6 +9,9 @@ import type { IProductsSlugs, SpreeSdkVariables } from '../../types' import getProductPath from '../../utils/get-product-path' import type { SpreeApiConfig, SpreeApiProvider } from '..' +const imagesSize = requireConfigValue('imagesSize') as string +const imagesQuality = requireConfigValue('imagesQuality') as number + export default function getAllProductPathsOperation({ commerce, }: OperationContext) { @@ -64,6 +67,10 @@ export default function getAllProductPathsOperation({ product: 'slug', }, per_page: productsCount, + image_transformation: { + quality: imagesQuality, + size: imagesSize + } }, ], } diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 40407cd40b..07ee9ba872 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -10,6 +10,9 @@ import type { SpreeSdkVariables } from 'framework/spree/types' import normalizeProduct from '../../utils/normalize-product' import { requireConfigValue } from '@framework/isomorphic-config' +const imagesSize = requireConfigValue('imagesSize') as string +const imagesQuality = requireConfigValue('imagesQuality') as number + export default function getAllProductsOperation({ commerce, }: OperationContext) { @@ -60,6 +63,10 @@ export default function getAllProductsOperation({ 'primary_variant,variants,images,option_types,variants.option_values', per_page: first, ...filter, + image_transformation: { + quality: imagesQuality, + size: imagesSize + } }, ], } diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index b665538340..13c18390a0 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -8,6 +8,10 @@ import type { IProduct } from '@spree/storefront-api-v2-sdk/types/interfaces/Pro import type { SpreeSdkVariables } from 'framework/spree/types' import MissingSlugVariableError from 'framework/spree/errors/MissingSlugVariableError' import normalizeProduct from '../../utils/normalize-product' +import { requireConfigValue } from '../../isomorphic-config' + +const imagesSize = requireConfigValue('imagesSize') as string +const imagesQuality = requireConfigValue('imagesQuality') as number export default function getProductOperation({ commerce, @@ -56,6 +60,10 @@ export default function getProductOperation({ { include: 'primary_variant,variants,images,option_types,variants.option_values', + image_transformation: { + quality: imagesQuality, + size: imagesSize + } }, ], } diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index 9e77452918..1895db1a93 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -5,6 +5,8 @@ import validateCookieExpire from './utils/validations/validate-cookie-expire' import validateImagesOptionFilter from './utils/validations/validate-images-option-filter' import validatePlaceholderImageUrl from './utils/validations/validate-placeholder-image-url' import validateProductsPrerenderCount from './utils/validations/validate-products-prerender-count' +import validateImagesSize from './utils/validations/validate-images-size' +import validateImagesQuality from './utils/validations/validate-images-quality' const isomorphicConfig = { apiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, @@ -35,6 +37,12 @@ const isomorphicConfig = { imagesOptionFilter: validateImagesOptionFilter( process.env.NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER ), + imagesSize: validateImagesSize( + process.env.NEXT_PUBLIC_SPREE_IMAGES_SIZE + ), + imagesQuality: validateImagesQuality( + process.env.NEXT_PUBLIC_SPREE_IMAGES_QUALITY + ), } export default forceIsomorphicConfigValues( @@ -54,6 +62,8 @@ export default forceIsomorphicConfigValues( 'productPlaceholderImageUrl', 'lineItemPlaceholderImageUrl', 'imagesOptionFilter', + 'imagesSize', + 'imagesQuality' ] ) diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index cd941f5b34..0cf4f3e209 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -5,6 +5,10 @@ import type { UseSearch } from '@commerce/product/use-search' import normalizeProduct from '../utils/normalize-product' import type { GraphQLFetcherResult } from '@commerce/api' import { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import { requireConfigValue } from '../isomorphic-config' + +const imagesSize = requireConfigValue('imagesSize') as string +const imagesQuality = requireConfigValue('imagesQuality') as number const nextToSpreeSortMap: { [key: string]: string } = { 'trending-desc': 'available_on', @@ -53,6 +57,10 @@ export const handler: SWRHook = { per_page: 50, ...filter, ...sort, + image_transformation: { + quality: imagesQuality, + size: imagesSize + } }, ], }, diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 68844f4f83..f2455dc648 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -43,6 +43,7 @@ export interface SpreeProductImage extends JsonApiDocument { position: number alt: string original_url: string + transformed_url: string | null styles: ImageStyle[] } } diff --git a/framework/spree/utils/create-get-absolute-image-url.ts b/framework/spree/utils/create-get-absolute-image-url.ts index 09a31c54cd..6e9e3260a3 100644 --- a/framework/spree/utils/create-get-absolute-image-url.ts +++ b/framework/spree/utils/create-get-absolute-image-url.ts @@ -11,7 +11,7 @@ const createGetAbsoluteImageUrl = let url if (useOriginalImageSize) { - url = image.attributes.original_url || null + url = image.attributes.transformed_url || null } else { url = getImageUrl(image, minWidth, minHeight) } diff --git a/framework/spree/utils/validations/validate-images-quality.ts b/framework/spree/utils/validations/validate-images-quality.ts new file mode 100644 index 0000000000..46debefe50 --- /dev/null +++ b/framework/spree/utils/validations/validate-images-quality.ts @@ -0,0 +1,25 @@ +const validateImagesQuality = ( + quality: unknown +): number => { + let quality_level: number + + if (typeof quality === 'string') { + quality_level = parseInt(quality) + } else if (typeof quality === 'number') { + quality_level = quality + } else { + throw new TypeError( + 'prerenderCount count must be a string containing a number or an integer.' + ) + } + + if (quality_level === NaN) { + throw new TypeError( + 'prerenderCount count must be a string containing a number or an integer.' + ) + } + + return quality_level +} + +export default validateImagesQuality diff --git a/framework/spree/utils/validations/validate-images-size.ts b/framework/spree/utils/validations/validate-images-size.ts new file mode 100644 index 0000000000..e4a7e32a85 --- /dev/null +++ b/framework/spree/utils/validations/validate-images-size.ts @@ -0,0 +1,15 @@ +const validateImagesSize = ( + size: unknown +): string => { + if (typeof size !== 'string') { + throw new TypeError('size must be a string.') + } + + if (!size.includes('x') || size.split('x').length != 2) { + throw new Error("size must have two numbers separated with an 'x'") + } + + return size +} + +export default validateImagesSize From 311c4951129addc0b2668305d91aa2ae8c6f8cec Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 16 Nov 2021 13:08:29 +0100 Subject: [PATCH 72/98] Use bind to properly call Spree SDK methods and update SDK fetcher in line with SDK 4.12.0 --- .../utils/create-customized-fetch-fetcher.ts | 30 ++++++++++++++----- ...get-spree-sdk-method-from-endpoint-path.ts | 7 +++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/framework/spree/utils/create-customized-fetch-fetcher.ts b/framework/spree/utils/create-customized-fetch-fetcher.ts index 3dd9ba54a0..580f2956f4 100644 --- a/framework/spree/utils/create-customized-fetch-fetcher.ts +++ b/framework/spree/utils/create-customized-fetch-fetcher.ts @@ -20,7 +20,7 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( // because @vercel/fetch doesn't accept a Request object as argument // and it's not used by NJC anyway. try { - const { url, params, method, headers } = fetchOptions + const { url, params, method, headers, responseParsing } = fetchOptions const absoluteUrl = new URL(url, host) let payload @@ -52,14 +52,22 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( const responseContentType = response.headers.get('content-type') let data - if ( - !responseContentType || - (!responseContentType.includes('application/json') && - !responseContentType.includes('application/vnd.api+json')) - ) { + if (responseParsing === 'automatic') { + if ( + !responseContentType || + (!responseContentType.includes('application/json') && + !responseContentType.includes('application/vnd.api+json')) + ) { + data = await response.text() + } else { + data = await response.json() + } + } else if (responseParsing === 'text') { data = await response.text() - } else { + } else if (responseParsing === 'json') { data = await response.json() + } else if (responseParsing === 'stream') { + data = await response.body } if (!response.ok) { @@ -75,6 +83,10 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( throw error } + if (!(error instanceof Error)) { + throw error + } + throw new FetchError(null, request, null, error.message) } } catch (error) { @@ -82,6 +94,10 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( throw error } + if (!(error instanceof Error)) { + throw error + } + throw new FetchError(null, null, null, error.message) } }, diff --git a/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts b/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts index f1ce31f86a..cdac804612 100644 --- a/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts +++ b/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts @@ -38,9 +38,11 @@ const getSpreeSdkMethodFromEndpointPath = < reachedPath.push(checkedPathPart) } + const foundEndpointMethod = node[pathParts[reachedPath.length]] + if ( reachedPath.length !== pathParts.length - 1 || - typeof node[pathParts[reachedPath.length]] !== 'function' + typeof foundEndpointMethod !== 'function' ) { throw new SpreeSdkMethodFromEndpointPathError( `Couldn't reach ${path}. Farthest path reached was: ${reachedPath.join( @@ -49,8 +51,7 @@ const getSpreeSdkMethodFromEndpointPath = < ) } - return (...args: any[]) => - (node[pathParts[reachedPath.length]] as SpreeSdkMethod)(...args) + return foundEndpointMethod.bind(node) } export default getSpreeSdkMethodFromEndpointPath From c9eabf8d41d7653a48eb7221ec7bc4d5aafa05ad Mon Sep 17 00:00:00 2001 From: tniezg Date: Tue, 16 Nov 2021 14:15:10 +0100 Subject: [PATCH 73/98] Fix ESLint issues in useHook --- framework/spree/cart/use-add-item.tsx | 28 ++++++---- framework/spree/cart/use-cart.tsx | 14 +++-- framework/spree/cart/use-remove-item.tsx | 28 ++++++---- framework/spree/cart/use-update-item.tsx | 70 +++++++++++++----------- framework/spree/product/use-search.tsx | 16 ++++-- 5 files changed, 90 insertions(+), 66 deletions(-) diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index b048028eca..251bd7e6d3 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -57,19 +57,25 @@ export const handler: MutationHook = { return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) }, - useHook: - ({ fetch }) => - () => { - console.log('useAddItem useHook called.') + useHook: ({ fetch }) => { + const useWrappedHook: ReturnType['useHook']> = + () => { + console.log('useAddItem useHook called.') - const { mutate } = useCart() + const { mutate } = useCart() - return useCallback(async (input) => { - const data = await fetch({ input }) + return useCallback( + async (input) => { + const data = await fetch({ input }) - await mutate(data, false) + await mutate(data, false) - return data - }, []) - }, + return data + }, + [fetch, mutate] + ) + } + + return useWrappedHook + }, } diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index 8852970fae..0cb884d313 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -90,13 +90,14 @@ export const handler: SWRHook = { return normalizeCart(spreeCartResponse, spreeCartResponse.data) }, - useHook: - ({ useData }) => - (input = {}) => { + useHook: ({ useData }) => { + const useWrappedHook: ReturnType['useHook']> = ( + input + ) => { console.log('useCart useHook called.') const response = useData({ - swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, }) return useMemo(() => { @@ -109,5 +110,8 @@ export const handler: SWRHook = { }, }) }, [response]) - }, + } + + return useWrappedHook + }, } diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx index 87d0b533df..36b5ccb099 100644 --- a/framework/spree/cart/use-remove-item.tsx +++ b/framework/spree/cart/use-remove-item.tsx @@ -53,19 +53,25 @@ export const handler: MutationHook = { return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) }, - useHook: - ({ fetch }) => - () => { - console.log('useRemoveItem useHook called.') + useHook: ({ fetch }) => { + const useWrappedHook: ReturnType['useHook']> = + () => { + console.log('useRemoveItem useHook called.') - const { mutate } = useCart() + const { mutate } = useCart() - return useCallback(async (input) => { - const data = await fetch({ input: { itemId: input.id } }) + return useCallback( + async (input) => { + const data = await fetch({ input: { itemId: input.id } }) - await mutate(data, false) + await mutate(data, false) - return data - }, []) - }, + return data + }, + [fetch, mutate] + ) + } + + return useWrappedHook + }, } diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx index 86c6e0e609..e058765b86 100644 --- a/framework/spree/cart/use-update-item.tsx +++ b/framework/spree/cart/use-update-item.tsx @@ -2,7 +2,7 @@ import type { MutationHook } from '@commerce/utils/types' import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item' import type { UpdateItemHook } from '@commerce/types/cart' import useCart from './use-cart' -import { useCallback } from 'react' +import { useCallback, useMemo } from 'react' import { ValidationError } from '@commerce/utils/errors' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { SetQuantity } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' @@ -62,42 +62,46 @@ export const handler: MutationHook = { return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) }, - useHook: - ({ fetch }) => - (context) => { - console.log('useUpdateItem useHook called.') + useHook: ({ fetch }) => { + const useWrappedHook: ReturnType['useHook']> = + (context) => { + console.log('useUpdateItem useHook called.') - const { mutate } = useCart() + const { mutate } = useCart() - return useCallback( - debounce(async (input: UpdateItemHook['actionInput']) => { - const itemId = context?.item?.id - const productId = input.productId ?? context?.item?.productId - const variantId = input.variantId ?? context?.item?.variantId - const quantity = input.quantity + return useMemo( + () => + debounce(async (input: UpdateItemHook['actionInput']) => { + const itemId = context?.item?.id + const productId = input.productId ?? context?.item?.productId + const variantId = input.variantId ?? context?.item?.variantId + const quantity = input.quantity - if (!itemId || !productId || !variantId) { - throw new ValidationError({ - message: 'Invalid input used for this operation', - }) - } + if (!itemId || !productId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } - const data = await fetch({ - input: { - item: { - productId, - variantId, - quantity, - }, - itemId, - }, - }) + const data = await fetch({ + input: { + item: { + productId, + variantId, + quantity, + }, + itemId, + }, + }) - await mutate(data, false) + await mutate(data, false) - return data - }, context?.wait ?? 500), - [] - ) - }, + return data + }, context?.wait ?? 500), + [fetch, mutate] + ) + } + + return useWrappedHook + }, } diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 0cf4f3e209..6d72aa91d7 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -59,8 +59,8 @@ export const handler: SWRHook = { ...sort, image_transformation: { quality: imagesQuality, - size: imagesSize - } + size: imagesSize, + }, }, ], }, @@ -76,9 +76,10 @@ export const handler: SWRHook = { }, // useHook is used for both, SWR and mutation requests to the store. // useHook is called in React components. For example, after clicking `Add to cart`. - useHook: - ({ useData }) => - (input = {}) => { + useHook: ({ useData }) => { + const useWrappedHook: ReturnType['useHook']> = ( + input = {} + ) => { // useData calls the fetcher method (above). // The difference between useHook and calling fetcher directly is // useHook accepts swrOptions. @@ -98,7 +99,10 @@ export const handler: SWRHook = { ...input.swrOptions, }, }) - }, + } + + return useWrappedHook + }, } export default useSearch as UseSearch From 1ce4ea786303878c16a0cbf921ed425a6c5a32ac Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 17 Nov 2021 14:49:14 +0100 Subject: [PATCH 74/98] Support account sign up, login and logout Also - Converts the guest cart to a persisted cart tied to the logged in user after log in. - Fixes issues with use-remove-item. The cart will now properly refresh after an item is removed. - Uses the logged in user's token to adjust the cart and make other authenticated requests. - Transparently refreshed the access token of the logged in user with a refresh token. Replays requests to Spree which fail with a 401 error after refreshing the access token. --- framework/spree/.env.template | 2 + .../spree/api/operations/get-all-products.ts | 6 +- framework/spree/api/operations/get-product.ts | 6 +- framework/spree/auth/use-login.tsx | 81 +++++++++++++-- framework/spree/auth/use-logout.tsx | 70 +++++++++++-- framework/spree/auth/use-signup.tsx | 87 ++++++++++++++-- framework/spree/cart/use-add-item.tsx | 68 ++++++++++--- framework/spree/cart/use-cart.tsx | 38 +++---- framework/spree/cart/use-remove-item.tsx | 77 +++++++++++---- framework/spree/cart/use-update-item.tsx | 98 +++++++++++++------ framework/spree/checkout/use-checkout.tsx | 5 +- framework/spree/commerce.config.json | 2 +- .../spree/customer/address/use-add-item.tsx | 4 +- .../spree/customer/card/use-add-item.tsx | 5 +- framework/spree/customer/use-customer.tsx | 86 ++++++++++++++-- .../spree/errors/TokensNotRejectedError.ts | 1 + .../errors/UserTokenResponseParseError.ts | 1 + framework/spree/fetcher.ts | 74 ++++++++++---- framework/spree/isomorphic-config.ts | 12 ++- framework/spree/product/use-search.tsx | 9 +- framework/spree/types/index.ts | 22 ++++- .../convert-spree-error-to-graph-ql-error.ts | 8 +- framework/spree/utils/create-empty-cart.ts | 26 +++++ framework/spree/utils/get-cart-token.ts | 7 -- ...get-spree-sdk-method-from-endpoint-path.ts | 10 +- framework/spree/utils/login.ts | 54 ++++++++++ .../{ => normalizations}/normalize-cart.ts | 10 +- .../{ => normalizations}/normalize-product.ts | 14 +-- .../utils/normalizations/normalize-user.ts | 16 +++ framework/spree/utils/set-cart-token.ts | 16 --- framework/spree/utils/tokens/cart-token.ts | 21 ++++ .../tokens/ensure-fresh-user-access-token.ts | 50 ++++++++++ framework/spree/utils/tokens/ensure-itoken.ts | 25 +++++ framework/spree/utils/tokens/is-logged-in.ts | 9 ++ .../spree/utils/tokens/revoke-user-tokens.ts | 49 ++++++++++ .../spree/utils/tokens/user-token-response.ts | 58 +++++++++++ 36 files changed, 936 insertions(+), 191 deletions(-) create mode 100644 framework/spree/errors/TokensNotRejectedError.ts create mode 100644 framework/spree/errors/UserTokenResponseParseError.ts create mode 100644 framework/spree/utils/create-empty-cart.ts delete mode 100644 framework/spree/utils/get-cart-token.ts create mode 100644 framework/spree/utils/login.ts rename framework/spree/utils/{ => normalizations}/normalize-cart.ts (95%) rename framework/spree/utils/{ => normalizations}/normalize-product.ts (93%) create mode 100644 framework/spree/utils/normalizations/normalize-user.ts delete mode 100644 framework/spree/utils/set-cart-token.ts create mode 100644 framework/spree/utils/tokens/cart-token.ts create mode 100644 framework/spree/utils/tokens/ensure-fresh-user-access-token.ts create mode 100644 framework/spree/utils/tokens/ensure-itoken.ts create mode 100644 framework/spree/utils/tokens/is-logged-in.ts create mode 100644 framework/spree/utils/tokens/revoke-user-tokens.ts create mode 100644 framework/spree/utils/tokens/user-token-response.ts diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 3e1e93954e..24ad1e4100 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -8,6 +8,8 @@ NEXT_PUBLIC_SPREE_DEFAULT_LOCALE=en-us NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart_token {# -- cookie expire in days #} NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE=7 +NEXT_PUBLIC_SPREE_USER_COOKIE_NAME=spree_user_token +NEXT_PUBLIC_SPREE_USER_COOKIE_EXPIRE=7 NEXT_PUBLIC_SPREE_IMAGE_HOST=http://localhost:4000 NEXT_PUBLIC_SPREE_ALLOWED_IMAGE_DOMAIN=localhost NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK=categories diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 07ee9ba872..52db5edd3c 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -7,7 +7,7 @@ import type { import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { SpreeApiConfig, SpreeApiProvider } from '../index' import type { SpreeSdkVariables } from 'framework/spree/types' -import normalizeProduct from '../../utils/normalize-product' +import normalizeProduct from '../../utils/normalizations/normalize-product' import { requireConfigValue } from '@framework/isomorphic-config' const imagesSize = requireConfigValue('imagesSize') as string @@ -65,8 +65,8 @@ export default function getAllProductsOperation({ ...filter, image_transformation: { quality: imagesQuality, - size: imagesSize - } + size: imagesSize, + }, }, ], } diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index 13c18390a0..cf9cd6fc71 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -7,7 +7,7 @@ import type { import type { IProduct } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { SpreeSdkVariables } from 'framework/spree/types' import MissingSlugVariableError from 'framework/spree/errors/MissingSlugVariableError' -import normalizeProduct from '../../utils/normalize-product' +import normalizeProduct from '../../utils/normalizations/normalize-product' import { requireConfigValue } from '../../isomorphic-config' const imagesSize = requireConfigValue('imagesSize') as string @@ -62,8 +62,8 @@ export default function getProductOperation({ 'primary_variant,variants,images,option_types,variants.option_values', image_transformation: { quality: imagesQuality, - size: imagesSize - } + size: imagesSize, + }, }, ], } diff --git a/framework/spree/auth/use-login.tsx b/framework/spree/auth/use-login.tsx index 28351dc7fa..3c6f819812 100644 --- a/framework/spree/auth/use-login.tsx +++ b/framework/spree/auth/use-login.tsx @@ -1,16 +1,83 @@ -import { MutationHook } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import useLogin, { UseLogin } from '@commerce/auth/use-login' +import type { LoginHook } from '@commerce/types/login' +import { useCallback } from 'react' +import type { AuthTokenAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Authentication' +import useCustomer from '../customer/use-customer' +import { FetcherError, ValidationError } from '@commerce/utils/errors' +import login from '../utils/login' export default useLogin as UseLogin -export const handler: MutationHook = { +export const handler: MutationHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - query: '', + url: 'authentication', + query: 'getToken', }, - async fetcher() { - return null + async fetcher({ input, options, fetch }) { + console.info( + 'useLogin fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + const { email, password } = input + + if (!email || !password) { + throw new ValidationError({ + message: 'Email and password need to be provided.', + }) + } + + const getTokenParameters: AuthTokenAttr = { + username: email, + password, + } + + try { + await login(fetch, getTokenParameters) + + return null + } catch (getTokenError) { + if ( + getTokenError instanceof FetcherError && + getTokenError.status === 400 + ) { + // Change the error message to be more user friendly. + throw new FetcherError({ + status: getTokenError.status, + message: 'The email or password is invalid.', + code: getTokenError.code, + }) + } + + throw getTokenError + } + + // TODO: Add token refresh after access token expiration. }, - useHook: () => () => { - return async function () {} + useHook: ({ fetch }) => { + const useWrappedHook: ReturnType['useHook']> = + () => { + console.log('useLogin useHook called.') + + const { revalidate } = useCustomer() + + return useCallback( + async function login(input) { + const data = await fetch({ input }) + + await revalidate() + + return data + }, + [revalidate] + ) + } + + return useWrappedHook }, } diff --git a/framework/spree/auth/use-logout.tsx b/framework/spree/auth/use-logout.tsx index 9b3fc3e44c..1c9d41fb61 100644 --- a/framework/spree/auth/use-logout.tsx +++ b/framework/spree/auth/use-logout.tsx @@ -1,17 +1,73 @@ import { MutationHook } from '@commerce/utils/types' import useLogout, { UseLogout } from '@commerce/auth/use-logout' +import type { LogoutHook } from '@commerce/types/logout' +import { useCallback } from 'react' +import useCustomer from '../customer/use-customer' +import useCart from '../cart/use-cart' +import { + ensureUserTokenResponse, + removeUserTokenResponse, +} from '../utils/tokens/user-token-response' +import revokeUserTokens from '../utils/tokens/revoke-user-tokens' +import TokensNotRejectedError from '../errors/TokensNotRejectedError' export default useLogout as UseLogout -export const handler: MutationHook = { +export const handler: MutationHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - query: '', + url: 'authentication', + query: 'revokeToken', }, - async fetcher() { + async fetcher({ input, options, fetch }) { + console.info( + 'useLogout fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + const userToken = ensureUserTokenResponse() + + if (userToken) { + try { + // Revoke any tokens associated with the logged in user. + await revokeUserTokens(fetch, { + accessToken: userToken.access_token, + refreshToken: userToken.refresh_token, + }) + } catch (revokeUserTokenError) { + // Squash token revocation errors and rethrow anything else. + if (!(revokeUserTokenError instanceof TokensNotRejectedError)) { + throw revokeUserTokenError + } + } + + // Whether token revocation succeeded or not, remove them from local storage. + removeUserTokenResponse() + } + return null }, - useHook: - ({ fetch }) => - () => - async () => {}, + useHook: ({ fetch }) => { + const useWrappedHook: ReturnType['useHook']> = + () => { + console.log('useLogout useHook called.') + + const customer = useCustomer() + const cart = useCart() + + return useCallback(async () => { + const data = await fetch() + + await customer.mutate(null, false) + await cart.mutate(null, false) + + return data + }, [customer, cart]) + } + + return useWrappedHook + }, } diff --git a/framework/spree/auth/use-signup.tsx b/framework/spree/auth/use-signup.tsx index e9ad134584..3c0ce78aaa 100644 --- a/framework/spree/auth/use-signup.tsx +++ b/framework/spree/auth/use-signup.tsx @@ -1,19 +1,88 @@ import { useCallback } from 'react' -import useCustomer from '../customer/use-customer' -import { MutationHook } from '@commerce/utils/types' +import type { GraphQLFetcherResult } from '@commerce/api' +import type { MutationHook } from '@commerce/utils/types' import useSignup, { UseSignup } from '@commerce/auth/use-signup' +import type { SignupHook } from '@commerce/types/signup' +import { ValidationError } from '@commerce/utils/errors' +import type { IAccount } from '@spree/storefront-api-v2-sdk/types/interfaces/Account' +import useCustomer from '../customer/use-customer' +import login from '../utils/login' +import type { AuthTokenAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Authentication' export default useSignup as UseSignup -export const handler: MutationHook = { +export const handler: MutationHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - query: '', + url: 'account', + query: 'create', }, - async fetcher() { + async fetcher({ input, options, fetch }) { + console.info( + 'useSignup fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + const { email, password } = input + + if (!email || !password) { + throw new ValidationError({ + message: 'Email and password need to be provided.', + }) + } + + // TODO: Replace any with specific type from Spree SDK + // once it's added to the SDK. + const createAccountParameters: any = { + user: { + email, + password, + // The stock NJC interface doesn't have a + // password confirmation field, so just copy password. + passwordConfirmation: password, + }, + } + + // Create the user account. + await fetch>({ + variables: { + methodPath: 'account.create', + arguments: [createAccountParameters], + }, + }) + + const getTokenParameters: AuthTokenAttr = { + username: email, + password, + } + + // Login immediately after the account is created. + await login(fetch, getTokenParameters) + return null }, - useHook: - ({ fetch }) => - () => - () => {}, + useHook: ({ fetch }) => { + const useWrappedHook: ReturnType['useHook']> = + () => { + console.log('useSignup useHook called.') + + const { revalidate } = useCustomer() + + return useCallback( + async (input) => { + const data = await fetch({ input }) + + await revalidate() + + return data + }, + [revalidate] + ) + } + + return useWrappedHook + }, } diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index 251bd7e6d3..631545d6b9 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -4,19 +4,24 @@ import type { MutationHook } from '@commerce/utils/types' import { useCallback } from 'react' import useCart from './use-cart' import type { AddItemHook } from '@commerce/types/cart' -import normalizeCart from '../utils/normalize-cart' +import normalizeCart from '../utils/normalizations/normalize-cart' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { AddItem } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' -import getCartToken from '../utils/get-cart-token' +import { setCartToken } from '../utils/tokens/cart-token' +import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import createEmptyCart from '@framework/utils/create-empty-cart' +import { FetcherError } from '@commerce/utils/errors' +import isLoggedIn from '@framework/utils/tokens/is-logged-in' export default useAddItem as UseAddItem export const handler: MutationHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - url: '__UNUSED__', - query: '', + url: 'cart', + query: 'addItem', }, async fetcher({ input, options, fetch }) { console.info( @@ -31,7 +36,8 @@ export const handler: MutationHook = { const safeQuantity = quantity ?? 1 - const token: IToken = { orderToken: getCartToken() } + let token: IToken | undefined = ensureIToken() + const addItemParameters: AddItem = { variant_id: variantId, quantity: safeQuantity, @@ -46,16 +52,48 @@ export const handler: MutationHook = { ].join(','), } - const { data: spreeSuccessResponse } = await fetch< - GraphQLFetcherResult - >({ - variables: { - methodPath: 'cart.addItem', - arguments: [token, addItemParameters], - }, - }) + if (!token) { + const { data: spreeCartCreateSuccessResponse } = await createEmptyCart( + fetch + ) + + setCartToken(spreeCartCreateSuccessResponse.data.attributes.token) + token = ensureIToken() + } + + try { + const { data: spreeSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ + variables: { + methodPath: 'cart.addItem', + arguments: [token, addItemParameters], + }, + }) + + return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + } catch (addItemError) { + if (addItemError instanceof FetcherError && addItemError.status === 404) { + const { data: spreeRetroactiveCartCreateSuccessResponse } = + await createEmptyCart(fetch) + + if (!isLoggedIn()) { + setCartToken( + spreeRetroactiveCartCreateSuccessResponse.data.attributes.token + ) + } - return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + // Return an empty cart. The user has to add the item again. + // This is going to be a rare situation. + + return normalizeCart( + spreeRetroactiveCartCreateSuccessResponse, + spreeRetroactiveCartCreateSuccessResponse.data + ) + } + + throw addItemError + } }, useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = @@ -72,7 +110,7 @@ export const handler: MutationHook = { return data }, - [fetch, mutate] + [mutate] ) } diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index 0cb884d313..aac7caa8fd 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -3,12 +3,15 @@ import type { SWRHook } from '@commerce/utils/types' import useCart from '@commerce/cart/use-cart' import type { UseCart } from '@commerce/cart/use-cart' import type { GetCartHook } from '@commerce/types/cart' -import normalizeCart from '../utils/normalize-cart' +import normalizeCart from '../utils/normalizations/normalize-cart' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' -import setCartToken from '../utils/set-cart-token' +import { setCartToken } from '../utils/tokens/cart-token' import { FetcherError } from '@commerce/utils/errors' +import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import isLoggedIn from '@framework/utils/tokens/is-logged-in' +import createEmptyCart from '@framework/utils/create-empty-cart' export default useCart as UseCart @@ -16,9 +19,10 @@ export default useCart as UseCart // There doesn't seem to be a good reason to call it. // So far, only @framework/bigcommerce uses it. export const handler: SWRHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - url: '__UNUSED__', - query: '', + url: 'cart', + query: 'show', }, async fetcher({ input, options, fetch }) { console.info( @@ -29,14 +33,13 @@ export const handler: SWRHook = { options ) - const { cartId: cartToken } = input let spreeCartResponse: IOrder | null - if (!cartToken) { + const token: IToken | undefined = ensureIToken() + + if (!token) { spreeCartResponse = null } else { - const spreeToken: IToken = { orderToken: cartToken } - try { const { data: spreeCartShowSuccessResponse } = await fetch< GraphQLFetcherResult @@ -44,7 +47,7 @@ export const handler: SWRHook = { variables: { methodPath: 'cart.show', arguments: [ - spreeToken, + token, { include: [ 'line_items', @@ -74,19 +77,16 @@ export const handler: SWRHook = { } if (!spreeCartResponse || spreeCartResponse?.data.attributes.completed_at) { - const { data: spreeCartCreateSuccessResponse } = await fetch< - GraphQLFetcherResult - >({ - variables: { - methodPath: 'cart.create', - arguments: [], - }, - }) + const { data: spreeCartCreateSuccessResponse } = await createEmptyCart( + fetch + ) spreeCartResponse = spreeCartCreateSuccessResponse - } - setCartToken(spreeCartResponse.data.attributes.token) + if (!isLoggedIn()) { + setCartToken(spreeCartResponse.data.attributes.token) + } + } return normalizeCart(spreeCartResponse, spreeCartResponse.data) }, diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx index 36b5ccb099..2eebe5fae7 100644 --- a/framework/spree/cart/use-remove-item.tsx +++ b/framework/spree/cart/use-remove-item.tsx @@ -1,22 +1,27 @@ -import { MutationHook } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import useRemoveItem from '@commerce/cart/use-remove-item' import type { UseRemoveItem } from '@commerce/cart/use-remove-item' import type { RemoveItemHook } from '@commerce/types/cart' import useCart from './use-cart' import { useCallback } from 'react' -import normalizeCart from '../utils/normalize-cart' +import normalizeCart from '../utils/normalizations/normalize-cart' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { GraphQLFetcherResult } from '@commerce/api' import type { IQuery } from '@spree/storefront-api-v2-sdk/types/interfaces/Query' -import getCartToken from '../utils/get-cart-token' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import createEmptyCart from '@framework/utils/create-empty-cart' +import { setCartToken } from '@framework/utils/tokens/cart-token' +import { FetcherError } from '@commerce/utils/errors' +import isLoggedIn from '@framework/utils/tokens/is-logged-in' export default useRemoveItem as UseRemoveItem export const handler: MutationHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - url: '__UNUSED__', - query: '', + url: 'cart', + query: 'removeItem', }, async fetcher({ input, options, fetch }) { console.info( @@ -29,7 +34,17 @@ export const handler: MutationHook = { const { itemId: lineItemId } = input - const token: IToken = { orderToken: getCartToken() } + let token: IToken | undefined = ensureIToken() + + if (!token) { + const { data: spreeCartCreateSuccessResponse } = await createEmptyCart( + fetch + ) + + setCartToken(spreeCartCreateSuccessResponse.data.attributes.token) + token = ensureIToken() + } + const removeItemParameters: IQuery = { include: [ 'line_items', @@ -42,16 +57,41 @@ export const handler: MutationHook = { ].join(','), } - const { data: spreeSuccessResponse } = await fetch< - GraphQLFetcherResult - >({ - variables: { - methodPath: 'cart.removeItem', - arguments: [token, lineItemId, removeItemParameters], - }, - }) + try { + const { data: spreeSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ + variables: { + methodPath: 'cart.removeItem', + arguments: [token, lineItemId, removeItemParameters], + }, + }) + + return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + } catch (removeItemError) { + if ( + removeItemError instanceof FetcherError && + removeItemError.status === 404 + ) { + const { data: spreeRetroactiveCartCreateSuccessResponse } = + await createEmptyCart(fetch) + + if (!isLoggedIn()) { + setCartToken( + spreeRetroactiveCartCreateSuccessResponse.data.attributes.token + ) + } - return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + // Return an empty cart. This is going to be a rare situation. + + return normalizeCart( + spreeRetroactiveCartCreateSuccessResponse, + spreeRetroactiveCartCreateSuccessResponse.data + ) + } + + throw removeItemError + } }, useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = @@ -64,11 +104,14 @@ export const handler: MutationHook = { async (input) => { const data = await fetch({ input: { itemId: input.id } }) - await mutate(data, false) + // Upon calling cart.removeItem, Spree returns the old version of the cart, + // with the already removed line item. Invalidate the useCart mutation + // to fetch the cart again. + await mutate(data, true) return data }, - [fetch, mutate] + [mutate] ) } diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx index e058765b86..82b239e988 100644 --- a/framework/spree/cart/use-update-item.tsx +++ b/framework/spree/cart/use-update-item.tsx @@ -2,22 +2,26 @@ import type { MutationHook } from '@commerce/utils/types' import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item' import type { UpdateItemHook } from '@commerce/types/cart' import useCart from './use-cart' -import { useCallback, useMemo } from 'react' -import { ValidationError } from '@commerce/utils/errors' +import { useMemo } from 'react' +import { FetcherError, ValidationError } from '@commerce/utils/errors' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { SetQuantity } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' -import getCartToken from '../utils/get-cart-token' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' -import normalizeCart from '../utils/normalize-cart' +import normalizeCart from '../utils/normalizations/normalize-cart' import debounce from 'lodash.debounce' +import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import createEmptyCart from '@framework/utils/create-empty-cart' +import { setCartToken } from '@framework/utils/tokens/cart-token' +import isLoggedIn from '@framework/utils/tokens/is-logged-in' export default useUpdateItem as UseUpdateItem export const handler: MutationHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - url: '__UNUSED__', - query: '', + url: 'cart', + query: 'setQuantity', }, async fetcher({ input, options, fetch }) { console.info( @@ -36,31 +40,67 @@ export const handler: MutationHook = { }) } - const token: IToken = { orderToken: getCartToken() } - const setQuantityParameters: SetQuantity = { - line_item_id: itemId, - quantity: item.quantity, - include: [ - 'line_items', - 'line_items.variant', - 'line_items.variant.product', - 'line_items.variant.product.images', - 'line_items.variant.images', - 'line_items.variant.option_values', - 'line_items.variant.product.option_types', - ].join(','), + let token: IToken | undefined = ensureIToken() + + if (!token) { + const { data: spreeCartCreateSuccessResponse } = await createEmptyCart( + fetch + ) + + setCartToken(spreeCartCreateSuccessResponse.data.attributes.token) + token = ensureIToken() } - const { data: spreeSuccessResponse } = await fetch< - GraphQLFetcherResult - >({ - variables: { - methodPath: 'cart.setQuantity', - arguments: [token, setQuantityParameters], - }, - }) + try { + const setQuantityParameters: SetQuantity = { + line_item_id: itemId, + quantity: item.quantity, + include: [ + 'line_items', + 'line_items.variant', + 'line_items.variant.product', + 'line_items.variant.product.images', + 'line_items.variant.images', + 'line_items.variant.option_values', + 'line_items.variant.product.option_types', + ].join(','), + } + + const { data: spreeSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ + variables: { + methodPath: 'cart.setQuantity', + arguments: [token, setQuantityParameters], + }, + }) + + return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + } catch (updateItemError) { + if ( + updateItemError instanceof FetcherError && + updateItemError.status === 404 + ) { + const { data: spreeRetroactiveCartCreateSuccessResponse } = + await createEmptyCart(fetch) - return normalizeCart(spreeSuccessResponse, spreeSuccessResponse.data) + if (!isLoggedIn()) { + setCartToken( + spreeRetroactiveCartCreateSuccessResponse.data.attributes.token + ) + } + + // Return an empty cart. The user has to update the item again. + // This is going to be a rare situation. + + return normalizeCart( + spreeRetroactiveCartCreateSuccessResponse, + spreeRetroactiveCartCreateSuccessResponse.data + ) + } + + throw updateItemError + } }, useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = @@ -98,7 +138,7 @@ export const handler: MutationHook = { return data }, context?.wait ?? 500), - [fetch, mutate] + [mutate, context] ) } diff --git a/framework/spree/checkout/use-checkout.tsx b/framework/spree/checkout/use-checkout.tsx index 942f85b830..dfd7fe02f5 100644 --- a/framework/spree/checkout/use-checkout.tsx +++ b/framework/spree/checkout/use-checkout.tsx @@ -4,8 +4,11 @@ import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout' export default useCheckout as UseCheckout export const handler: SWRHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - query: '', + // TODO: Revise url and query + url: 'checkout', + query: 'show', }, async fetcher({ input, options, fetch }) {}, useHook: diff --git a/framework/spree/commerce.config.json b/framework/spree/commerce.config.json index a27eb30c8f..cde5e50b7a 100644 --- a/framework/spree/commerce.config.json +++ b/framework/spree/commerce.config.json @@ -4,7 +4,7 @@ "wishlist": false, "cart": true, "search": true, - "customerAuth": false, + "customerAuth": true, "customCheckout": false } } diff --git a/framework/spree/customer/address/use-add-item.tsx b/framework/spree/customer/address/use-add-item.tsx index 2394bbd3cf..c2f645a164 100644 --- a/framework/spree/customer/address/use-add-item.tsx +++ b/framework/spree/customer/address/use-add-item.tsx @@ -5,8 +5,10 @@ import type { MutationHook } from '@commerce/utils/types' export default useAddItem as UseAddItem export const handler: MutationHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - query: '', + url: 'account', + query: 'createAddress', }, async fetcher({ input, options, fetch }) {}, useHook: diff --git a/framework/spree/customer/card/use-add-item.tsx b/framework/spree/customer/card/use-add-item.tsx index 2394bbd3cf..a8bb3cd887 100644 --- a/framework/spree/customer/card/use-add-item.tsx +++ b/framework/spree/customer/card/use-add-item.tsx @@ -5,8 +5,11 @@ import type { MutationHook } from '@commerce/utils/types' export default useAddItem as UseAddItem export const handler: MutationHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - query: '', + // TODO: Revise url and query + url: 'checkout', + query: 'addPayment', }, async fetcher({ input, options, fetch }) {}, useHook: diff --git a/framework/spree/customer/use-customer.tsx b/framework/spree/customer/use-customer.tsx index 41757cd0d5..4ca5086ddb 100644 --- a/framework/spree/customer/use-customer.tsx +++ b/framework/spree/customer/use-customer.tsx @@ -1,15 +1,85 @@ -import { SWRHook } from '@commerce/utils/types' -import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' +import type { SWRHook } from '@commerce/utils/types' +import useCustomer from '@commerce/customer/use-customer' +import type { UseCustomer } from '@commerce/customer/use-customer' +import type { CustomerHook } from '@commerce/types/customer' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import type { GraphQLFetcherResult } from '@commerce/api' +import type { IAccount } from '@spree/storefront-api-v2-sdk/types/interfaces/Account' +import { FetcherError } from '@commerce/utils/errors' +import normalizeUser from '@framework/utils/normalizations/normalize-user' +import isLoggedIn from '@framework/utils/tokens/is-logged-in' +import ensureIToken from '@framework/utils/tokens/ensure-itoken' export default useCustomer as UseCustomer -export const handler: SWRHook = { + +export const handler: SWRHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - query: '', + url: 'account', + query: 'get', }, - async fetcher({ input, options, fetch }) {}, - useHook: () => () => { - return async function addItem() { - return {} + async fetcher({ input, options, fetch }) { + console.info( + 'useCustomer fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + if (!isLoggedIn()) { + return null + } + + const token: IToken | undefined = ensureIToken() + + if (!token) { + return null } + + try { + const { data: spreeAccountInfoSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ + variables: { + methodPath: 'account.accountInfo', + arguments: [token], + }, + }) + + const spreeUser = spreeAccountInfoSuccessResponse.data + + const normalizedUser = normalizeUser( + spreeAccountInfoSuccessResponse, + spreeUser + ) + + return normalizedUser + } catch (fetchUserError) { + if ( + !(fetchUserError instanceof FetcherError) || + fetchUserError.status !== 404 + ) { + throw fetchUserError + } + + return null + } + }, + useHook: ({ useData }) => { + const useWrappedHook: ReturnType['useHook']> = ( + input + ) => { + console.log('useCustomer useHook called.') + + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input?.swrOptions, + }, + }) + } + + return useWrappedHook }, } diff --git a/framework/spree/errors/TokensNotRejectedError.ts b/framework/spree/errors/TokensNotRejectedError.ts new file mode 100644 index 0000000000..245f66414e --- /dev/null +++ b/framework/spree/errors/TokensNotRejectedError.ts @@ -0,0 +1 @@ +export default class TokensNotRejectedError extends Error {} diff --git a/framework/spree/errors/UserTokenResponseParseError.ts b/framework/spree/errors/UserTokenResponseParseError.ts new file mode 100644 index 0000000000..9631971c14 --- /dev/null +++ b/framework/spree/errors/UserTokenResponseParseError.ts @@ -0,0 +1 @@ +export default class UserTokenResponseParseError extends Error {} diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 18040da68e..6700df7edb 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -2,19 +2,20 @@ import type { Fetcher } from '@commerce/utils/types' import convertSpreeErrorToGraphQlError from './utils/convert-spree-error-to-graph-ql-error' import { makeClient } from '@spree/storefront-api-v2-sdk' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' +import type { GraphQLFetcherResult } from '@commerce/api' import { errors } from '@spree/storefront-api-v2-sdk' import { requireConfigValue } from './isomorphic-config' import getSpreeSdkMethodFromEndpointPath from './utils/get-spree-sdk-method-from-endpoint-path' import SpreeSdkMethodFromEndpointPathError from './errors/SpreeSdkMethodFromEndpointPathError' import type { + FetcherVariables, SpreeSdkResponse, SpreeSdkResponseWithRawResponse, - SpreeSdkVariables, } from './types' -import type { GraphQLFetcherResult } from '@commerce/api' import createCustomizedFetchFetcher, { fetchResponseKey, } from './utils/create-customized-fetch-fetcher' +import ensureFreshUserAccessToken from './utils/tokens/ensure-fresh-user-access-token' const client = makeClient({ host: requireConfigValue('apiHost') as string, @@ -28,12 +29,22 @@ const client = makeClient({ }, }) -const fetcher: Fetcher< - GraphQLFetcherResult, - SpreeSdkVariables -> = async (requestOptions) => { +const normalizeSpreeSuccessResponse = ( + storeResponse: ResultResponse +): GraphQLFetcherResult => { + const data = storeResponse.success() + const rawFetchResponse = data[fetchResponseKey] + + return { + data, + res: rawFetchResponse, + } +} + +const fetcher: Fetcher> = async ( + requestOptions +) => { const { url, method, variables, query } = requestOptions - const { locale, ...vars } = variables ?? {} console.log( 'Fetcher called. Configuration: ', @@ -45,28 +56,53 @@ const fetcher: Fetcher< if (!variables) { throw new SpreeSdkMethodFromEndpointPathError( - `Required SpreeSdkVariables not provided.` + `Required FetcherVariables not provided.` ) } + const { + methodPath, + arguments: args, + refreshExpiredAccessToken = true, + replayUnauthorizedRequest = true, + } = variables as FetcherVariables + + if (refreshExpiredAccessToken) { + await ensureFreshUserAccessToken(client) + } + + const spreeSdkMethod = getSpreeSdkMethodFromEndpointPath(client, methodPath) + const storeResponse: ResultResponse = - await getSpreeSdkMethodFromEndpointPath( - client, - variables.methodPath - )(...variables.arguments) + await spreeSdkMethod(...args) if (storeResponse.isSuccess()) { - const data = storeResponse.success() - const rawFetchResponse = data[fetchResponseKey] - - return { - data, - res: rawFetchResponse, - } + return normalizeSpreeSuccessResponse(storeResponse) } const storeResponseError = storeResponse.fail() + if ( + storeResponseError instanceof errors.SpreeError && + storeResponseError.serverResponse.status === 401 && + replayUnauthorizedRequest + ) { + console.info( + 'Request ended with 401. Replaying request after refreshing the user token.' + ) + + await ensureFreshUserAccessToken(client) + + const replayedStoreResponse: ResultResponse = + await spreeSdkMethod(...args) + + if (replayedStoreResponse.isSuccess()) { + return normalizeSpreeSuccessResponse(replayedStoreResponse) + } + + console.warn('Replaying the request failed', replayedStoreResponse.fail()) + } + if (storeResponseError instanceof errors.SpreeError) { throw convertSpreeErrorToGraphQlError(storeResponseError) } diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index 1895db1a93..423fa76688 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -15,6 +15,10 @@ const isomorphicConfig = { cartCookieExpire: validateCookieExpire( process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE ), + userCookieName: process.env.NEXT_PUBLIC_SPREE_USER_COOKIE_NAME, + userCookieExpire: validateCookieExpire( + process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_EXPIRE + ), imageHost: process.env.NEXT_PUBLIC_SPREE_IMAGE_HOST, categoriesTaxonomyPermalink: process.env.NEXT_PUBLIC_SPREE_CATEGORIES_TAXONOMY_PERMALINK, @@ -37,9 +41,7 @@ const isomorphicConfig = { imagesOptionFilter: validateImagesOptionFilter( process.env.NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER ), - imagesSize: validateImagesSize( - process.env.NEXT_PUBLIC_SPREE_IMAGES_SIZE - ), + imagesSize: validateImagesSize(process.env.NEXT_PUBLIC_SPREE_IMAGES_SIZE), imagesQuality: validateImagesQuality( process.env.NEXT_PUBLIC_SPREE_IMAGES_QUALITY ), @@ -53,6 +55,8 @@ export default forceIsomorphicConfigValues( 'defaultLocale', 'cartCookieName', 'cartCookieExpire', + 'userCookieName', + 'userCookieExpire', 'imageHost', 'categoriesTaxonomyPermalink', 'brandsTaxonomyPermalink', @@ -63,7 +67,7 @@ export default forceIsomorphicConfigValues( 'lineItemPlaceholderImageUrl', 'imagesOptionFilter', 'imagesSize', - 'imagesQuality' + 'imagesQuality', ] ) diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 6d72aa91d7..9ebab8462b 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -1,8 +1,8 @@ -import type { Fetcher, SWRHook } from '@commerce/utils/types' +import type { SWRHook } from '@commerce/utils/types' import useSearch from '@commerce/product/use-search' import type { Product, SearchProductsHook } from '@commerce/types/product' import type { UseSearch } from '@commerce/product/use-search' -import normalizeProduct from '../utils/normalize-product' +import normalizeProduct from '../utils/normalizations/normalize-product' import type { GraphQLFetcherResult } from '@commerce/api' import { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import { requireConfigValue } from '../isomorphic-config' @@ -18,9 +18,10 @@ const nextToSpreeSortMap: { [key: string]: string } = { } export const handler: SWRHook = { + // Provide fetchOptions for SWR cache key fetchOptions: { - url: '__UNUSED__', - query: '', + url: 'products', + query: 'list', }, async fetcher({ input, options, fetch }) { // This method is only needed if the options need to be modified before calling the generic fetcher (created in createFetcher). diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index f2455dc648..e742de5df9 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -20,17 +20,26 @@ export type SpreeSdkResponseWithRawResponse = SpreeSdkResponse & { [fetchResponseKey]: Response } -export type SpreeSdkMethodReturnType = Promise< - ResultResponse -> +export type SpreeSdkResultResponseSuccessType = SpreeSdkResponseWithRawResponse -export type SpreeSdkMethod = (...args: any[]) => SpreeSdkMethodReturnType +export type SpreeSdkMethodReturnType< + ResultResponseSuccessType extends SpreeSdkResultResponseSuccessType = SpreeSdkResultResponseSuccessType +> = Promise> + +export type SpreeSdkMethod< + ResultResponseSuccessType extends SpreeSdkResultResponseSuccessType = SpreeSdkResultResponseSuccessType +> = (...args: any[]) => SpreeSdkMethodReturnType export type SpreeSdkVariables = { methodPath: string arguments: any[] } +export type FetcherVariables = SpreeSdkVariables & { + refreshExpiredAccessToken: boolean + replayUnauthorizedRequest: boolean +} + export interface ImageStyle { url: string width: string @@ -113,3 +122,8 @@ export interface IProductsSlugs extends JsonApiListResponse { } export type ExpandedProductOption = ProductOption & { position: number } + +export type UserOAuthTokens = { + refreshToken: string + accessToken: string +} diff --git a/framework/spree/utils/convert-spree-error-to-graph-ql-error.ts b/framework/spree/utils/convert-spree-error-to-graph-ql-error.ts index 5db1a64b2b..def4920ba0 100644 --- a/framework/spree/utils/convert-spree-error-to-graph-ql-error.ts +++ b/framework/spree/utils/convert-spree-error-to-graph-ql-error.ts @@ -19,8 +19,14 @@ const convertSpreeErrorToGraphQlError = ( const fetcherErrors = Object.keys(error.errors).map((sdkErrorKey) => { const errors = error.errors[sdkErrorKey] as string[] + // Naively assume sdkErrorKey is a label. Capitalize it for a better + // out-of-the-box experience. + const capitalizedSdkErrorKey = sdkErrorKey.replace(/^\w/, (firstChar) => + firstChar.toUpperCase() + ) + return { - message: `${sdkErrorKey} ${errors.join(', ')}`, + message: `${capitalizedSdkErrorKey} ${errors.join(', ')}`, } }) diff --git a/framework/spree/utils/create-empty-cart.ts b/framework/spree/utils/create-empty-cart.ts new file mode 100644 index 0000000000..7ad512e66d --- /dev/null +++ b/framework/spree/utils/create-empty-cart.ts @@ -0,0 +1,26 @@ +import type { GraphQLFetcherResult } from '@commerce/api' +import type { HookFetcherContext } from '@commerce/utils/types' +import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import ensureIToken from './tokens/ensure-itoken' + +const createEmptyCart = ( + fetch: HookFetcherContext<{ + data: any + }>['fetch'] +): Promise> => { + const token: IToken | undefined = ensureIToken() + + return fetch>({ + variables: { + methodPath: 'cart.create', + arguments: [ + { + token, + }, + ], + }, + }) +} + +export default createEmptyCart diff --git a/framework/spree/utils/get-cart-token.ts b/framework/spree/utils/get-cart-token.ts deleted file mode 100644 index b38337cb13..0000000000 --- a/framework/spree/utils/get-cart-token.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { requireConfigValue } from '../isomorphic-config' -import Cookies from 'js-cookie' - -const getCartToken = () => - Cookies.get(requireConfigValue('cartCookieName') as string) - -export default getCartToken diff --git a/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts b/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts index cdac804612..9b87daadc8 100644 --- a/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts +++ b/framework/spree/utils/get-spree-sdk-method-from-endpoint-path.ts @@ -1,13 +1,17 @@ import type { Client } from '@spree/storefront-api-v2-sdk' import SpreeSdkMethodFromEndpointPathError from '../errors/SpreeSdkMethodFromEndpointPathError' -import { SpreeSdkMethod } from '../types' +import type { + SpreeSdkMethod, + SpreeSdkResultResponseSuccessType, +} from '../types' const getSpreeSdkMethodFromEndpointPath = < - ExactSpreeSdkClientType extends Client + ExactSpreeSdkClientType extends Client, + ResultResponseSuccessType extends SpreeSdkResultResponseSuccessType = SpreeSdkResultResponseSuccessType >( client: ExactSpreeSdkClientType, path: string -) => { +): SpreeSdkMethod => { const pathParts = path.split('.') const reachedPath: string[] = [] let node = >client diff --git a/framework/spree/utils/login.ts b/framework/spree/utils/login.ts new file mode 100644 index 0000000000..5f64e7837a --- /dev/null +++ b/framework/spree/utils/login.ts @@ -0,0 +1,54 @@ +import type { GraphQLFetcherResult } from '@commerce/api' +import type { HookFetcherContext } from '@commerce/utils/types' +import type { AuthTokenAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Authentication' +import type { AssociateCart } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' +import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' +import type { + IOAuthToken, + IToken, +} from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import { getCartToken, removeCartToken } from './tokens/cart-token' +import { setUserTokenResponse } from './tokens/user-token-response' + +const login = async ( + fetch: HookFetcherContext<{ + data: any + }>['fetch'], + getTokenParameters: AuthTokenAttr +): Promise => { + const { data: spreeGetTokenSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ + variables: { + methodPath: 'authentication.getToken', + arguments: [getTokenParameters], + }, + }) + + setUserTokenResponse(spreeGetTokenSuccessResponse) + + const cartToken = getCartToken() + + if (cartToken) { + // If the user had a cart as guest still use its contents + // after logging in. + const accessToken = spreeGetTokenSuccessResponse.access_token + const token: IToken = { bearerToken: accessToken } + + const associateGuestCartParameters: AssociateCart = { + guest_order_token: cartToken, + } + + await fetch>({ + variables: { + methodPath: 'cart.associateGuestCart', + arguments: [token, associateGuestCartParameters], + }, + }) + + // We no longer need the guest cart token, so let's remove it. + removeCartToken() + } +} + +export default login diff --git a/framework/spree/utils/normalize-cart.ts b/framework/spree/utils/normalizations/normalize-cart.ts similarity index 95% rename from framework/spree/utils/normalize-cart.ts rename to framework/spree/utils/normalizations/normalize-cart.ts index a0d7dd622b..481c3a8649 100644 --- a/framework/spree/utils/normalize-cart.ts +++ b/framework/spree/utils/normalizations/normalize-cart.ts @@ -4,19 +4,19 @@ import type { ProductVariant, SelectedOption, } from '@commerce/types/cart' -import MissingLineItemVariantError from '../errors/MissingLineItemVariantError' -import { requireConfigValue } from '../isomorphic-config' +import MissingLineItemVariantError from '../../errors/MissingLineItemVariantError' +import { requireConfigValue } from '../../isomorphic-config' import type { OrderAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' -import createGetAbsoluteImageUrl from './create-get-absolute-image-url' -import getMediaGallery from './get-media-gallery' +import createGetAbsoluteImageUrl from '../create-get-absolute-image-url' +import getMediaGallery from '../get-media-gallery' import type { LineItemAttr, OptionTypeAttr, SpreeProductImage, SpreeSdkResponse, VariantAttr, -} from '../types' +} from '../../types' import type { Image } from '@commerce/types/common' import { jsonApi } from '@spree/storefront-api-v2-sdk' diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalizations/normalize-product.ts similarity index 93% rename from framework/spree/utils/normalize-product.ts rename to framework/spree/utils/normalizations/normalize-product.ts index 6da2b6f0a1..2b8d5e7d6f 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalizations/normalize-product.ts @@ -6,13 +6,13 @@ import type { } from '@commerce/types/product' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' -import { requireConfigValue } from '../isomorphic-config' -import createGetAbsoluteImageUrl from './create-get-absolute-image-url' -import expandOptions from './expand-options' -import getMediaGallery from './get-media-gallery' -import getProductPath from './get-product-path' -import MissingPrimaryVariantError from '../errors/MissingPrimaryVariantError' -import MissingOptionValueError from '../errors/MissingOptionValueError' +import { requireConfigValue } from '../../isomorphic-config' +import createGetAbsoluteImageUrl from '../create-get-absolute-image-url' +import expandOptions from '../expand-options' +import getMediaGallery from '../get-media-gallery' +import getProductPath from '../get-product-path' +import MissingPrimaryVariantError from '../../errors/MissingPrimaryVariantError' +import MissingOptionValueError from '../../errors/MissingOptionValueError' import type { SpreeSdkResponse, VariantAttr } from '@framework/types' import { jsonApi } from '@spree/storefront-api-v2-sdk' import { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' diff --git a/framework/spree/utils/normalizations/normalize-user.ts b/framework/spree/utils/normalizations/normalize-user.ts new file mode 100644 index 0000000000..a262acab6f --- /dev/null +++ b/framework/spree/utils/normalizations/normalize-user.ts @@ -0,0 +1,16 @@ +import type { Customer } from '@commerce/types/customer' +import type { SpreeSdkResponse } from '@framework/types' +import type { AccountAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Account' + +const normalizeUser = ( + _spreeSuccessResponse: SpreeSdkResponse, + spreeUser: AccountAttr +): Customer => { + const email = spreeUser.attributes.email + + return { + email, + } +} + +export default normalizeUser diff --git a/framework/spree/utils/set-cart-token.ts b/framework/spree/utils/set-cart-token.ts deleted file mode 100644 index 7463aea69a..0000000000 --- a/framework/spree/utils/set-cart-token.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { requireConfigValue } from '../isomorphic-config' -import Cookies from 'js-cookie' - -const setCartToken = (cartToken: string) => { - const cookieOptions = { - expires: requireConfigValue('cartCookieExpire') as number, - } - - Cookies.set( - requireConfigValue('cartCookieName') as string, - cartToken, - cookieOptions - ) -} - -export default setCartToken diff --git a/framework/spree/utils/tokens/cart-token.ts b/framework/spree/utils/tokens/cart-token.ts new file mode 100644 index 0000000000..8352f9adaa --- /dev/null +++ b/framework/spree/utils/tokens/cart-token.ts @@ -0,0 +1,21 @@ +import { requireConfigValue } from '../../isomorphic-config' +import Cookies from 'js-cookie' + +export const getCartToken = () => + Cookies.get(requireConfigValue('cartCookieName') as string) + +export const setCartToken = (cartToken: string) => { + const cookieOptions = { + expires: requireConfigValue('cartCookieExpire') as number, + } + + Cookies.set( + requireConfigValue('cartCookieName') as string, + cartToken, + cookieOptions + ) +} + +export const removeCartToken = () => { + Cookies.remove(requireConfigValue('cartCookieName') as string) +} diff --git a/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts b/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts new file mode 100644 index 0000000000..ccce662706 --- /dev/null +++ b/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts @@ -0,0 +1,50 @@ +import { SpreeSdkResponseWithRawResponse } from '../../types' +import type { Client } from '@spree/storefront-api-v2-sdk' +import type { IOAuthToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import getSpreeSdkMethodFromEndpointPath from '../get-spree-sdk-method-from-endpoint-path' +import { + ensureUserTokenResponse, + removeUserTokenResponse, + setUserTokenResponse, +} from './user-token-response' + +/** + * If the user has a saved access token, make sure it's not expired + * If it is expired, attempt to refresh it. + */ +const ensureFreshUserAccessToken = async (client: Client): Promise => { + const userTokenResponse = ensureUserTokenResponse() + + if (!userTokenResponse) { + // There's no user token or it has an invalid format. + return + } + + const isAccessTokenExpired = + (userTokenResponse.created_at + userTokenResponse.expires_in) * 1000 < + Date.now() + + if (!isAccessTokenExpired) { + return + } + + const spreeRefreshAccessTokenSdkMethod = getSpreeSdkMethodFromEndpointPath< + Client, + SpreeSdkResponseWithRawResponse & IOAuthToken + >(client, 'authentication.refreshToken') + + const spreeRefreshAccessTokenResponse = + await spreeRefreshAccessTokenSdkMethod([ + { refresh_token: userTokenResponse.refresh_token }, + ]) + + if (spreeRefreshAccessTokenResponse.isFail()) { + removeUserTokenResponse() + + return + } + + setUserTokenResponse(spreeRefreshAccessTokenResponse.success()) +} + +export default ensureFreshUserAccessToken diff --git a/framework/spree/utils/tokens/ensure-itoken.ts b/framework/spree/utils/tokens/ensure-itoken.ts new file mode 100644 index 0000000000..0d4e6f8997 --- /dev/null +++ b/framework/spree/utils/tokens/ensure-itoken.ts @@ -0,0 +1,25 @@ +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import { getCartToken } from './cart-token' +import { ensureUserTokenResponse } from './user-token-response' + +const ensureIToken = (): IToken | undefined => { + const userTokenResponse = ensureUserTokenResponse() + + if (userTokenResponse) { + return { + bearerToken: userTokenResponse.access_token, + } + } + + const cartToken = getCartToken() + + if (cartToken) { + return { + orderToken: cartToken, + } + } + + return undefined +} + +export default ensureIToken diff --git a/framework/spree/utils/tokens/is-logged-in.ts b/framework/spree/utils/tokens/is-logged-in.ts new file mode 100644 index 0000000000..218c25bdd5 --- /dev/null +++ b/framework/spree/utils/tokens/is-logged-in.ts @@ -0,0 +1,9 @@ +import { ensureUserTokenResponse } from './user-token-response' + +const isLoggedIn = (): boolean => { + const userTokenResponse = ensureUserTokenResponse() + + return !!userTokenResponse +} + +export default isLoggedIn diff --git a/framework/spree/utils/tokens/revoke-user-tokens.ts b/framework/spree/utils/tokens/revoke-user-tokens.ts new file mode 100644 index 0000000000..9c69937ea5 --- /dev/null +++ b/framework/spree/utils/tokens/revoke-user-tokens.ts @@ -0,0 +1,49 @@ +import type { GraphQLFetcherResult } from '@commerce/api' +import type { HookFetcherContext } from '@commerce/utils/types' +import TokensNotRejectedError from '@framework/errors/TokensNotRejectedError' +import type { UserOAuthTokens } from '@framework/types' +import type { EmptyObjectResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/EmptyObject' + +const revokeUserTokens = async ( + fetch: HookFetcherContext<{ + data: any + }>['fetch'], + userTokens: UserOAuthTokens +): Promise => { + const spreeRevokeTokensResponses = await Promise.allSettled([ + fetch>({ + variables: { + methodPath: 'authentication.revokeToken', + arguments: [ + { + token: userTokens.refreshToken, + }, + ], + }, + }), + fetch>({ + variables: { + methodPath: 'authentication.revokeToken', + arguments: [ + { + token: userTokens.accessToken, + }, + ], + }, + }), + ]) + + const anyRejected = spreeRevokeTokensResponses.some( + (response) => response.status === 'rejected' + ) + + if (anyRejected) { + throw new TokensNotRejectedError( + 'Some tokens could not be rejected in Spree.' + ) + } + + return undefined +} + +export default revokeUserTokens diff --git a/framework/spree/utils/tokens/user-token-response.ts b/framework/spree/utils/tokens/user-token-response.ts new file mode 100644 index 0000000000..ec6ff94520 --- /dev/null +++ b/framework/spree/utils/tokens/user-token-response.ts @@ -0,0 +1,58 @@ +import { requireConfigValue } from '../../isomorphic-config' +import Cookies from 'js-cookie' +import type { IOAuthToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import UserTokenResponseParseError from '@framework/errors/UserTokenResponseParseError' + +export const getUserTokenResponse = (): IOAuthToken | undefined => { + const stringifiedToken = Cookies.get( + requireConfigValue('userCookieName') as string + ) + + if (!stringifiedToken) { + return undefined + } + + try { + const token: IOAuthToken = JSON.parse(stringifiedToken) + + return token + } catch (parseError) { + throw new UserTokenResponseParseError( + 'Could not parse stored user token response.' + ) + } +} + +/** + * Retrieves the saved user token response. If the response fails json parsing, + * removes the saved token and returns @type {undefined} instead. + */ +export const ensureUserTokenResponse = (): IOAuthToken | undefined => { + try { + return getUserTokenResponse() + } catch (error) { + if (error instanceof UserTokenResponseParseError) { + removeUserTokenResponse() + + return undefined + } + + throw error + } +} + +export const setUserTokenResponse = (token: IOAuthToken) => { + const cookieOptions = { + expires: requireConfigValue('userCookieExpire') as number, + } + + Cookies.set( + requireConfigValue('userCookieName') as string, + JSON.stringify(token), + cookieOptions + ) +} + +export const removeUserTokenResponse = () => { + Cookies.remove(requireConfigValue('userCookieName') as string) +} From cdd4758d4393d0fbf3f78d879f710e607fd4d820 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 18 Nov 2021 21:51:58 +0100 Subject: [PATCH 75/98] Fetch logged in user's cart after login or signup but associate guest cart only after signup --- framework/spree/auth/use-login.tsx | 13 +++++----- framework/spree/auth/use-signup.tsx | 11 +++++--- framework/spree/utils/login.ts | 39 ++++++++++++++++------------- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/framework/spree/auth/use-login.tsx b/framework/spree/auth/use-login.tsx index 3c6f819812..0a31076822 100644 --- a/framework/spree/auth/use-login.tsx +++ b/framework/spree/auth/use-login.tsx @@ -4,6 +4,7 @@ import type { LoginHook } from '@commerce/types/login' import { useCallback } from 'react' import type { AuthTokenAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Authentication' import useCustomer from '../customer/use-customer' +import useCart from '../cart/use-cart' import { FetcherError, ValidationError } from '@commerce/utils/errors' import login from '../utils/login' @@ -38,7 +39,7 @@ export const handler: MutationHook = { } try { - await login(fetch, getTokenParameters) + await login(fetch, getTokenParameters, false) return null } catch (getTokenError) { @@ -56,25 +57,25 @@ export const handler: MutationHook = { throw getTokenError } - - // TODO: Add token refresh after access token expiration. }, useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = () => { console.log('useLogin useHook called.') - const { revalidate } = useCustomer() + const customer = useCustomer() + const cart = useCart() return useCallback( async function login(input) { const data = await fetch({ input }) - await revalidate() + await customer.revalidate() + await cart.revalidate() return data }, - [revalidate] + [customer, cart] ) } diff --git a/framework/spree/auth/use-signup.tsx b/framework/spree/auth/use-signup.tsx index 3c0ce78aaa..f202cceeda 100644 --- a/framework/spree/auth/use-signup.tsx +++ b/framework/spree/auth/use-signup.tsx @@ -6,6 +6,7 @@ import type { SignupHook } from '@commerce/types/signup' import { ValidationError } from '@commerce/utils/errors' import type { IAccount } from '@spree/storefront-api-v2-sdk/types/interfaces/Account' import useCustomer from '../customer/use-customer' +import useCart from '../cart/use-cart' import login from '../utils/login' import type { AuthTokenAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Authentication' @@ -60,7 +61,7 @@ export const handler: MutationHook = { } // Login immediately after the account is created. - await login(fetch, getTokenParameters) + await login(fetch, getTokenParameters, true) return null }, @@ -69,17 +70,19 @@ export const handler: MutationHook = { () => { console.log('useSignup useHook called.') - const { revalidate } = useCustomer() + const customer = useCustomer() + const cart = useCart() return useCallback( async (input) => { const data = await fetch({ input }) - await revalidate() + await customer.revalidate() + await cart.revalidate() return data }, - [revalidate] + [customer, cart] ) } diff --git a/framework/spree/utils/login.ts b/framework/spree/utils/login.ts index 5f64e7837a..d2fb724a21 100644 --- a/framework/spree/utils/login.ts +++ b/framework/spree/utils/login.ts @@ -14,7 +14,8 @@ const login = async ( fetch: HookFetcherContext<{ data: any }>['fetch'], - getTokenParameters: AuthTokenAttr + getTokenParameters: AuthTokenAttr, + associateGuestCart: boolean ): Promise => { const { data: spreeGetTokenSuccessResponse } = await fetch< GraphQLFetcherResult @@ -27,27 +28,29 @@ const login = async ( setUserTokenResponse(spreeGetTokenSuccessResponse) - const cartToken = getCartToken() + if (associateGuestCart) { + const cartToken = getCartToken() - if (cartToken) { - // If the user had a cart as guest still use its contents - // after logging in. - const accessToken = spreeGetTokenSuccessResponse.access_token - const token: IToken = { bearerToken: accessToken } + if (cartToken) { + // If the user had a cart as guest still use its contents + // after logging in. + const accessToken = spreeGetTokenSuccessResponse.access_token + const token: IToken = { bearerToken: accessToken } - const associateGuestCartParameters: AssociateCart = { - guest_order_token: cartToken, - } + const associateGuestCartParameters: AssociateCart = { + guest_order_token: cartToken, + } - await fetch>({ - variables: { - methodPath: 'cart.associateGuestCart', - arguments: [token, associateGuestCartParameters], - }, - }) + await fetch>({ + variables: { + methodPath: 'cart.associateGuestCart', + arguments: [token, associateGuestCartParameters], + }, + }) - // We no longer need the guest cart token, so let's remove it. - removeCartToken() + // We no longer need the guest cart token, so let's remove it. + removeCartToken() + } } } From 160cf94ba42f2902c18f2ec6d5c4521f34177623 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 19 Nov 2021 10:01:07 +0100 Subject: [PATCH 76/98] Support Spree default wishlist show, add and remove wished items operations --- framework/spree/auth/use-login.tsx | 9 +- framework/spree/auth/use-logout.tsx | 5 +- framework/spree/auth/use-signup.tsx | 7 +- framework/spree/commerce.config.json | 2 +- framework/spree/errors/MissingProductError.ts | 1 + framework/spree/errors/MissingVariantError.ts | 1 + framework/spree/provider.ts | 8 ++ framework/spree/types/index.ts | 37 +++++- .../utils/normalizations/normalize-cart.ts | 4 +- .../utils/normalizations/normalize-product.ts | 11 +- .../utils/normalizations/normalize-user.ts | 2 +- .../normalizations/normalize-wishlist.ts | 68 ++++++++++ framework/spree/wishlist/index.ts | 3 + framework/spree/wishlist/use-add-item.tsx | 94 ++++++++++++-- framework/spree/wishlist/use-remove-item.tsx | 84 ++++++++++-- framework/spree/wishlist/use-wishlist.tsx | 122 +++++++++++++----- 16 files changed, 387 insertions(+), 71 deletions(-) create mode 100644 framework/spree/errors/MissingProductError.ts create mode 100644 framework/spree/errors/MissingVariantError.ts create mode 100644 framework/spree/utils/normalizations/normalize-wishlist.ts create mode 100644 framework/spree/wishlist/index.ts diff --git a/framework/spree/auth/use-login.tsx b/framework/spree/auth/use-login.tsx index 0a31076822..57c2b64bfc 100644 --- a/framework/spree/auth/use-login.tsx +++ b/framework/spree/auth/use-login.tsx @@ -1,11 +1,12 @@ +import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import useLogin, { UseLogin } from '@commerce/auth/use-login' import type { LoginHook } from '@commerce/types/login' -import { useCallback } from 'react' import type { AuthTokenAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Authentication' +import { FetcherError, ValidationError } from '@commerce/utils/errors' import useCustomer from '../customer/use-customer' import useCart from '../cart/use-cart' -import { FetcherError, ValidationError } from '@commerce/utils/errors' +import useWishlist from '../wishlist/use-wishlist' import login from '../utils/login' export default useLogin as UseLogin @@ -65,6 +66,7 @@ export const handler: MutationHook = { const customer = useCustomer() const cart = useCart() + const wishlist = useWishlist() return useCallback( async function login(input) { @@ -72,10 +74,11 @@ export const handler: MutationHook = { await customer.revalidate() await cart.revalidate() + await wishlist.revalidate() return data }, - [customer, cart] + [customer, cart, wishlist] ) } diff --git a/framework/spree/auth/use-logout.tsx b/framework/spree/auth/use-logout.tsx index 1c9d41fb61..ae33aeed24 100644 --- a/framework/spree/auth/use-logout.tsx +++ b/framework/spree/auth/use-logout.tsx @@ -4,6 +4,7 @@ import type { LogoutHook } from '@commerce/types/logout' import { useCallback } from 'react' import useCustomer from '../customer/use-customer' import useCart from '../cart/use-cart' +import useWishlist from '../wishlist/use-wishlist' import { ensureUserTokenResponse, removeUserTokenResponse, @@ -57,15 +58,17 @@ export const handler: MutationHook = { const customer = useCustomer() const cart = useCart() + const wishlist = useWishlist() return useCallback(async () => { const data = await fetch() await customer.mutate(null, false) await cart.mutate(null, false) + await wishlist.mutate(null, false) return data - }, [customer, cart]) + }, [customer, cart, wishlist]) } return useWrappedHook diff --git a/framework/spree/auth/use-signup.tsx b/framework/spree/auth/use-signup.tsx index f202cceeda..b8385339cd 100644 --- a/framework/spree/auth/use-signup.tsx +++ b/framework/spree/auth/use-signup.tsx @@ -5,10 +5,11 @@ import useSignup, { UseSignup } from '@commerce/auth/use-signup' import type { SignupHook } from '@commerce/types/signup' import { ValidationError } from '@commerce/utils/errors' import type { IAccount } from '@spree/storefront-api-v2-sdk/types/interfaces/Account' +import type { AuthTokenAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Authentication' import useCustomer from '../customer/use-customer' import useCart from '../cart/use-cart' +import useWishlist from '../wishlist/use-wishlist' import login from '../utils/login' -import type { AuthTokenAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Authentication' export default useSignup as UseSignup @@ -72,6 +73,7 @@ export const handler: MutationHook = { const customer = useCustomer() const cart = useCart() + const wishlist = useWishlist() return useCallback( async (input) => { @@ -79,10 +81,11 @@ export const handler: MutationHook = { await customer.revalidate() await cart.revalidate() + await wishlist.revalidate() return data }, - [customer, cart] + [customer, cart, wishlist] ) } diff --git a/framework/spree/commerce.config.json b/framework/spree/commerce.config.json index cde5e50b7a..6f8399fb50 100644 --- a/framework/spree/commerce.config.json +++ b/framework/spree/commerce.config.json @@ -1,7 +1,7 @@ { "provider": "spree", "features": { - "wishlist": false, + "wishlist": true, "cart": true, "search": true, "customerAuth": true, diff --git a/framework/spree/errors/MissingProductError.ts b/framework/spree/errors/MissingProductError.ts new file mode 100644 index 0000000000..3098be6890 --- /dev/null +++ b/framework/spree/errors/MissingProductError.ts @@ -0,0 +1 @@ +export default class MissingProductError extends Error {} diff --git a/framework/spree/errors/MissingVariantError.ts b/framework/spree/errors/MissingVariantError.ts new file mode 100644 index 0000000000..5ed9e0ed24 --- /dev/null +++ b/framework/spree/errors/MissingVariantError.ts @@ -0,0 +1 @@ +export default class MissingVariantError extends Error {} diff --git a/framework/spree/provider.ts b/framework/spree/provider.ts index 4adda3bbd9..de6ddb2077 100644 --- a/framework/spree/provider.ts +++ b/framework/spree/provider.ts @@ -9,6 +9,9 @@ import { handler as useLogin } from './auth/use-login' import { handler as useLogout } from './auth/use-logout' import { handler as useSignup } from './auth/use-signup' import { handler as useCheckout } from './checkout/use-checkout' +import { handler as useWishlist } from './wishlist/use-wishlist' +import { handler as useWishlistAddItem } from './wishlist/use-add-item' +import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item' import { requireConfigValue } from './isomorphic-config' const spreeProvider = { @@ -20,6 +23,11 @@ const spreeProvider = { products: { useSearch }, auth: { useLogin, useLogout, useSignup }, checkout: { useCheckout }, + wishlist: { + useWishlist, + useAddItem: useWishlistAddItem, + useRemoveItem: useWishlistRemoveItem, + }, } export { spreeProvider } diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index e742de5df9..9ea717727a 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -6,7 +6,13 @@ import type { } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { Response } from '@vercel/fetch' -import type { ProductOption } from '@commerce/types/product' +import type { ProductOption, Product } from '@commerce/types/product' +import type { + AddItemHook, + RemoveItemHook, + WishlistItemBody, + WishlistTypes, +} from '@commerce/types/wishlist' export type UnknownObjectValues = Record @@ -127,3 +133,32 @@ export type UserOAuthTokens = { refreshToken: string accessToken: string } + +// TODO: ExplicitCommerceWishlist is a temporary type +// derived from tsx views. It will be removed once +// Wishlist in @commerce/types/wishlist is updated +// to a more specific type than `any`. +export type ExplicitCommerceWishlist = { + id: string + token: string + items: { + id: string + product_id: number + variant_id: number + product: Product + }[] +} + +export type ExplicitWishlistAddItemHook = AddItemHook< + WishlistTypes & { + wishlist: ExplicitCommerceWishlist + itemBody: WishlistItemBody & { + wishlistToken?: string + } + } +> + +export type ExplicitWishlistRemoveItemHook = RemoveItemHook & { + fetcherInput: { wishlistToken?: string } + body: { wishlistToken?: string } +} diff --git a/framework/spree/utils/normalizations/normalize-cart.ts b/framework/spree/utils/normalizations/normalize-cart.ts index 481c3a8649..a1751eaecc 100644 --- a/framework/spree/utils/normalizations/normalize-cart.ts +++ b/framework/spree/utils/normalizations/normalize-cart.ts @@ -8,6 +8,8 @@ import MissingLineItemVariantError from '../../errors/MissingLineItemVariantErro import { requireConfigValue } from '../../isomorphic-config' import type { OrderAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import type { Image } from '@commerce/types/common' +import { jsonApi } from '@spree/storefront-api-v2-sdk' import createGetAbsoluteImageUrl from '../create-get-absolute-image-url' import getMediaGallery from '../get-media-gallery' import type { @@ -17,8 +19,6 @@ import type { SpreeSdkResponse, VariantAttr, } from '../../types' -import type { Image } from '@commerce/types/common' -import { jsonApi } from '@spree/storefront-api-v2-sdk' const placeholderImage = requireConfigValue('lineItemPlaceholderImageUrl') as | string diff --git a/framework/spree/utils/normalizations/normalize-product.ts b/framework/spree/utils/normalizations/normalize-product.ts index 2b8d5e7d6f..e70bd34b46 100644 --- a/framework/spree/utils/normalizations/normalize-product.ts +++ b/framework/spree/utils/normalizations/normalize-product.ts @@ -6,6 +6,8 @@ import type { } from '@commerce/types/product' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' +import { jsonApi } from '@spree/storefront-api-v2-sdk' +import { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import { requireConfigValue } from '../../isomorphic-config' import createGetAbsoluteImageUrl from '../create-get-absolute-image-url' import expandOptions from '../expand-options' @@ -13,10 +15,11 @@ import getMediaGallery from '../get-media-gallery' import getProductPath from '../get-product-path' import MissingPrimaryVariantError from '../../errors/MissingPrimaryVariantError' import MissingOptionValueError from '../../errors/MissingOptionValueError' -import type { SpreeSdkResponse, VariantAttr } from '@framework/types' -import { jsonApi } from '@spree/storefront-api-v2-sdk' -import { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' -import type { ExpandedProductOption } from '@framework/types' +import type { + ExpandedProductOption, + SpreeSdkResponse, + VariantAttr, +} from '../../types' const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as | string diff --git a/framework/spree/utils/normalizations/normalize-user.ts b/framework/spree/utils/normalizations/normalize-user.ts index a262acab6f..897b1c59b7 100644 --- a/framework/spree/utils/normalizations/normalize-user.ts +++ b/framework/spree/utils/normalizations/normalize-user.ts @@ -1,6 +1,6 @@ import type { Customer } from '@commerce/types/customer' -import type { SpreeSdkResponse } from '@framework/types' import type { AccountAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Account' +import type { SpreeSdkResponse } from '../../types' const normalizeUser = ( _spreeSuccessResponse: SpreeSdkResponse, diff --git a/framework/spree/utils/normalizations/normalize-wishlist.ts b/framework/spree/utils/normalizations/normalize-wishlist.ts new file mode 100644 index 0000000000..94768ba569 --- /dev/null +++ b/framework/spree/utils/normalizations/normalize-wishlist.ts @@ -0,0 +1,68 @@ +import MissingProductError from '@framework/errors/MissingProductError' +import MissingVariantError from '@framework/errors/MissingVariantError' +import { jsonApi } from '@spree/storefront-api-v2-sdk' +import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' +import type { WishedItemAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/WishedItem' +import type { WishlistAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Wishlist' +import type { + ExplicitCommerceWishlist, + SpreeSdkResponse, + VariantAttr, +} from '../../types' +import normalizeProduct from './normalize-product' + +const normalizeWishlist = ( + spreeSuccessResponse: SpreeSdkResponse, + spreeWishlist: WishlistAttr +): ExplicitCommerceWishlist => { + const spreeWishedItems = jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeWishlist, + 'wished_items' + ) + + const items: ExplicitCommerceWishlist['items'] = spreeWishedItems.map( + (spreeWishedItem) => { + const spreeWishedVariant = + jsonApi.findSingleRelationshipDocument( + spreeSuccessResponse, + spreeWishedItem, + 'variant' + ) + + if (spreeWishedVariant === null) { + throw new MissingVariantError( + `Couldn't find variant for wished item with id ${spreeWishedItem.id}.` + ) + } + + const spreeWishedProduct = + jsonApi.findSingleRelationshipDocument( + spreeSuccessResponse, + spreeWishedVariant, + 'product' + ) + + if (spreeWishedProduct === null) { + throw new MissingProductError( + `Couldn't find product for variant with id ${spreeWishedVariant.id}.` + ) + } + + return { + id: spreeWishedItem.id, + product_id: parseInt(spreeWishedProduct.id, 10), + variant_id: parseInt(spreeWishedVariant.id, 10), + product: normalizeProduct(spreeSuccessResponse, spreeWishedProduct), + } + } + ) + + return { + id: spreeWishlist.id, + token: spreeWishlist.attributes.token, + items, + } +} + +export default normalizeWishlist diff --git a/framework/spree/wishlist/index.ts b/framework/spree/wishlist/index.ts new file mode 100644 index 0000000000..241af3c7e4 --- /dev/null +++ b/framework/spree/wishlist/index.ts @@ -0,0 +1,3 @@ +export { default as useAddItem } from './use-add-item' +export { default as useWishlist } from './use-wishlist' +export { default as useRemoveItem } from './use-remove-item' diff --git a/framework/spree/wishlist/use-add-item.tsx b/framework/spree/wishlist/use-add-item.tsx index 75f067c3a3..86a51f8cbd 100644 --- a/framework/spree/wishlist/use-add-item.tsx +++ b/framework/spree/wishlist/use-add-item.tsx @@ -1,13 +1,89 @@ import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import useAddItem from '@commerce/wishlist/use-add-item' +import type { UseAddItem } from '@commerce/wishlist/use-add-item' +import useWishlist from './use-wishlist' +import type { ExplicitWishlistAddItemHook } from '@framework/types' +import type { + WishedItem, + WishlistsAddWishedItem, +} from '@spree/storefront-api-v2-sdk/types/interfaces/WishedItem' +import type { GraphQLFetcherResult } from '@commerce/api' +import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import type { AddItemHook } from '@commerce/types/wishlist' +import isLoggedIn from '@framework/utils/tokens/is-logged-in' -export function emptyHook() { - const useEmptyHook = async (options = {}) => { - return useCallback(async function () { - return Promise.resolve() - }, []) - } +export default useAddItem as UseAddItem - return useEmptyHook -} +export const handler: MutationHook = { + fetchOptions: { + url: 'wishlists', + query: 'addWishedItem', + }, + async fetcher({ input, options, fetch }) { + console.info( + 'useAddItem (wishlist) fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + const { + item: { productId, variantId, wishlistToken }, + } = input + + if (!isLoggedIn() || !wishlistToken) { + return null + } + + let token: IToken | undefined = ensureIToken() + + const addItemParameters: WishlistsAddWishedItem = { + variant_id: `${variantId}`, + quantity: 1, + } + + await fetch>({ + variables: { + methodPath: 'wishlists.addWishedItem', + arguments: [token, wishlistToken, addItemParameters], + }, + }) + + return null + }, + useHook: ({ fetch }) => { + const useWrappedHook: ReturnType['useHook']> = + () => { + console.log('useAddItem (wishlist) useHook called.') -export default emptyHook + const wishlist = useWishlist() + + return useCallback( + async (item) => { + if (!wishlist.data) { + return null + } + + const data = await fetch({ + input: { + item: { + ...item, + wishlistToken: wishlist.data.token, + }, + }, + }) + + await wishlist.revalidate() + + return data + }, + [wishlist] + ) + } + + return useWrappedHook + }, +} diff --git a/framework/spree/wishlist/use-remove-item.tsx b/framework/spree/wishlist/use-remove-item.tsx index a2d3a8a052..c2074e18b7 100644 --- a/framework/spree/wishlist/use-remove-item.tsx +++ b/framework/spree/wishlist/use-remove-item.tsx @@ -1,17 +1,77 @@ import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import useRemoveItem from '@commerce/wishlist/use-remove-item' +import type { UseRemoveItem } from '@commerce/wishlist/use-remove-item' +import useWishlist from './use-wishlist' +import type { ExplicitWishlistRemoveItemHook } from '../types' +import isLoggedIn from '@framework/utils/tokens/is-logged-in' +import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import type { GraphQLFetcherResult } from '@commerce/api' +import type { WishedItem } from '@spree/storefront-api-v2-sdk/types/interfaces/WishedItem' -type Options = { - includeProducts?: boolean -} +export default useRemoveItem as UseRemoveItem -export function emptyHook(options?: Options) { - const useEmptyHook = async ({ id }: { id: string | number }) => { - return useCallback(async function () { - return Promise.resolve() - }, []) - } +export const handler: MutationHook = { + fetchOptions: { + url: 'wishlists', + query: 'removeWishedItem', + }, + async fetcher({ input, options, fetch }) { + console.info( + 'useRemoveItem (wishlist) fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) - return useEmptyHook -} + const { itemId, wishlistToken } = input + + if (!isLoggedIn() || !wishlistToken) { + return null + } + + let token: IToken | undefined = ensureIToken() + + await fetch>({ + variables: { + methodPath: 'wishlists.removeWishedItem', + arguments: [token, wishlistToken, itemId], + }, + }) + + return null + }, + useHook: ({ fetch }) => { + const useWrappedHook: ReturnType< + MutationHook['useHook'] + > = () => { + console.log('useRemoveItem (wishlist) useHook called.') -export default emptyHook + const wishlist = useWishlist() + + return useCallback( + async (input) => { + if (!wishlist.data) { + return null + } + + const data = await fetch({ + input: { + itemId: String(input.id), + wishlistToken: wishlist.data.token, + }, + }) + + await wishlist.revalidate() + + return data + }, + [wishlist] + ) + } + + return useWrappedHook + }, +} diff --git a/framework/spree/wishlist/use-wishlist.tsx b/framework/spree/wishlist/use-wishlist.tsx index 9fe0e758f3..7c2e7ad8ba 100644 --- a/framework/spree/wishlist/use-wishlist.tsx +++ b/framework/spree/wishlist/use-wishlist.tsx @@ -1,43 +1,95 @@ -import { HookFetcher } from '@commerce/utils/types' -import type { Product } from '@commerce/types/product' - -const defaultOpts = {} - -export type Wishlist = { - items: [ - { - product_id: number - variant_id: number - id: number - product: Product +import { useMemo } from 'react' +import type { SWRHook } from '@commerce/utils/types' +import useWishlist from '@commerce/wishlist/use-wishlist' +import type { UseWishlist } from '@commerce/wishlist/use-wishlist' +import type { GetWishlistHook } from '@commerce/types/wishlist' +import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' +import type { GraphQLFetcherResult } from '@commerce/api' +import type { Wishlist } from '@spree/storefront-api-v2-sdk/types/interfaces/Wishlist' +import ensureIToken from '../utils/tokens/ensure-itoken' +import normalizeWishlist from '../utils/normalizations/normalize-wishlist' +import isLoggedIn from '@framework/utils/tokens/is-logged-in' + +export default useWishlist as UseWishlist + +export const handler: SWRHook = { + // Provide fetchOptions for SWR cache key + fetchOptions: { + url: 'wishlists', + query: 'default', + }, + async fetcher({ input, options, fetch }) { + console.info( + 'useWishlist fetcher called. Configuration: ', + 'input: ', + input, + 'options: ', + options + ) + + if (!isLoggedIn()) { + return null } - ] -} -export interface UseWishlistOptions { - includeProducts?: boolean -} + // TODO: Optimize with includeProducts. -export interface UseWishlistInput extends UseWishlistOptions { - customerId?: number -} + const token: IToken | undefined = ensureIToken() -export const fetcher: HookFetcher = () => { - return null -} + const { data: spreeWishlistsDefaultSuccessResponse } = await fetch< + GraphQLFetcherResult + >({ + variables: { + methodPath: 'wishlists.default', + arguments: [ + token, + { + include: [ + 'wished_items', + 'wished_items.variant', + 'wished_items.variant.product', + 'wished_items.variant.product.primary_variant', + 'wished_items.variant.product.images', + 'wished_items.variant.product.option_types', + 'wished_items.variant.product.variants', + 'wished_items.variant.product.variants.option_values', + ].join(','), + }, + ], + }, + }) -export function extendHook( - customFetcher: typeof fetcher, - // swrOptions?: SwrOptions - swrOptions?: any -) { - const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { - return { data: null } - } + return normalizeWishlist( + spreeWishlistsDefaultSuccessResponse, + spreeWishlistsDefaultSuccessResponse.data + ) + }, + useHook: ({ useData }) => { + const useWrappedHook: ReturnType['useHook']> = ( + input + ) => { + console.log('useWishlist useHook called.') - useWishlist.extend = extendHook + const response = useData({ + swrOptions: { + revalidateOnFocus: false, + ...input?.swrOptions, + }, + }) - return useWishlist -} + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.items?.length || 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + } -export default extendHook(fetcher) + return useWrappedHook + }, +} From 1a0b5e245a7f8ff44eda7770eb24d31ca81c0ae2 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 19 Nov 2021 16:10:59 +0100 Subject: [PATCH 77/98] Fetch Spree CMS Pages --- .../spree/api/operations/get-all-pages.ts | 57 +++++++++++++++++-- framework/spree/api/operations/get-page.ts | 51 +++++++++++++++-- .../utils/normalizations/normalize-page.ts | 42 ++++++++++++++ 3 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 framework/spree/utils/normalizations/normalize-page.ts diff --git a/framework/spree/api/operations/get-all-pages.ts b/framework/spree/api/operations/get-all-pages.ts index 4ff56b8e8e..9ed69b9035 100644 --- a/framework/spree/api/operations/get-all-pages.ts +++ b/framework/spree/api/operations/get-all-pages.ts @@ -1,6 +1,12 @@ -export type Page = { url: string } -import { OperationContext, OperationOptions } from '@commerce/api/operations' -import { GetAllPagesOperation } from '@commerce/types/page' +import type { + OperationContext, + OperationOptions, +} from '@commerce/api/operations' +import type { GetAllPagesOperation, Page } from '@commerce/types/page' +import { requireConfigValue } from '@framework/isomorphic-config' +import normalizePage from '@framework/utils/normalizations/normalize-page' +import type { IPages } from '@spree/storefront-api-v2-sdk/types/interfaces/Page' +import type { SpreeSdkVariables } from '../../types' import type { SpreeApiConfig, SpreeApiProvider } from '../index' export default function getAllPagesOperation({ @@ -19,18 +25,57 @@ export default function getAllPagesOperation({ ): Promise async function getAllPages({ - config, + config: userConfig, preview, query, + url, }: { url?: string config?: Partial preview?: boolean query?: string } = {}): Promise { - return { - pages: [], + console.info( + 'getAllPages called. Configuration: ', + 'query: ', + query, + 'userConfig: ', + userConfig, + 'preview: ', + preview, + 'url: ', + url + ) + + const config = commerce.getConfig(userConfig) + const { fetch: apiFetch } = config + + const variables: SpreeSdkVariables = { + methodPath: 'pages.list', + arguments: [ + { + per_page: 500, + filter: { + locale_eq: + config.locale || (requireConfigValue('defaultLocale') as string), + }, + }, + ], } + + const { data: spreeSuccessResponse } = await apiFetch< + IPages, + SpreeSdkVariables + >('__UNUSED__', { + variables, + }) + + const normalizedPages: Page[] = spreeSuccessResponse.data.map( + (spreePage) => + normalizePage(spreeSuccessResponse, spreePage, config.locales || []) + ) + + return { pages: normalizedPages } } return getAllPages diff --git a/framework/spree/api/operations/get-page.ts b/framework/spree/api/operations/get-page.ts index 2bfbb330af..c613729ae3 100644 --- a/framework/spree/api/operations/get-page.ts +++ b/framework/spree/api/operations/get-page.ts @@ -1,6 +1,12 @@ -import type { OperationOptions } from '@commerce/api/operations' +import type { + OperationContext, + OperationOptions, +} from '@commerce/api/operations' import type { GetPageOperation } from '@commerce/types/page' -import type { SpreeApiConfig } from '..' +import type { SpreeSdkVariables } from '../../types' +import type { SpreeApiConfig, SpreeApiProvider } from '..' +import type { IPage } from '@spree/storefront-api-v2-sdk/types/interfaces/Page' +import normalizePage from '@framework/utils/normalizations/normalize-page' export type Page = any export type GetPageResult = { page?: Page } @@ -9,7 +15,9 @@ export type PageVariables = { id: number } -export default function getPageOperation() { +export default function getPageOperation({ + commerce, +}: OperationContext) { async function getPage(opts: { variables: T['variables'] config?: Partial @@ -26,16 +34,47 @@ export default function getPageOperation() { async function getPage({ url, - variables, - config, + config: userConfig, preview, + variables: getPageVariables, }: { url?: string variables: T['variables'] config?: Partial preview?: boolean }): Promise { - return Promise.resolve({}) + console.info( + 'getPage called. Configuration: ', + 'userConfig: ', + userConfig, + 'preview: ', + preview, + 'url: ', + url + ) + + const config = commerce.getConfig(userConfig) + const { fetch: apiFetch } = config + + const variables: SpreeSdkVariables = { + methodPath: 'pages.show', + arguments: [getPageVariables.id], + } + + const { data: spreeSuccessResponse } = await apiFetch< + IPage, + SpreeSdkVariables + >('__UNUSED__', { + variables, + }) + + const normalizedPage: Page = normalizePage( + spreeSuccessResponse, + spreeSuccessResponse.data, + config.locales || [] + ) + + return { page: normalizedPage } } return getPage diff --git a/framework/spree/utils/normalizations/normalize-page.ts b/framework/spree/utils/normalizations/normalize-page.ts new file mode 100644 index 0000000000..c49d862d1a --- /dev/null +++ b/framework/spree/utils/normalizations/normalize-page.ts @@ -0,0 +1,42 @@ +import { Page } from '@commerce/types/page' +import type { PageAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Page' +import { SpreeSdkResponse } from '../../types' + +const normalizePage = ( + _spreeSuccessResponse: SpreeSdkResponse, + spreePage: PageAttr, + commerceLocales: string[] +): Page => { + // If the locale returned by Spree is not available, search + // for a similar one. + + const spreeLocale = spreePage.attributes.locale + let usedCommerceLocale: string + + if (commerceLocales.includes(spreeLocale)) { + usedCommerceLocale = spreeLocale + } else { + const genericSpreeLocale = spreeLocale.split('-')[0] + + const foundExactGenericLocale = commerceLocales.includes(genericSpreeLocale) + + if (foundExactGenericLocale) { + usedCommerceLocale = genericSpreeLocale + } else { + const foundSimilarLocale = commerceLocales.find((locale) => { + return locale.split('-')[0] === genericSpreeLocale + }) + + usedCommerceLocale = foundSimilarLocale || spreeLocale + } + } + + return { + id: spreePage.id, + name: spreePage.attributes.title, + url: `/${usedCommerceLocale}/${spreePage.attributes.slug}`, + body: spreePage.attributes.content, + } +} + +export default normalizePage From 55da90a9d25db63b3f9c7e638b5e37f6a7350d17 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 19 Nov 2021 18:29:24 +0100 Subject: [PATCH 78/98] Fix login, handle critical token errors and fix WishlistCard Fix to WishlistCard changes its props to be consistent with WishlistButton when calling useRemoveItem --- framework/spree/auth/use-logout.tsx | 12 ++++-- framework/spree/errors/AccessTokenError.ts | 1 + framework/spree/errors/RefreshTokenError.ts | 1 + framework/spree/fetcher.ts | 5 +++ framework/spree/index.tsx | 40 ++++++++++++++++++- framework/spree/utils/handle-token-errors.ts | 14 +++++++ framework/spree/utils/login.ts | 3 +- .../tokens/ensure-fresh-user-access-token.ts | 3 +- framework/spree/wishlist/use-remove-item.tsx | 2 +- 9 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 framework/spree/errors/AccessTokenError.ts create mode 100644 framework/spree/errors/RefreshTokenError.ts create mode 100644 framework/spree/utils/handle-token-errors.ts diff --git a/framework/spree/auth/use-logout.tsx b/framework/spree/auth/use-logout.tsx index ae33aeed24..5fc19218d4 100644 --- a/framework/spree/auth/use-logout.tsx +++ b/framework/spree/auth/use-logout.tsx @@ -56,9 +56,15 @@ export const handler: MutationHook = { () => { console.log('useLogout useHook called.') - const customer = useCustomer() - const cart = useCart() - const wishlist = useWishlist() + const customer = useCustomer({ + swrOptions: { isPaused: () => true }, + }) + const cart = useCart({ + swrOptions: { isPaused: () => true }, + }) + const wishlist = useWishlist({ + swrOptions: { isPaused: () => true }, + }) return useCallback(async () => { const data = await fetch() diff --git a/framework/spree/errors/AccessTokenError.ts b/framework/spree/errors/AccessTokenError.ts new file mode 100644 index 0000000000..4c79c0be80 --- /dev/null +++ b/framework/spree/errors/AccessTokenError.ts @@ -0,0 +1 @@ +export default class AccessTokenError extends Error {} diff --git a/framework/spree/errors/RefreshTokenError.ts b/framework/spree/errors/RefreshTokenError.ts new file mode 100644 index 0000000000..a79365bbbe --- /dev/null +++ b/framework/spree/errors/RefreshTokenError.ts @@ -0,0 +1 @@ +export default class RefreshTokenError extends Error {} diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index 6700df7edb..c8cbfaec58 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -16,6 +16,7 @@ import createCustomizedFetchFetcher, { fetchResponseKey, } from './utils/create-customized-fetch-fetcher' import ensureFreshUserAccessToken from './utils/tokens/ensure-fresh-user-access-token' +import RefreshTokenError from './errors/RefreshTokenError' const client = makeClient({ host: requireConfigValue('apiHost') as string, @@ -101,6 +102,10 @@ const fetcher: Fetcher> = async ( } console.warn('Replaying the request failed', replayedStoreResponse.fail()) + + throw new RefreshTokenError( + 'Could not authorize request with current access token.' + ) } if (storeResponseError instanceof errors.SpreeError) { diff --git a/framework/spree/index.tsx b/framework/spree/index.tsx index d7d34dbe3a..f7eff69e9c 100644 --- a/framework/spree/index.tsx +++ b/framework/spree/index.tsx @@ -1,10 +1,48 @@ -import { getCommerceProvider, useCommerce as useCoreCommerce } from '@commerce' +import type { ComponentType, FunctionComponent } from 'react' +import { + Provider, + CommerceProviderProps, + CoreCommerceProvider, + useCommerce as useCoreCommerce, +} from '@commerce' import { spreeProvider } from './provider' import type { SpreeProvider } from './provider' +import { SWRConfig } from 'swr' +import handleTokenErrors from './utils/handle-token-errors' +import useLogout from '@commerce/auth/use-logout' export { spreeProvider } export type { SpreeProvider } +export const WithTokenErrorsHandling: FunctionComponent = ({ children }) => { + const logout = useLogout() + + return ( + { + handleTokenErrors(error, () => void logout()) + }, + }} + > + {children} + + ) +} + +export const getCommerceProvider =

(provider: P) => { + return function CommerceProvider({ + children, + ...props + }: CommerceProviderProps) { + return ( + + {children} + + ) + } +} + export const CommerceProvider = getCommerceProvider(spreeProvider) diff --git a/framework/spree/utils/handle-token-errors.ts b/framework/spree/utils/handle-token-errors.ts new file mode 100644 index 0000000000..a5d49fde6b --- /dev/null +++ b/framework/spree/utils/handle-token-errors.ts @@ -0,0 +1,14 @@ +import AccessTokenError from '../errors/AccessTokenError' +import RefreshTokenError from '../errors/RefreshTokenError' + +const handleTokenErrors = (error: unknown, action: () => void): boolean => { + if (error instanceof AccessTokenError || error instanceof RefreshTokenError) { + action() + + return true + } + + return false +} + +export default handleTokenErrors diff --git a/framework/spree/utils/login.ts b/framework/spree/utils/login.ts index d2fb724a21..3894b79526 100644 --- a/framework/spree/utils/login.ts +++ b/framework/spree/utils/login.ts @@ -49,9 +49,10 @@ const login = async ( }) // We no longer need the guest cart token, so let's remove it. - removeCartToken() } } + + removeCartToken() } export default login diff --git a/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts b/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts index ccce662706..b321b64385 100644 --- a/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts +++ b/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts @@ -7,6 +7,7 @@ import { removeUserTokenResponse, setUserTokenResponse, } from './user-token-response' +import AccessTokenError from '@framework/errors/AccessTokenError' /** * If the user has a saved access token, make sure it's not expired @@ -41,7 +42,7 @@ const ensureFreshUserAccessToken = async (client: Client): Promise => { if (spreeRefreshAccessTokenResponse.isFail()) { removeUserTokenResponse() - return + throw new AccessTokenError('Could not refresh access token.') } setUserTokenResponse(spreeRefreshAccessTokenResponse.success()) diff --git a/framework/spree/wishlist/use-remove-item.tsx b/framework/spree/wishlist/use-remove-item.tsx index c2074e18b7..59986e436b 100644 --- a/framework/spree/wishlist/use-remove-item.tsx +++ b/framework/spree/wishlist/use-remove-item.tsx @@ -59,7 +59,7 @@ export const handler: MutationHook = { const data = await fetch({ input: { - itemId: String(input.id), + itemId: `${input.id}`, wishlistToken: wishlist.data.token, }, }) From 82509306ec333e09fe1e311c6827e4eb778d5a3b Mon Sep 17 00:00:00 2001 From: Grey <57859708+greyhere@users.noreply.github.com> Date: Thu, 18 Nov 2021 20:28:30 +0530 Subject: [PATCH 79/98] Fix variable name (#574) Variable name should be `ChevronRight` --- components/icons/ChevronRight.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/icons/ChevronRight.tsx b/components/icons/ChevronRight.tsx index d72b77a32c..8da0122caf 100644 --- a/components/icons/ChevronRight.tsx +++ b/components/icons/ChevronRight.tsx @@ -1,4 +1,4 @@ -const ChevronUp = ({ ...props }) => { +const ChevronRight = ({ ...props }) => { return ( { ) } -export default ChevronUp +export default ChevronRight From 8618a034123d59e1beea2dfc26422828f4c9f513 Mon Sep 17 00:00:00 2001 From: pfcodes Date: Fri, 19 Nov 2021 07:59:23 -0800 Subject: [PATCH 80/98] Update get-cart.ts (#474) include digital items Co-authored-by: Gonzalo Pozzo --- framework/bigcommerce/api/endpoints/cart/get-cart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/bigcommerce/api/endpoints/cart/get-cart.ts b/framework/bigcommerce/api/endpoints/cart/get-cart.ts index d3bb309e2f..b6a7d88e98 100644 --- a/framework/bigcommerce/api/endpoints/cart/get-cart.ts +++ b/framework/bigcommerce/api/endpoints/cart/get-cart.ts @@ -15,7 +15,7 @@ const getCart: CartEndpoint['handlers']['getCart'] = async ({ if (cartId) { try { result = await config.storeApiFetch( - `/v3/carts/${cartId}?include=line_items.physical_items.options` + `/v3/carts/${cartId}?include=line_items.physical_items.options,line_items.digital_items.options` ) } catch (error) { if (error instanceof BigcommerceApiError && error.status === 404) { From ea9c126b94292f9ddac30fa20cf9fc83f2903152 Mon Sep 17 00:00:00 2001 From: pfcodes Date: Fri, 19 Nov 2021 08:01:31 -0800 Subject: [PATCH 81/98] Update normalize.ts (#475) add missing options property to `normalizeLineItem` Co-authored-by: Gonzalo Pozzo --- framework/bigcommerce/lib/normalize.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index fee1fbcc39..5cdda49d79 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -122,6 +122,7 @@ function normalizeLineItem(item: any): LineItem { price: item.sale_price, listPrice: item.list_price, }, + options: item.options, path: item.url.split('/')[3], discounts: item.discounts.map((discount: any) => ({ value: discount.discounted_amount, From 3ff49fed2c4bb4bcd942f2ceafd03629b1be644a Mon Sep 17 00:00:00 2001 From: pfcodes Date: Fri, 19 Nov 2021 08:01:51 -0800 Subject: [PATCH 82/98] Update add-item.ts (#473) * Update add-item.ts include digital items * Update add-item.ts include digital items Co-authored-by: Gonzalo Pozzo --- framework/bigcommerce/api/endpoints/cart/add-item.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/bigcommerce/api/endpoints/cart/add-item.ts b/framework/bigcommerce/api/endpoints/cart/add-item.ts index 52ef1223d9..d2bb7ad6dd 100644 --- a/framework/bigcommerce/api/endpoints/cart/add-item.ts +++ b/framework/bigcommerce/api/endpoints/cart/add-item.ts @@ -27,11 +27,11 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({ } const { data } = cartId ? await config.storeApiFetch( - `/v3/carts/${cartId}/items?include=line_items.physical_items.options`, + `/v3/carts/${cartId}/items?include=line_items.physical_items.options,line_items.digital_items.options`, options ) : await config.storeApiFetch( - '/v3/carts?include=line_items.physical_items.options', + '/v3/carts?include=line_items.physical_items.options,line_items.digital_items.options', options ) From 36ff343caa890f52f820734e01559ca4ad756eb5 Mon Sep 17 00:00:00 2001 From: Konrad Kruk Date: Fri, 19 Nov 2021 19:42:54 +0100 Subject: [PATCH 83/98] fix typo (#572) Co-authored-by: Gonzalo Pozzo --- framework/swell/schema.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/swell/schema.d.ts b/framework/swell/schema.d.ts index e77d3c8d9c..0ec625048d 100644 --- a/framework/swell/schema.d.ts +++ b/framework/swell/schema.d.ts @@ -332,7 +332,7 @@ export type Checkout = { terms_policy?: string refund_policy?: string privacy_policy?: string - theme?: stirng + theme?: string countries: any[] currencies: any[] payment_methods: any[] From 578f61bbaae6817e3ab17d307b49ee5f162a1093 Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 22 Nov 2021 12:50:45 +0100 Subject: [PATCH 84/98] Fix authentication.refreshToken arguments --- .../spree/utils/tokens/ensure-fresh-user-access-token.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts b/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts index b321b64385..536ecdfa66 100644 --- a/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts +++ b/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts @@ -35,9 +35,9 @@ const ensureFreshUserAccessToken = async (client: Client): Promise => { >(client, 'authentication.refreshToken') const spreeRefreshAccessTokenResponse = - await spreeRefreshAccessTokenSdkMethod([ - { refresh_token: userTokenResponse.refresh_token }, - ]) + await spreeRefreshAccessTokenSdkMethod({ + refresh_token: userTokenResponse.refresh_token, + }) if (spreeRefreshAccessTokenResponse.isFail()) { removeUserTokenResponse() From 8c73220564226491a90155b1432023115591b68e Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 22 Nov 2021 12:55:56 +0100 Subject: [PATCH 85/98] Remove redundant comments and logs --- framework/spree/auth/use-login.tsx | 2 -- framework/spree/auth/use-logout.tsx | 2 -- framework/spree/auth/use-signup.tsx | 2 -- framework/spree/cart/use-add-item.tsx | 2 -- framework/spree/cart/use-cart.tsx | 2 -- framework/spree/cart/use-remove-item.tsx | 2 -- framework/spree/cart/use-update-item.tsx | 2 -- framework/spree/customer/use-customer.tsx | 2 -- framework/spree/product/use-search.tsx | 8 -------- framework/spree/wishlist/use-add-item.tsx | 2 -- framework/spree/wishlist/use-remove-item.tsx | 2 -- framework/spree/wishlist/use-wishlist.tsx | 2 -- 12 files changed, 30 deletions(-) diff --git a/framework/spree/auth/use-login.tsx b/framework/spree/auth/use-login.tsx index 57c2b64bfc..308ac6597c 100644 --- a/framework/spree/auth/use-login.tsx +++ b/framework/spree/auth/use-login.tsx @@ -62,8 +62,6 @@ export const handler: MutationHook = { useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = () => { - console.log('useLogin useHook called.') - const customer = useCustomer() const cart = useCart() const wishlist = useWishlist() diff --git a/framework/spree/auth/use-logout.tsx b/framework/spree/auth/use-logout.tsx index 5fc19218d4..0d8eb4bc9d 100644 --- a/framework/spree/auth/use-logout.tsx +++ b/framework/spree/auth/use-logout.tsx @@ -54,8 +54,6 @@ export const handler: MutationHook = { useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = () => { - console.log('useLogout useHook called.') - const customer = useCustomer({ swrOptions: { isPaused: () => true }, }) diff --git a/framework/spree/auth/use-signup.tsx b/framework/spree/auth/use-signup.tsx index b8385339cd..ffa6ecc6aa 100644 --- a/framework/spree/auth/use-signup.tsx +++ b/framework/spree/auth/use-signup.tsx @@ -69,8 +69,6 @@ export const handler: MutationHook = { useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = () => { - console.log('useSignup useHook called.') - const customer = useCustomer() const cart = useCart() const wishlist = useWishlist() diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index 631545d6b9..1941b0f573 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -98,8 +98,6 @@ export const handler: MutationHook = { useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = () => { - console.log('useAddItem useHook called.') - const { mutate } = useCart() return useCallback( diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index aac7caa8fd..91682b5b9f 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -94,8 +94,6 @@ export const handler: SWRHook = { const useWrappedHook: ReturnType['useHook']> = ( input ) => { - console.log('useCart useHook called.') - const response = useData({ swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, }) diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx index 2eebe5fae7..1ee57d7cdc 100644 --- a/framework/spree/cart/use-remove-item.tsx +++ b/framework/spree/cart/use-remove-item.tsx @@ -96,8 +96,6 @@ export const handler: MutationHook = { useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = () => { - console.log('useRemoveItem useHook called.') - const { mutate } = useCart() return useCallback( diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx index 82b239e988..93780f26ac 100644 --- a/framework/spree/cart/use-update-item.tsx +++ b/framework/spree/cart/use-update-item.tsx @@ -105,8 +105,6 @@ export const handler: MutationHook = { useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = (context) => { - console.log('useUpdateItem useHook called.') - const { mutate } = useCart() return useMemo( diff --git a/framework/spree/customer/use-customer.tsx b/framework/spree/customer/use-customer.tsx index 4ca5086ddb..abc1dc8633 100644 --- a/framework/spree/customer/use-customer.tsx +++ b/framework/spree/customer/use-customer.tsx @@ -70,8 +70,6 @@ export const handler: SWRHook = { const useWrappedHook: ReturnType['useHook']> = ( input ) => { - console.log('useCustomer useHook called.') - return useData({ swrOptions: { revalidateOnFocus: false, diff --git a/framework/spree/product/use-search.tsx b/framework/spree/product/use-search.tsx index 9ebab8462b..5912a72cab 100644 --- a/framework/spree/product/use-search.tsx +++ b/framework/spree/product/use-search.tsx @@ -75,18 +75,10 @@ export const handler: SWRHook = { return { products: normalizedProducts, found } }, - // useHook is used for both, SWR and mutation requests to the store. - // useHook is called in React components. For example, after clicking `Add to cart`. useHook: ({ useData }) => { const useWrappedHook: ReturnType['useHook']> = ( input = {} ) => { - // useData calls the fetcher method (above). - // The difference between useHook and calling fetcher directly is - // useHook accepts swrOptions. - - console.log('useSearch useHook called.') - return useData({ input: [ ['search', input.search], diff --git a/framework/spree/wishlist/use-add-item.tsx b/framework/spree/wishlist/use-add-item.tsx index 86a51f8cbd..3b6a401d13 100644 --- a/framework/spree/wishlist/use-add-item.tsx +++ b/framework/spree/wishlist/use-add-item.tsx @@ -57,8 +57,6 @@ export const handler: MutationHook = { useHook: ({ fetch }) => { const useWrappedHook: ReturnType['useHook']> = () => { - console.log('useAddItem (wishlist) useHook called.') - const wishlist = useWishlist() return useCallback( diff --git a/framework/spree/wishlist/use-remove-item.tsx b/framework/spree/wishlist/use-remove-item.tsx index 59986e436b..32e6fa58d9 100644 --- a/framework/spree/wishlist/use-remove-item.tsx +++ b/framework/spree/wishlist/use-remove-item.tsx @@ -47,8 +47,6 @@ export const handler: MutationHook = { const useWrappedHook: ReturnType< MutationHook['useHook'] > = () => { - console.log('useRemoveItem (wishlist) useHook called.') - const wishlist = useWishlist() return useCallback( diff --git a/framework/spree/wishlist/use-wishlist.tsx b/framework/spree/wishlist/use-wishlist.tsx index 7c2e7ad8ba..096c4cc6e6 100644 --- a/framework/spree/wishlist/use-wishlist.tsx +++ b/framework/spree/wishlist/use-wishlist.tsx @@ -67,8 +67,6 @@ export const handler: SWRHook = { const useWrappedHook: ReturnType['useHook']> = ( input ) => { - console.log('useWishlist useHook called.') - const response = useData({ swrOptions: { revalidateOnFocus: false, From e03b9f310b47afb1c5752f62b971c5b0fd7d9ccc Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 22 Nov 2021 14:33:36 +0100 Subject: [PATCH 86/98] Fix createEmptyCart request to Spree and add option to disable auto login --- framework/spree/.env.template | 1 + framework/spree/auth/use-signup.tsx | 5 ++++- framework/spree/isomorphic-config.ts | 2 ++ framework/spree/utils/create-empty-cart.ts | 6 +----- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 24ad1e4100..8f4dbf5dd0 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -22,3 +22,4 @@ NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER=false NEXT_PUBLIC_SPREE_IMAGES_SIZE=1000x1000 NEXT_PUBLIC_SPREE_IMAGES_QUALITY=100 +NEXT_PUBLIC_SPREE_LOGIN_AFTER_SIGNUP=true diff --git a/framework/spree/auth/use-signup.tsx b/framework/spree/auth/use-signup.tsx index ffa6ecc6aa..f975dc9216 100644 --- a/framework/spree/auth/use-signup.tsx +++ b/framework/spree/auth/use-signup.tsx @@ -10,6 +10,7 @@ import useCustomer from '../customer/use-customer' import useCart from '../cart/use-cart' import useWishlist from '../wishlist/use-wishlist' import login from '../utils/login' +import { requireConfigValue } from '@framework/isomorphic-config' export default useSignup as UseSignup @@ -62,7 +63,9 @@ export const handler: MutationHook = { } // Login immediately after the account is created. - await login(fetch, getTokenParameters, true) + if (requireConfigValue('loginAfterSignup')) { + await login(fetch, getTokenParameters, true) + } return null }, diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index 423fa76688..b824fd80a3 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -45,6 +45,7 @@ const isomorphicConfig = { imagesQuality: validateImagesQuality( process.env.NEXT_PUBLIC_SPREE_IMAGES_QUALITY ), + loginAfterSignup: process.env.NEXT_PUBLIC_SPREE_LOGIN_AFTER_SIGNUP === 'true', } export default forceIsomorphicConfigValues( @@ -68,6 +69,7 @@ export default forceIsomorphicConfigValues( 'imagesOptionFilter', 'imagesSize', 'imagesQuality', + 'loginAfterSignup', ] ) diff --git a/framework/spree/utils/create-empty-cart.ts b/framework/spree/utils/create-empty-cart.ts index 7ad512e66d..0bf0aa5228 100644 --- a/framework/spree/utils/create-empty-cart.ts +++ b/framework/spree/utils/create-empty-cart.ts @@ -14,11 +14,7 @@ const createEmptyCart = ( return fetch>({ variables: { methodPath: 'cart.create', - arguments: [ - { - token, - }, - ], + arguments: [token], }, }) } From 9e6f80e5344bd2eebfe040a210e3ef2294d5e142 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 25 Nov 2021 20:30:17 +0100 Subject: [PATCH 87/98] Fix formatting issues --- framework/spree/api/operations/get-all-product-paths.ts | 4 ++-- framework/spree/utils/expand-options.ts | 4 +--- framework/spree/utils/sort-option-types.ts | 4 +++- framework/spree/utils/validations/validate-images-quality.ts | 4 +--- framework/spree/utils/validations/validate-images-size.ts | 4 +--- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/framework/spree/api/operations/get-all-product-paths.ts b/framework/spree/api/operations/get-all-product-paths.ts index 518ccfde24..4795d1fdb7 100644 --- a/framework/spree/api/operations/get-all-product-paths.ts +++ b/framework/spree/api/operations/get-all-product-paths.ts @@ -69,8 +69,8 @@ export default function getAllProductPathsOperation({ per_page: productsCount, image_transformation: { quality: imagesQuality, - size: imagesSize - } + size: imagesSize, + }, }, ], } diff --git a/framework/spree/utils/expand-options.ts b/framework/spree/utils/expand-options.ts index 8bada03de9..f0ec2ef600 100644 --- a/framework/spree/utils/expand-options.ts +++ b/framework/spree/utils/expand-options.ts @@ -1,6 +1,4 @@ -import type { - ProductOptionValues -} from '@commerce/types/product' +import type { ProductOptionValues } from '@commerce/types/product' import type { JsonApiDocument, JsonApiResponse, diff --git a/framework/spree/utils/sort-option-types.ts b/framework/spree/utils/sort-option-types.ts index 1217579303..b818727743 100644 --- a/framework/spree/utils/sort-option-types.ts +++ b/framework/spree/utils/sort-option-types.ts @@ -1,6 +1,8 @@ import type { ExpandedProductOption } from '@framework/types' -const sortOptionsByPosition = (options: ExpandedProductOption[]): ExpandedProductOption[] => { +const sortOptionsByPosition = ( + options: ExpandedProductOption[] +): ExpandedProductOption[] => { return options.sort((firstOption, secondOption) => { return firstOption.position - secondOption.position }) diff --git a/framework/spree/utils/validations/validate-images-quality.ts b/framework/spree/utils/validations/validate-images-quality.ts index 46debefe50..909caad57e 100644 --- a/framework/spree/utils/validations/validate-images-quality.ts +++ b/framework/spree/utils/validations/validate-images-quality.ts @@ -1,6 +1,4 @@ -const validateImagesQuality = ( - quality: unknown -): number => { +const validateImagesQuality = (quality: unknown): number => { let quality_level: number if (typeof quality === 'string') { diff --git a/framework/spree/utils/validations/validate-images-size.ts b/framework/spree/utils/validations/validate-images-size.ts index e4a7e32a85..e02036dad3 100644 --- a/framework/spree/utils/validations/validate-images-size.ts +++ b/framework/spree/utils/validations/validate-images-size.ts @@ -1,6 +1,4 @@ -const validateImagesSize = ( - size: unknown -): string => { +const validateImagesSize = (size: unknown): string => { if (typeof size !== 'string') { throw new TypeError('size must be a string.') } From 8374ac58eb86d853e030c08ff29980c2f20e8b37 Mon Sep 17 00:00:00 2001 From: tniezg Date: Thu, 25 Nov 2021 20:56:17 +0100 Subject: [PATCH 88/98] Apply image transformation when fetching images for products in cart --- framework/spree/cart/use-cart.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/framework/spree/cart/use-cart.tsx b/framework/spree/cart/use-cart.tsx index 91682b5b9f..e700c27fad 100644 --- a/framework/spree/cart/use-cart.tsx +++ b/framework/spree/cart/use-cart.tsx @@ -7,11 +7,15 @@ import normalizeCart from '../utils/normalizations/normalize-cart' import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' -import { setCartToken } from '../utils/tokens/cart-token' import { FetcherError } from '@commerce/utils/errors' -import ensureIToken from '@framework/utils/tokens/ensure-itoken' -import isLoggedIn from '@framework/utils/tokens/is-logged-in' -import createEmptyCart from '@framework/utils/create-empty-cart' +import { setCartToken } from '../utils/tokens/cart-token' +import ensureIToken from '../utils/tokens/ensure-itoken' +import isLoggedIn from '../utils/tokens/is-logged-in' +import createEmptyCart from '../utils/create-empty-cart' +import { requireConfigValue } from '../isomorphic-config' + +const imagesSize = requireConfigValue('imagesSize') as string +const imagesQuality = requireConfigValue('imagesQuality') as number export default useCart as UseCart @@ -58,6 +62,10 @@ export const handler: SWRHook = { 'line_items.variant.option_values', 'line_items.variant.product.option_types', ].join(','), + image_transformation: { + quality: imagesQuality, + size: imagesSize, + }, }, ], }, From f1377e29675404284c57969d525bbbf763ec98f2 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 1 Dec 2021 16:30:18 +0100 Subject: [PATCH 89/98] Replace call to qs with Spree SDK built-in helper --- .../spree/utils/create-customized-fetch-fetcher.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/framework/spree/utils/create-customized-fetch-fetcher.ts b/framework/spree/utils/create-customized-fetch-fetcher.ts index 580f2956f4..ae52568dc5 100644 --- a/framework/spree/utils/create-customized-fetch-fetcher.ts +++ b/framework/spree/utils/create-customized-fetch-fetcher.ts @@ -1,5 +1,7 @@ -import * as qs from 'qs' -import { errors } from '@spree/storefront-api-v2-sdk' +import { + errors, + request as spreeSdkRequestHelpers, +} from '@spree/storefront-api-v2-sdk' import type { CreateCustomizedFetchFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/CreateCustomizedFetchFetcher' export const fetchResponseKey = Symbol('fetch-response-key') @@ -33,9 +35,8 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( break default: payload = null - absoluteUrl.search = qs.stringify(params, { - arrayFormat: 'brackets', - }) + absoluteUrl.search = + spreeSdkRequestHelpers.objectToQuerystring(params) } const request: Request = new requestConstructor( From 282132dec1f1407d1bb0cbd057e52e5d4b446349 Mon Sep 17 00:00:00 2001 From: tniezg Date: Fri, 3 Dec 2021 14:42:21 +0100 Subject: [PATCH 90/98] Upgrade Spree SDK to 5.0.1 --- framework/spree/api/utils/create-api-fetch.ts | 1 - framework/spree/fetcher.ts | 4 +- package-lock.json | 94 ++++--------------- package.json | 5 +- 4 files changed, 22 insertions(+), 82 deletions(-) diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index 0fe90cfe21..dad789b4eb 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -22,7 +22,6 @@ export type CreateApiFetch = ( const createApiFetch: CreateApiFetch = (_getConfig) => { const client = makeClient({ host: requireConfigValue('apiHost') as string, - fetcherType: 'custom', createFetcher: (fetcherOptions) => { return createCustomizedFetchFetcher({ fetch, diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index c8cbfaec58..c9505e4c93 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -1,9 +1,8 @@ import type { Fetcher } from '@commerce/utils/types' import convertSpreeErrorToGraphQlError from './utils/convert-spree-error-to-graph-ql-error' -import { makeClient } from '@spree/storefront-api-v2-sdk' +import { makeClient, errors } from '@spree/storefront-api-v2-sdk' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import type { GraphQLFetcherResult } from '@commerce/api' -import { errors } from '@spree/storefront-api-v2-sdk' import { requireConfigValue } from './isomorphic-config' import getSpreeSdkMethodFromEndpointPath from './utils/get-spree-sdk-method-from-endpoint-path' import SpreeSdkMethodFromEndpointPathError from './errors/SpreeSdkMethodFromEndpointPathError' @@ -20,7 +19,6 @@ import RefreshTokenError from './errors/RefreshTokenError' const client = makeClient({ host: requireConfigValue('apiHost') as string, - fetcherType: 'custom', createFetcher: (fetcherOptions) => { return createCustomizedFetchFetcher({ fetch: globalThis.fetch, diff --git a/package-lock.json b/package-lock.json index 48851b6669..196a45582b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,7 @@ "license": "MIT", "dependencies": { "@react-spring/web": "^9.2.1", - "@spree/storefront-api-v2-sdk": "^4.12.0", - "@types/qs": "^6.9.7", + "@spree/storefront-api-v2-sdk": "^5.0.1", "@vercel/fetch": "^6.1.1", "autoprefixer": "^10.2.6", "body-scroll-lock": "^3.1.5", @@ -27,10 +26,8 @@ "next": "^12.0.3", "next-seo": "^4.26.0", "next-themes": "^0.0.14", - "node-fetch": "^2.6.1", "postcss": "^8.3.5", "postcss-nesting": "^8.0.1", - "qs": "^6.7.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-fast-marquee": "^1.1.4", @@ -2461,19 +2458,23 @@ } }, "node_modules/@spree/storefront-api-v2-sdk": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-4.12.0.tgz", - "integrity": "sha512-3S/1a0KlpmJPupN3icnYaLBeQyoyjU7T/R8SfFjSDbiA95Nxk86J45vCYYM1VEhnTqjk8BdPic9Lli4vtkHSrQ==", - "dependencies": { - "axios": "^0.21.4", - "lodash": "^4.17.21", - "qs": "^6.10.1" - }, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-5.0.1.tgz", + "integrity": "sha512-4soQAydchJ9G1d3Xa96XRZ5Uq6IqE0amc8jEjL3H0QLv1NJEv1IK4OfbLK5VRMxv+7QcL/ewHEo2zHm6tqBizA==", "engines": { - "node": ">=14.15.4" + "node": ">=14.17.0" }, - "optionalDependencies": { - "node-fetch": "^2.6.1" + "peerDependencies": { + "axios": "^0.24.0", + "node-fetch": "^2.6.6" + }, + "peerDependenciesMeta": { + "axios": { + "optional": true + }, + "node-fetch": { + "optional": true + } } }, "node_modules/@szmarczak/http-timer": { @@ -2628,11 +2629,6 @@ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", "dev": true }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, "node_modules/@types/react": { "version": "17.0.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.11.tgz", @@ -3258,14 +3254,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -5821,25 +5809,6 @@ "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.", "dev": true }, - "node_modules/follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", @@ -17254,15 +17223,10 @@ "dev": true }, "@spree/storefront-api-v2-sdk": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-4.12.0.tgz", - "integrity": "sha512-3S/1a0KlpmJPupN3icnYaLBeQyoyjU7T/R8SfFjSDbiA95Nxk86J45vCYYM1VEhnTqjk8BdPic9Lli4vtkHSrQ==", - "requires": { - "axios": "^0.21.4", - "lodash": "^4.17.21", - "node-fetch": "^2.6.1", - "qs": "^6.10.1" - } + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@spree/storefront-api-v2-sdk/-/storefront-api-v2-sdk-5.0.1.tgz", + "integrity": "sha512-4soQAydchJ9G1d3Xa96XRZ5Uq6IqE0amc8jEjL3H0QLv1NJEv1IK4OfbLK5VRMxv+7QcL/ewHEo2zHm6tqBizA==", + "requires": {} }, "@szmarczak/http-timer": { "version": "1.1.2", @@ -17409,11 +17373,6 @@ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", "dev": true }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, "@types/react": { "version": "17.0.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.11.tgz", @@ -17884,14 +17843,6 @@ "integrity": "sha512-5LMaDRWm8ZFPAEdzTYmgjjEdj1YnQcpfrVajO/sn/LhbpGp0Y0H64c2hLZI1gRMxfA+w1S71Uc/nHaOXgcCvGg==", "dev": true }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -19946,11 +19897,6 @@ "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", "dev": true }, - "follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" - }, "foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", diff --git a/package.json b/package.json index 04c988315c..b53d79ece7 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,7 @@ }, "dependencies": { "@react-spring/web": "^9.2.1", - "@spree/storefront-api-v2-sdk": "^4.12.0", - "@types/qs": "^6.9.7", + "@spree/storefront-api-v2-sdk": "^5.0.1", "@vercel/fetch": "^6.1.1", "autoprefixer": "^10.2.6", "body-scroll-lock": "^3.1.5", @@ -38,10 +37,8 @@ "next": "^12.0.3", "next-seo": "^4.26.0", "next-themes": "^0.0.14", - "node-fetch": "^2.6.1", "postcss": "^8.3.5", "postcss-nesting": "^8.0.1", - "qs": "^6.7.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-fast-marquee": "^1.1.4", From 7436f6d8dd8307be26523fbee86f483e73235b8f Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 8 Dec 2021 16:24:24 +0100 Subject: [PATCH 91/98] Rename zeitFetch import to vercelFetch --- framework/spree/api/utils/fetch.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/spree/api/utils/fetch.ts b/framework/spree/api/utils/fetch.ts index 9d9fff3edc..26f9ab6748 100644 --- a/framework/spree/api/utils/fetch.ts +++ b/framework/spree/api/utils/fetch.ts @@ -1,3 +1,3 @@ -import zeitFetch from '@vercel/fetch' +import vercelFetch from '@vercel/fetch' -export default zeitFetch() +export default vercelFetch() From 80c74ba30aac97461b068c7ad0c4fb85f4a7609f Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 8 Dec 2021 16:52:21 +0100 Subject: [PATCH 92/98] Abstract fetcher JSON Content-Type checking into separate function --- .../spree/utils/create-customized-fetch-fetcher.ts | 11 ++++------- framework/spree/utils/is-json-content-type.ts | 5 +++++ 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 framework/spree/utils/is-json-content-type.ts diff --git a/framework/spree/utils/create-customized-fetch-fetcher.ts b/framework/spree/utils/create-customized-fetch-fetcher.ts index ae52568dc5..1c10b19e94 100644 --- a/framework/spree/utils/create-customized-fetch-fetcher.ts +++ b/framework/spree/utils/create-customized-fetch-fetcher.ts @@ -3,6 +3,7 @@ import { request as spreeSdkRequestHelpers, } from '@spree/storefront-api-v2-sdk' import type { CreateCustomizedFetchFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/CreateCustomizedFetchFetcher' +import isJsonContentType from './is-json-content-type' export const fetchResponseKey = Symbol('fetch-response-key') @@ -54,14 +55,10 @@ const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( let data if (responseParsing === 'automatic') { - if ( - !responseContentType || - (!responseContentType.includes('application/json') && - !responseContentType.includes('application/vnd.api+json')) - ) { - data = await response.text() - } else { + if (responseContentType && isJsonContentType(responseContentType)) { data = await response.json() + } else { + data = await response.text() } } else if (responseParsing === 'text') { data = await response.text() diff --git a/framework/spree/utils/is-json-content-type.ts b/framework/spree/utils/is-json-content-type.ts new file mode 100644 index 0000000000..fd82d65fd8 --- /dev/null +++ b/framework/spree/utils/is-json-content-type.ts @@ -0,0 +1,5 @@ +const isJsonContentType = (contentType: string): boolean => + contentType.includes('application/json') || + contentType.includes('application/vnd.api+json') + +export default isJsonContentType From 7f27ed6d268d8a7b04d5363dfa7309682054d8ed Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 8 Dec 2021 17:10:55 +0100 Subject: [PATCH 93/98] Rename imageUrl to url getMediaGallery already provides context for the constant --- framework/spree/utils/get-media-gallery.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/framework/spree/utils/get-media-gallery.ts b/framework/spree/utils/get-media-gallery.ts index e697562d42..da939c82be 100644 --- a/framework/spree/utils/get-media-gallery.ts +++ b/framework/spree/utils/get-media-gallery.ts @@ -12,15 +12,10 @@ const getMediaGallery = ( ) => string | null ) => { return images.reduce((productImages, _, imageIndex) => { - const imageUrl = getImageUrl(images[imageIndex], 800, 800) + const url = getImageUrl(images[imageIndex], 800, 800) - if (imageUrl) { - return [ - ...productImages, - { - url: imageUrl, - }, - ] + if (url) { + return [...productImages, { url }] } return productImages From 9d35c02281ebdd6322a96c63585a9da9195701d4 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 8 Dec 2021 17:25:11 +0100 Subject: [PATCH 94/98] Remove return type for getProductPath The return type can be trivially determined from the returned value. --- framework/spree/utils/get-product-path.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/spree/utils/get-product-path.ts b/framework/spree/utils/get-product-path.ts index b4c03096fa..6749a4a3eb 100644 --- a/framework/spree/utils/get-product-path.ts +++ b/framework/spree/utils/get-product-path.ts @@ -1,6 +1,6 @@ import type { ProductSlugAttr } from '../types' -const getProductPath = (partialSpreeProduct: ProductSlugAttr): string => { +const getProductPath = (partialSpreeProduct: ProductSlugAttr) => { return `/${partialSpreeProduct.attributes.slug}` } From d1606684e328e4876b911a02692d0b9e95304ea3 Mon Sep 17 00:00:00 2001 From: Tomek Niezgoda <1410097+tniezg@users.noreply.github.com> Date: Mon, 13 Dec 2021 15:15:11 +0100 Subject: [PATCH 95/98] Change URL to Spree demo store in root README Co-authored-by: Gonzalo Pozzo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7493e7be4c..1c862e172a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/) - Vendure Demo: https://vendure.vercel.store - Saleor Demo: https://saleor.vercel.store/ - Ordercloud Demo: https://ordercloud.vercel.store/ -- Spree Demo: https://spree.vercel.app/ +- Spree Demo: https://spree.vercel.store/ ## Features From acb4e89b9cf195ba924d365df98035653bab26fa Mon Sep 17 00:00:00 2001 From: Tomek Niezgoda <1410097+tniezg@users.noreply.github.com> Date: Mon, 13 Dec 2021 15:16:45 +0100 Subject: [PATCH 96/98] Change label for link to Spree demo store in Spree's README Co-authored-by: Gonzalo Pozzo --- framework/spree/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/spree/README.md b/framework/spree/README.md index b80e4d0960..251e40146e 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -4,7 +4,7 @@ An integration of [Spree Commerce](https://spreecommerce.org/) within NextJS Commerce. It supports browsing and searching Spree products and adding products to the cart. -**Demo**: [https://spree.vercel.app/][6] +**Demo**: [https://spree.vercel.store/][6] ## Installation From 489023f81b934bbe5d84a3cf0c5fcdeb1b13a31b Mon Sep 17 00:00:00 2001 From: Tomek Niezgoda <1410097+tniezg@users.noreply.github.com> Date: Mon, 13 Dec 2021 15:16:59 +0100 Subject: [PATCH 97/98] Change URL to Spree demo store in Spree's README Co-authored-by: Gonzalo Pozzo --- framework/spree/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/spree/README.md b/framework/spree/README.md index 251e40146e..0b2068cb58 100644 --- a/framework/spree/README.md +++ b/framework/spree/README.md @@ -30,4 +30,4 @@ An integration of [Spree Commerce](https://spreecommerce.org/) within NextJS Com [3]: https://github.com/spree/spree_starter [4]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS [5]: ./README-assets/screenshots.png -[6]: https://spree.vercel.app/ +[6]: https://spree.vercel.store/ From beac98608930f3a06f483d2eebe92d385aa4428f Mon Sep 17 00:00:00 2001 From: tniezg Date: Mon, 13 Dec 2021 20:59:36 +0100 Subject: [PATCH 98/98] Use only relative paths to /framework/spree from itself --- framework/spree/api/operations/get-all-pages.ts | 4 ++-- framework/spree/api/operations/get-all-products.ts | 4 ++-- framework/spree/api/operations/get-page.ts | 2 +- framework/spree/api/operations/get-product.ts | 4 ++-- framework/spree/api/operations/get-site-info.ts | 2 +- framework/spree/api/utils/create-api-fetch.ts | 4 ++-- framework/spree/auth/use-signup.tsx | 2 +- framework/spree/cart/use-add-item.tsx | 6 +++--- framework/spree/cart/use-remove-item.tsx | 8 ++++---- framework/spree/cart/use-update-item.tsx | 8 ++++---- framework/spree/customer/use-customer.tsx | 6 +++--- framework/spree/types/index.ts | 2 +- framework/spree/utils/expand-options.ts | 4 ++-- .../spree/utils/normalizations/normalize-wishlist.ts | 4 ++-- framework/spree/utils/sort-option-types.ts | 2 +- .../spree/utils/tokens/ensure-fresh-user-access-token.ts | 2 +- framework/spree/utils/tokens/revoke-user-tokens.ts | 4 ++-- framework/spree/utils/tokens/user-token-response.ts | 2 +- framework/spree/wishlist/use-add-item.tsx | 6 +++--- framework/spree/wishlist/use-remove-item.tsx | 4 ++-- framework/spree/wishlist/use-wishlist.tsx | 2 +- 21 files changed, 41 insertions(+), 41 deletions(-) diff --git a/framework/spree/api/operations/get-all-pages.ts b/framework/spree/api/operations/get-all-pages.ts index 9ed69b9035..580a74999c 100644 --- a/framework/spree/api/operations/get-all-pages.ts +++ b/framework/spree/api/operations/get-all-pages.ts @@ -3,8 +3,8 @@ import type { OperationOptions, } from '@commerce/api/operations' import type { GetAllPagesOperation, Page } from '@commerce/types/page' -import { requireConfigValue } from '@framework/isomorphic-config' -import normalizePage from '@framework/utils/normalizations/normalize-page' +import { requireConfigValue } from '../../isomorphic-config' +import normalizePage from '../../utils/normalizations/normalize-page' import type { IPages } from '@spree/storefront-api-v2-sdk/types/interfaces/Page' import type { SpreeSdkVariables } from '../../types' import type { SpreeApiConfig, SpreeApiProvider } from '../index' diff --git a/framework/spree/api/operations/get-all-products.ts b/framework/spree/api/operations/get-all-products.ts index 52db5edd3c..a292e6097e 100644 --- a/framework/spree/api/operations/get-all-products.ts +++ b/framework/spree/api/operations/get-all-products.ts @@ -6,9 +6,9 @@ import type { } from '@commerce/api/operations' import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { SpreeApiConfig, SpreeApiProvider } from '../index' -import type { SpreeSdkVariables } from 'framework/spree/types' +import type { SpreeSdkVariables } from '../../types' import normalizeProduct from '../../utils/normalizations/normalize-product' -import { requireConfigValue } from '@framework/isomorphic-config' +import { requireConfigValue } from '../../isomorphic-config' const imagesSize = requireConfigValue('imagesSize') as string const imagesQuality = requireConfigValue('imagesQuality') as number diff --git a/framework/spree/api/operations/get-page.ts b/framework/spree/api/operations/get-page.ts index c613729ae3..ecb02755d0 100644 --- a/framework/spree/api/operations/get-page.ts +++ b/framework/spree/api/operations/get-page.ts @@ -6,7 +6,7 @@ import type { GetPageOperation } from '@commerce/types/page' import type { SpreeSdkVariables } from '../../types' import type { SpreeApiConfig, SpreeApiProvider } from '..' import type { IPage } from '@spree/storefront-api-v2-sdk/types/interfaces/Page' -import normalizePage from '@framework/utils/normalizations/normalize-page' +import normalizePage from '../../utils/normalizations/normalize-page' export type Page = any export type GetPageResult = { page?: Page } diff --git a/framework/spree/api/operations/get-product.ts b/framework/spree/api/operations/get-product.ts index cf9cd6fc71..18e9643cd1 100644 --- a/framework/spree/api/operations/get-product.ts +++ b/framework/spree/api/operations/get-product.ts @@ -5,8 +5,8 @@ import type { OperationOptions, } from '@commerce/api/operations' import type { IProduct } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' -import type { SpreeSdkVariables } from 'framework/spree/types' -import MissingSlugVariableError from 'framework/spree/errors/MissingSlugVariableError' +import type { SpreeSdkVariables } from '../../types' +import MissingSlugVariableError from '../../errors/MissingSlugVariableError' import normalizeProduct from '../../utils/normalizations/normalize-product' import { requireConfigValue } from '../../isomorphic-config' diff --git a/framework/spree/api/operations/get-site-info.ts b/framework/spree/api/operations/get-site-info.ts index 1e826ee56a..4d9aaf0ad8 100644 --- a/framework/spree/api/operations/get-site-info.ts +++ b/framework/spree/api/operations/get-site-info.ts @@ -8,7 +8,7 @@ import type { TaxonAttr, } from '@spree/storefront-api-v2-sdk/types/interfaces/Taxon' import { requireConfigValue } from '../../isomorphic-config' -import type { SpreeSdkVariables } from 'framework/spree/types' +import type { SpreeSdkVariables } from '../../types' import type { SpreeApiConfig, SpreeApiProvider } from '..' const taxonsSort = (spreeTaxon1: TaxonAttr, spreeTaxon2: TaxonAttr): number => { diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index dad789b4eb..0c7d51b0b2 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -4,13 +4,13 @@ import { requireConfigValue } from '../../isomorphic-config' import convertSpreeErrorToGraphQlError from '../../utils/convert-spree-error-to-graph-ql-error' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' import getSpreeSdkMethodFromEndpointPath from '../../utils/get-spree-sdk-method-from-endpoint-path' -import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' +import SpreeSdkMethodFromEndpointPathError from '../../errors/SpreeSdkMethodFromEndpointPathError' import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' import createCustomizedFetchFetcher, { fetchResponseKey, } from '../../utils/create-customized-fetch-fetcher' import fetch, { Request } from 'node-fetch' -import type { SpreeSdkResponseWithRawResponse } from '@framework/types' +import type { SpreeSdkResponseWithRawResponse } from '../../types' export type CreateApiFetch = ( getConfig: () => SpreeApiConfig diff --git a/framework/spree/auth/use-signup.tsx b/framework/spree/auth/use-signup.tsx index f975dc9216..708668b9cb 100644 --- a/framework/spree/auth/use-signup.tsx +++ b/framework/spree/auth/use-signup.tsx @@ -10,7 +10,7 @@ import useCustomer from '../customer/use-customer' import useCart from '../cart/use-cart' import useWishlist from '../wishlist/use-wishlist' import login from '../utils/login' -import { requireConfigValue } from '@framework/isomorphic-config' +import { requireConfigValue } from '../isomorphic-config' export default useSignup as UseSignup diff --git a/framework/spree/cart/use-add-item.tsx b/framework/spree/cart/use-add-item.tsx index 1941b0f573..74bdd633fe 100644 --- a/framework/spree/cart/use-add-item.tsx +++ b/framework/spree/cart/use-add-item.tsx @@ -10,10 +10,10 @@ import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { AddItem } from '@spree/storefront-api-v2-sdk/types/interfaces/endpoints/CartClass' import { setCartToken } from '../utils/tokens/cart-token' -import ensureIToken from '@framework/utils/tokens/ensure-itoken' -import createEmptyCart from '@framework/utils/create-empty-cart' +import ensureIToken from '../utils/tokens/ensure-itoken' +import createEmptyCart from '../utils/create-empty-cart' import { FetcherError } from '@commerce/utils/errors' -import isLoggedIn from '@framework/utils/tokens/is-logged-in' +import isLoggedIn from '../utils/tokens/is-logged-in' export default useAddItem as UseAddItem diff --git a/framework/spree/cart/use-remove-item.tsx b/framework/spree/cart/use-remove-item.tsx index 1ee57d7cdc..42e7536a91 100644 --- a/framework/spree/cart/use-remove-item.tsx +++ b/framework/spree/cart/use-remove-item.tsx @@ -9,11 +9,11 @@ import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order import type { GraphQLFetcherResult } from '@commerce/api' import type { IQuery } from '@spree/storefront-api-v2-sdk/types/interfaces/Query' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' -import ensureIToken from '@framework/utils/tokens/ensure-itoken' -import createEmptyCart from '@framework/utils/create-empty-cart' -import { setCartToken } from '@framework/utils/tokens/cart-token' +import ensureIToken from '../utils/tokens/ensure-itoken' +import createEmptyCart from '../utils/create-empty-cart' +import { setCartToken } from '../utils/tokens/cart-token' import { FetcherError } from '@commerce/utils/errors' -import isLoggedIn from '@framework/utils/tokens/is-logged-in' +import isLoggedIn from '../utils/tokens/is-logged-in' export default useRemoveItem as UseRemoveItem diff --git a/framework/spree/cart/use-update-item.tsx b/framework/spree/cart/use-update-item.tsx index 93780f26ac..86b8599fa5 100644 --- a/framework/spree/cart/use-update-item.tsx +++ b/framework/spree/cart/use-update-item.tsx @@ -10,10 +10,10 @@ import type { GraphQLFetcherResult } from '@commerce/api' import type { IOrder } from '@spree/storefront-api-v2-sdk/types/interfaces/Order' import normalizeCart from '../utils/normalizations/normalize-cart' import debounce from 'lodash.debounce' -import ensureIToken from '@framework/utils/tokens/ensure-itoken' -import createEmptyCart from '@framework/utils/create-empty-cart' -import { setCartToken } from '@framework/utils/tokens/cart-token' -import isLoggedIn from '@framework/utils/tokens/is-logged-in' +import ensureIToken from '../utils/tokens/ensure-itoken' +import createEmptyCart from '../utils/create-empty-cart' +import { setCartToken } from '../utils/tokens/cart-token' +import isLoggedIn from '../utils/tokens/is-logged-in' export default useUpdateItem as UseUpdateItem diff --git a/framework/spree/customer/use-customer.tsx b/framework/spree/customer/use-customer.tsx index abc1dc8633..647645ac2d 100644 --- a/framework/spree/customer/use-customer.tsx +++ b/framework/spree/customer/use-customer.tsx @@ -6,9 +6,9 @@ import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token import type { GraphQLFetcherResult } from '@commerce/api' import type { IAccount } from '@spree/storefront-api-v2-sdk/types/interfaces/Account' import { FetcherError } from '@commerce/utils/errors' -import normalizeUser from '@framework/utils/normalizations/normalize-user' -import isLoggedIn from '@framework/utils/tokens/is-logged-in' -import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import normalizeUser from '../utils/normalizations/normalize-user' +import isLoggedIn from '../utils/tokens/is-logged-in' +import ensureIToken from '../utils/tokens/ensure-itoken' export default useCustomer as UseCustomer diff --git a/framework/spree/types/index.ts b/framework/spree/types/index.ts index 9ea717727a..79b75c2494 100644 --- a/framework/spree/types/index.ts +++ b/framework/spree/types/index.ts @@ -1,4 +1,4 @@ -import type { fetchResponseKey } from '@framework/utils/create-customized-fetch-fetcher' +import type { fetchResponseKey } from '../utils/create-customized-fetch-fetcher' import type { JsonApiDocument, JsonApiListResponse, diff --git a/framework/spree/utils/expand-options.ts b/framework/spree/utils/expand-options.ts index f0ec2ef600..29b9d6760e 100644 --- a/framework/spree/utils/expand-options.ts +++ b/framework/spree/utils/expand-options.ts @@ -6,8 +6,8 @@ import type { import { jsonApi } from '@spree/storefront-api-v2-sdk' import type { RelationType } from '@spree/storefront-api-v2-sdk/types/interfaces/Relationships' import SpreeResponseContentError from '../errors/SpreeResponseContentError' -import type { OptionTypeAttr, ExpandedProductOption } from '@framework/types' -import sortOptionsByPosition from '@framework/utils/sort-option-types' +import type { OptionTypeAttr, ExpandedProductOption } from '../types' +import sortOptionsByPosition from '../utils/sort-option-types' const isColorProductOption = (productOption: ExpandedProductOption) => { return productOption.displayName === 'Color' diff --git a/framework/spree/utils/normalizations/normalize-wishlist.ts b/framework/spree/utils/normalizations/normalize-wishlist.ts index 94768ba569..c9cfee2dbf 100644 --- a/framework/spree/utils/normalizations/normalize-wishlist.ts +++ b/framework/spree/utils/normalizations/normalize-wishlist.ts @@ -1,5 +1,5 @@ -import MissingProductError from '@framework/errors/MissingProductError' -import MissingVariantError from '@framework/errors/MissingVariantError' +import MissingProductError from '../../errors/MissingProductError' +import MissingVariantError from '../../errors/MissingVariantError' import { jsonApi } from '@spree/storefront-api-v2-sdk' import type { ProductAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/Product' import type { WishedItemAttr } from '@spree/storefront-api-v2-sdk/types/interfaces/WishedItem' diff --git a/framework/spree/utils/sort-option-types.ts b/framework/spree/utils/sort-option-types.ts index b818727743..bac632e09e 100644 --- a/framework/spree/utils/sort-option-types.ts +++ b/framework/spree/utils/sort-option-types.ts @@ -1,4 +1,4 @@ -import type { ExpandedProductOption } from '@framework/types' +import type { ExpandedProductOption } from '../types' const sortOptionsByPosition = ( options: ExpandedProductOption[] diff --git a/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts b/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts index 536ecdfa66..de22634fb7 100644 --- a/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts +++ b/framework/spree/utils/tokens/ensure-fresh-user-access-token.ts @@ -7,7 +7,7 @@ import { removeUserTokenResponse, setUserTokenResponse, } from './user-token-response' -import AccessTokenError from '@framework/errors/AccessTokenError' +import AccessTokenError from '../../errors/AccessTokenError' /** * If the user has a saved access token, make sure it's not expired diff --git a/framework/spree/utils/tokens/revoke-user-tokens.ts b/framework/spree/utils/tokens/revoke-user-tokens.ts index 9c69937ea5..9c603a8843 100644 --- a/framework/spree/utils/tokens/revoke-user-tokens.ts +++ b/framework/spree/utils/tokens/revoke-user-tokens.ts @@ -1,7 +1,7 @@ import type { GraphQLFetcherResult } from '@commerce/api' import type { HookFetcherContext } from '@commerce/utils/types' -import TokensNotRejectedError from '@framework/errors/TokensNotRejectedError' -import type { UserOAuthTokens } from '@framework/types' +import TokensNotRejectedError from '../../errors/TokensNotRejectedError' +import type { UserOAuthTokens } from '../../types' import type { EmptyObjectResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/EmptyObject' const revokeUserTokens = async ( diff --git a/framework/spree/utils/tokens/user-token-response.ts b/framework/spree/utils/tokens/user-token-response.ts index ec6ff94520..0c524eccfe 100644 --- a/framework/spree/utils/tokens/user-token-response.ts +++ b/framework/spree/utils/tokens/user-token-response.ts @@ -1,7 +1,7 @@ import { requireConfigValue } from '../../isomorphic-config' import Cookies from 'js-cookie' import type { IOAuthToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' -import UserTokenResponseParseError from '@framework/errors/UserTokenResponseParseError' +import UserTokenResponseParseError from '../../errors/UserTokenResponseParseError' export const getUserTokenResponse = (): IOAuthToken | undefined => { const stringifiedToken = Cookies.get( diff --git a/framework/spree/wishlist/use-add-item.tsx b/framework/spree/wishlist/use-add-item.tsx index 3b6a401d13..dac003ddc1 100644 --- a/framework/spree/wishlist/use-add-item.tsx +++ b/framework/spree/wishlist/use-add-item.tsx @@ -3,16 +3,16 @@ import type { MutationHook } from '@commerce/utils/types' import useAddItem from '@commerce/wishlist/use-add-item' import type { UseAddItem } from '@commerce/wishlist/use-add-item' import useWishlist from './use-wishlist' -import type { ExplicitWishlistAddItemHook } from '@framework/types' +import type { ExplicitWishlistAddItemHook } from '../types' import type { WishedItem, WishlistsAddWishedItem, } from '@spree/storefront-api-v2-sdk/types/interfaces/WishedItem' import type { GraphQLFetcherResult } from '@commerce/api' -import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import ensureIToken from '../utils/tokens/ensure-itoken' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { AddItemHook } from '@commerce/types/wishlist' -import isLoggedIn from '@framework/utils/tokens/is-logged-in' +import isLoggedIn from '../utils/tokens/is-logged-in' export default useAddItem as UseAddItem diff --git a/framework/spree/wishlist/use-remove-item.tsx b/framework/spree/wishlist/use-remove-item.tsx index 32e6fa58d9..3b92b029f4 100644 --- a/framework/spree/wishlist/use-remove-item.tsx +++ b/framework/spree/wishlist/use-remove-item.tsx @@ -4,8 +4,8 @@ import useRemoveItem from '@commerce/wishlist/use-remove-item' import type { UseRemoveItem } from '@commerce/wishlist/use-remove-item' import useWishlist from './use-wishlist' import type { ExplicitWishlistRemoveItemHook } from '../types' -import isLoggedIn from '@framework/utils/tokens/is-logged-in' -import ensureIToken from '@framework/utils/tokens/ensure-itoken' +import isLoggedIn from '../utils/tokens/is-logged-in' +import ensureIToken from '../utils/tokens/ensure-itoken' import type { IToken } from '@spree/storefront-api-v2-sdk/types/interfaces/Token' import type { GraphQLFetcherResult } from '@commerce/api' import type { WishedItem } from '@spree/storefront-api-v2-sdk/types/interfaces/WishedItem' diff --git a/framework/spree/wishlist/use-wishlist.tsx b/framework/spree/wishlist/use-wishlist.tsx index 096c4cc6e6..0292d40961 100644 --- a/framework/spree/wishlist/use-wishlist.tsx +++ b/framework/spree/wishlist/use-wishlist.tsx @@ -8,7 +8,7 @@ import type { GraphQLFetcherResult } from '@commerce/api' import type { Wishlist } from '@spree/storefront-api-v2-sdk/types/interfaces/Wishlist' import ensureIToken from '../utils/tokens/ensure-itoken' import normalizeWishlist from '../utils/normalizations/normalize-wishlist' -import isLoggedIn from '@framework/utils/tokens/is-logged-in' +import isLoggedIn from '../utils/tokens/is-logged-in' export default useWishlist as UseWishlist