{"_id":"@fwl/web","_rev":"94-99d0bcc4fdc1fcc4a76a97d657337a56","name":"@fwl/web","dist-tags":{"latest":"0.14.4"},"versions":{"0.1.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fewlines/fwl-logging":"1.0.0","@fwl/tracing":"0.2.1","express":"4.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.7","@types/jest":"26.0.7","@types/node":"14.0.26","@typescript-eslint/eslint-plugin":"3.7.0","@typescript-eslint/parser":"3.7.0","eslint":"7.5.0","eslint-config-prettier":"6.11.0","eslint-plugin-prettier":"3.1.4","jest":"26.1.0","prettier":"2.0.5","ts-jest":"26.1.3","ts-node":"8.10.2","typescript":"3.9.7"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","test":"jest"},"types":"dist/index.d.ts","version":"0.1.0","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.1.0","dist":{"shasum":"7efea319c4481227a5464b1079ee37dc4ff3eba7","integrity":"sha512-soxmJWm5Jg1udiNmoqwxgybPEqWlQKsLI1PMIcgt3IQ1KOeMjVb/mAp7BMZs1JHEr3Vq7HrxeNVCJ65trDAKYw==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.1.0.tgz","fileCount":50,"unpackedSize":256064,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfKVkmCRA9TVsSAnZWagAAf4kQAJZFovPdD9n5JknOuCPy\nzu/03JcWg6UEsevkxBwt1/j1QA+WEeMvCKVnX4asRHejVqfX1dJtAoHdAY43\nb5zCqCOHRFH3ZskdgQPtfr5FMPNYLoYNxf3k03FOkxA6e2DvXBg5DeEymCIA\n7P+fA1bkLLBLEm9Ui94hTnf5/S67M2pbdt7SUuCy7RDdvBWjaO/Z3pmFXF5k\nVP+4p+6KnFLpCJCR4t9r1+C8rWyfyzxNwjpQ0225G4i/kWV9hVh/Ng1Ak5gq\nNUVp/RWyUvJ3/XO+qLh+WmZzG5a7Zn1Lj1K899VTKl5ejCni7lQEdbGGpaJO\nlJg6pqXMyp21pPbFl67vgypIN7q5DhD8tIuyzrzvXIaElWPzz52tRTXe8LtJ\ncvMdcf62teBPTayBuOgD1dXhXUQ6vXE+EFfdhS54vemTWR7KLTjOcwKLUxk6\nmWVU1TJE3vWRGHf5P4zbXJAaS110yN8skikQgGcFComCekCQFhRIex2IREuR\n5MXoIy8ElJoUGZ6nR/0oJ7Syl6J5QuwHeS4H/ubzgYZrlY45yr6jyHWKzkvb\nwgxyBP4da1Lwi0hcielxA9RkwvC2TAjUG8iZgHnmLlT+t3KJl8Z90F7O5EKj\n2Z3x5dKr0I+hr0Ntn75YnZBbmu1O8uTrdDlfaEr3SXtGuj306u0qabY5Q6Zl\nEb4N\r\n=Xw9P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCoBRN4C44uumdxqRWvQErCTbtOFnYmocoDxIE3azL1lwIhAKfHFEcwIQSzxlzTlwPGdyB36T+nIkYhu+/FhT8bFbo+"}]},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.1.0_1596545318488_0.9587309789626683"},"_hasShrinkwrap":false},"0.2.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fewlines/fwl-logging":"1.0.0","@fwl/tracing":"0.2.1","express":"4.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.7","@types/jest":"26.0.7","@types/node":"14.0.26","@typescript-eslint/eslint-plugin":"3.7.0","@typescript-eslint/parser":"3.7.0","eslint":"7.5.0","eslint-config-prettier":"6.11.0","eslint-plugin-prettier":"3.1.4","jest":"26.1.0","prettier":"2.0.5","ts-jest":"26.1.3","ts-node":"8.10.2","typescript":"3.9.7"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","test":"jest"},"types":"dist/index.d.ts","version":"0.2.0","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.2.0","dist":{"shasum":"83e7a0b1aa474d5ef1211dce17264fdee69369c6","integrity":"sha512-Kdg6wSnQlUEhAoL2qxE8vz1DhcGRmLNKqtLbcC99ZUXyV3jmIk8AVkz+AbxXY5OOS5EiBp6wpUbcO1PNn4z2vw==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.2.0.tgz","fileCount":50,"unpackedSize":256746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfKrIECRA9TVsSAnZWagAAafMP/0Amxo+JzKZJsfUAyfGF\nAlKhDpeHepmH1cV127FEDOJELTeHrASizkNaZIy/2In932pK/evWeykzthzZ\n/CObcszR3o4gx2PUgV/yH4cS8Yh8T6TbMFWcEwBgbr5hLCq0wAeogja+gcOy\n3Q0iaPLKBxEtQkBwPjmqzsDaP1y+YezkYAxoxo/0u4AvIbTWZnb4dOkqyfNn\nk21rApEbaEaVRBDDacJBHs/JLU43IcoiNeAyCKDRuLmJRF+SEsGIe9C4hWL9\nCBdzZ7ujvdS6kZpfU6ukkgZ7nXSIzc9eNI0TPWUO0B9JWctf10k14fGDA4Tv\nDl+utz0NQSIFA+B9SZvMLwpgfZDhy/q56ZCydJXyvzUFooTeFD66mLAuKMNL\nk6Cu3pJSmTrb4xW2B4A1uwus8xqgMjwIPOEegV38hRJcLBrrFpnFMeTDa3hA\nVJCThgEKruuEjoU96whZpCim+p0UMjTY+85W/3liCBWozQ0CIPDH+Ty/ctG/\nkTJu/DCF05UnONFWusoZLrCOgwnxGnFFOEz7WAbR52yo7PfhF7QcKgAA5Tep\nmbR7U87D870+JC0GRLiY2opLR/rZHqfQvmS+V4xNsynMJKK5+W/7Mjdo0WTd\nx541/+6fdWhTR7PGj04CGN+KcKVlp+fOGNxVm7mFynz8dVj85LIW4e7dBfWE\nl3vv\r\n=2FDV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDJLyQwNxJlsF0TMUK0Y3r+ZozLdtW9yWoj/I6a82LHBAiBCS/WIFR+xIY7tWBb+Sj2j8ZzWNoxeFV27A5ng6MJ+Cg=="}]},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.2.0_1596633603564_0.9060787023459511"},"_hasShrinkwrap":false},"0.3.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fewlines/fwl-logging":"1.0.0","@fwl/tracing":"0.2.1","express":"4.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.7","@types/jest":"26.0.7","@types/node":"14.0.26","@typescript-eslint/eslint-plugin":"3.7.0","@typescript-eslint/parser":"3.7.0","eslint":"7.5.0","eslint-config-prettier":"6.11.0","eslint-plugin-prettier":"3.1.4","jest":"26.1.0","prettier":"2.0.5","ts-jest":"26.1.3","ts-node":"8.10.2","typescript":"3.9.7"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","test":"jest"},"types":"dist/index.d.ts","version":"0.3.0","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.3.0","dist":{"shasum":"0767fa6009c797b6d442620fc07a8213b7ccd5c5","integrity":"sha512-7mFp2hTBPtPuyIuwSHxDQXtpJosaR9Yt+rpwQ5OHZY7ISG3QqJ1vZUQePcoT5cWHn2lX+vtBnu/EPiHu6TuTNQ==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.3.0.tgz","fileCount":50,"unpackedSize":256328,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfLBEKCRA9TVsSAnZWagAAUwwP/3ZXA+v22OxnR+Z7Qjvs\nl9jFeK3LjPEhljIXBYMNtIvXALVuJJG8EOPTm0aDfEm6nzprUaK1sKTcthe6\nzwff0+nDBbtGkjqbXXzPatMSobFlIlnAk86KfMN9ygPGbPZaLesd3sNoH6WI\nOEdnxUahm+1RpDifNI29T9nEQ0uV6RwICFtp2mc21cHSLj6ac9RQajNeuiBe\nzx/edpyY6QFz0SJ/4fPcomi++8gLwTYN0wd1AJlnCKXkyEszmwVylXs3Qhz8\n2PqbEsk0fnP/kAvtxiidh1q8tSoSmSFaIO/QpVWmxw4BJ3rc/UDzJF5PmxVI\nWf4MRwvojd2cT5GWkCvQUs4ICLvPU15dT5mimRAxGUO8Xsxzn0CLgeYTg9TR\n3JKjt3QQO99aOZdJbEDwJYKwBw3eKVMthkm+dsBgaER1FmXrdeiDO/HUGEMc\nW4biCBYdeiSs/ycDk3YLyX9KsZW4PQ58ep7krkCpbSRRmpRz6FjAmZXmZXTq\nASI6h8+y88q2fmd6E8vtLFfGAPXpdI6wuvq5hlN1UMA9kzve5r+s0Xut7QjW\nyp5bbK9CZAUh5W5ZGtAiYtIuNceuhoYgPeZJFP6uS3LFX+S9+VcomSV56fks\nR8N/FF3Axwe9EtkV+R+A8REvU6y0Obhy32Yb0Za3oUeiR9QWdLyRNdpWNMi2\nwCt0\r\n=j9Qk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC+qpsbS9yJ7hawkCAUTQNPna9G/lSIY3j7QBP3fJViKwIhAP6Ggd+o45bW4vEH8pGLQ3ng1XxXwzgxWu78N77J0L8A"}]},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.3.0_1596723465693_0.3729846908922101"},"_hasShrinkwrap":false},"0.4.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fewlines/fwl-logging":"1.0.0","@fwl/tracing":"0.2.1","express":"4.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.7","@types/jest":"26.0.7","@types/node":"14.0.26","@typescript-eslint/eslint-plugin":"3.7.0","@typescript-eslint/parser":"3.7.0","eslint":"7.5.0","eslint-config-prettier":"6.11.0","eslint-plugin-prettier":"3.1.4","jest":"26.1.0","prettier":"2.0.5","ts-jest":"26.1.3","ts-node":"8.10.2","typescript":"3.9.7"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","test":"jest"},"types":"dist/index.d.ts","version":"0.4.0","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.4.0","dist":{"shasum":"ffab7d33fa01a7b13176acc062b193cb0f8e4342","integrity":"sha512-4KrJrywvmX6yBzJR6PspI9yE4XJnmISMbgWnDRTGK2/Lm7r3ikMHszX/r3Dqph1fz4s8+aW4YKuj4eYcYtrqKg==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.4.0.tgz","fileCount":54,"unpackedSize":387370,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfMr+NCRA9TVsSAnZWagAAREUP+QC3pTtBVj7Lw5I2Md9h\nyJtJd+BqoGtq0dIXjOuWo5DAc/t1PXUA6K7pnvbf2zXsPJ/NlOMOkgCcRmIL\nEw5hsyFxK/EWHqrNwjwyeTnpYKUeKmsvSxv5zD4qbymm0RAkn4PieHOeviRV\ntbO7tS0GeLDr2V2QWqyuoj/bJjFf2Pbg5APd6B5xOkBkgu4lIY62zSw1WEIb\ncNEVK1jiSYT7d7PIgQ4dy7x+CXeOtV4XdalTw069JRRLYf35ipvjqK/ieWlV\nrLPL5GO3xqe8kRrXCxlrJ5xK46Du/ZK7TPnQh5KErEDN57qIsdBRzQ4La9ts\nOwCsctfdzLpYaBIGu2arb25oQlAw4Rt5T0ewbk+/rfgUx4e35lXxpKHQDQEh\nDfxc9g67GRQAqXqhRVlM8CJwp4vBOnhZRLvt/W291ZpxLsBIoZCVs8FppJGi\nPflYW4ShJekvL7cCxF6Gf/htnfwhRXMsBraN89PHSOB1heSocXmwaRd6pDVO\nyrInCISchihVRk+kPBNPNS4crmJF8xaWQTZbZsqsdcJGYSja4b0XobOauqZv\nt9WX2sUBCeo0r+IF0+AmTeel/gE3hiM+JJ2BNaXkzdO1pqFRE9HWkG9NPEHM\ndgEMiEFWzd9gKSoGvDaXYozHAU3jbAIwUvuZBneEu0MEm1o12DREELYjdsBs\nnKIE\r\n=1/CT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDYu2Q4ml+lSvgW7CSe9yJmT3KGTIm76AXnDCEAdF056gIhANWKVaB/htCTkKqXLhYBBPkKkk/t94N3TBR60rDJ4uWx"}]},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.4.0_1597161356613_0.646354979685908"},"_hasShrinkwrap":false},"0.4.1":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fewlines/fwl-logging":"1.0.1","@fwl/tracing":"0.2.1","express":"4.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.7","@types/jest":"26.0.7","@types/node":"14.0.26","@typescript-eslint/eslint-plugin":"3.7.0","@typescript-eslint/parser":"3.7.0","eslint":"7.5.0","eslint-config-prettier":"6.11.0","eslint-plugin-prettier":"3.1.4","jest":"26.1.0","prettier":"2.0.5","ts-jest":"26.1.3","ts-node":"8.10.2","typescript":"3.9.7"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","test":"jest"},"types":"dist/index.d.ts","version":"0.4.1","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.4.1","dist":{"shasum":"645d500087e3d245ff4be31b34580f596b63d3fb","integrity":"sha512-Z2a5Tyf6IrBd30JxcziJZrItQV04ufmWqvE6gebnAsK6WQFPKSbr8tq2LpNqYuJ5Uw78EGrZZaQa9zipyXo+OA==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.4.1.tgz","fileCount":54,"unpackedSize":260586,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfM6gYCRA9TVsSAnZWagAAuBUP/jjvp8d513pHFeADmAXr\npvZPzd0l9bioZGzkcHUBluv8DktGLOECupnwhcqziwFJPYiK1dxiv2Joyu7Q\nL2+3/awLMA4+XEtxKkBedtoPWRZKXLo9CN/LsXBzTQcjcKQF6dlGO96qL69R\nuoXzjouwjibIdQ6/wa+ZcjmtwlwWdg4FCbE4GjXWTigSoKsTfgTaVChDUjbM\nzdn+fgRkIeRFDtvqfbMY2MbFFW/drzGTbAPslksq03HP+zecNcrYeN7aBcHp\nDqFXUkCRYtWdh5pJ+OANhLsDxWRdOWnQrO3/xp8GZi7v6oP2f0RR0m+YS7bv\nl2o0eWKgU4RYBmjXnGCNC0g4KDZKAXAFUTJ+1YSZMfRE2vkQNjSPWmdAh0EA\nfhn9GekgtwANm872TcGCYoWvlQREqumy7x1s8+Rs0b1/j82OrSjwET1l4EgT\nL6NfdGo9LyRXTBV1GNiAfiCpEnTJXmyAO6yOAhPX/fMRuVj+MmZXcW8SmwHp\nPPQgqEdu41V7f/tXmpVnGquM6c7ps90g6aujWKV8Ubnh+dm9ItOt5uHSeSC9\nfxqvqnZChcYw5tD/SrHlzv6sHvBr1402kdlzVzkXZIGth0txokdDoblKlkBD\nukmn8XxH2IdfLgJ+ZdwEzgoMHFApmQWn8HgM4WSf33Dtj+LqxnUhoVUIeE3y\nK5gi\r\n=dlaK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCyOaVkaW3V+zHnZfsmFJ0rRH8MM/VCDAEWT3vL2OPlswIhAPxNiNMQpxnakRfdqvbk3WDNGB1lBktocfwDYrIompmZ"}]},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.4.1_1597220887702_0.4985782739475204"},"_hasShrinkwrap":false},"0.5.1":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fewlines/fwl-logging":"1.0.1","@fwl/tracing":"0.6.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.4","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.9","@types/jest":"26.0.19","@types/node":"14.14.14","@types/on-finished":"^2.3.1","@types/parseurl":"^1.3.1","@types/qs":"^6.9.5","@types/send":"^0.14.5","@typescript-eslint/eslint-plugin":"4.11.0","@typescript-eslint/parser":"4.11.0","eslint":"7.16.0","eslint-config-prettier":"7.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.0","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.4","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.5.1","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.5.1","dist":{"shasum":"e8df6092c6de5530326e314222281336650dfbb1","integrity":"sha512-S9YYhE/Ul58iGM6ZohQhO3ocahzUevcdkNB21Wx/v4JXnpTwNxK43lV/KrIXREXTQjb6KiscU5yO4atBqFkc4Q==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.5.1.tgz","fileCount":95,"unpackedSize":266757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9KT0CRA9TVsSAnZWagAA9pYP/jnBGMsTsa7kQWkU1qXH\n+krpHnXXvIRENWd2DyM/s05hHPwX9b6EbuVjkngJASR3vcLWgub/hegjS9Pc\nXTffTBPtuCIH3GV9UiX5lqYLE7kVKleXPdkXz6Nz2Y1IFT2hY4evjsV2Y3/E\nOxTvxe0s4p/wy5VlIfVu9GHJ6ceN5ChMasg9jdvifddBnNUhvWGCh8xjtyPb\nk1DNC/+aa4kwkfyl+03pRrvzWFS7bIVx8yvpEIUNegg9fQ8YaTh/032R485i\nyRKW8eGqgjtsJsnBtEgj3qkmt89/Ti3c0gMbT7S9wki4nONW6otFCyRsxpAi\nSc+FyFEXay+x6ZL8AtAeBQjxxqRJmHuV2Lhv5seu/kRJ31P4sJO7MKsSy+RV\nrcPfE/a1UyPTztkOrN/tHqSPSUrqwvHaXxDzPO5EfZm+CifTdC8Expebp85p\nMRMqqrBiFJRMWvMCx1lcm9/DJo5L63d0r+Tqfka3Jaf5Ey3iHWhpZl2Y0Cv5\nklXDErZSf5bEKDyPHu5tIHuqn+ur80bYpfMqTkCFhgojRt1H4gT/W21Em03R\njMWf5YsWerkG6S7uItuXYXzZ3RCsEBFod2wQMumOA1koWaUqSCCsVf24TAqy\nwQpcO2dDL7hJgOrWMEravYDN9eVmVkyiOSNX7mjLD+o1V1PaP8nYshLmDc8V\npMgB\r\n=lHhQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICpXDnYWe51+jd0PWOHJhiBZ8Jmb/KGUDZPSZ3QQ7wmbAiEAzPGKz7rY5ov9IMYHvMIv4mHUuD8XG0I1wMv4uJ4NHAk="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.5.1_1609868531864_0.9009098533776547"},"_hasShrinkwrap":false},"0.5.2":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.6.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.4","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.9","@types/jest":"26.0.19","@types/node":"14.14.20","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.5","@typescript-eslint/eslint-plugin":"4.12.0","@typescript-eslint/parser":"4.12.0","eslint":"7.17.0","eslint-config-prettier":"7.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.4","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.5.2","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.5.2","dist":{"shasum":"0bce92bfc7da64a06536ebc1ecf022d36fe14bc3","integrity":"sha512-tTLRoYkjiQNLgWQEZpEADbHOMLeA7b4lKu2TIPOBSr0xGO1vDlBFCqCJUfmExDTmekv0urIfojFHneCuEOhQjw==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.5.2.tgz","fileCount":95,"unpackedSize":268449,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9ckJCRA9TVsSAnZWagAACBEP/jIigfY3xIkkvBtEKzJz\nBsZPYbQ247xbM3kha4XYmxFUQjZCF5qrvxRgjNzfwn7Qq2yrV8G1bFDwphix\njNgtrskfo3Z/p1aJOLPyk4s2/E7x+UugFpAcfNoIABSp2AALloeJ4S2lUXW/\nu/PcCv4h6Tq/RlLrg5Txjscoscy1Ifj/WcbGU+y3E3iwf8EO3UJGpYD/SV1E\nIIKN/Z4/OwRWU1bQciBvFCiL7kvOZj7avqQCdrb+CPTt7Dfu94pYjCtQcCSM\n1NxTH9WU3rDF4xEQTJOlq929y0OH9MpW/lRS1//wH3ELKPvMXBCpl4gwwioK\nPXd3CQPkbuWZUUIwjyFiKGAn3iTQxFFeCGfRr9OGGmGqL8A34tOW3l16kDtZ\nO8PufdnljlsvqjYyZSGX9J9ibJyzwz6UZBvzqB/SF66p1qU3U4rFXUOxKN6u\nf8HkBDB56Kiu6u0/SV24ziZB7KqYnnMQJu1MMZ2kbbuseyglV/gpOqcBZWfc\n5gy4nsOir4loNil8kD8eQ51jV72w6F9vNSVnPgwIKHp/pPff5FqLZO3N5AYa\nW7JTnrAyDyIV7bDOk4mSS/rZGdO+bBCzLft8r4CvH1N1PArpkfVPY58NaWru\nObIGST/3aQTq/JBDVqMRqctgidoZKxhNlPQvv4L1v9vHeb8KwqiCEA6QL7Z1\nY+jj\r\n=jsnp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHvhP5XkYcZd+2puB28SP5a+CQxHMuDY1KjIxWEHaoe7AiEA7zBwNGW/J7uevMqJJ+4EfMlbn2JDnLFO0q6GR+4TQL4="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.5.2_1609943304569_0.8825335316214893"},"_hasShrinkwrap":false},"0.5.3":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.6.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.4","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.9","@types/jest":"26.0.19","@types/node":"14.14.20","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.5","@typescript-eslint/eslint-plugin":"4.12.0","@typescript-eslint/parser":"4.12.0","eslint":"7.17.0","eslint-config-prettier":"7.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.4","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.5.3","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.5.3","dist":{"shasum":"10f146414f77a190f7f2e38bb2c3a5cad9b18e37","integrity":"sha512-QCIs2MSCUYsbmhk7sxluBHnbz2+/S50IJTbfaauJw63cD3iic75uc8oWB/vDxMjvMD9wk8oV6FpZjEfxNWgwgA==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.5.3.tgz","fileCount":95,"unpackedSize":268650,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9efXCRA9TVsSAnZWagAA3LoP/RcCPHyA8Nlv82GgOlJH\nTMCKuSgbLJKJ2idXUdT1UqIINjo367KNBKMU48gvC/RQCRzqQwMLbchoSvgq\nUOYN26dq1b0dWiSG0JBD0xjvQi4tLBvG5NbckMb9x8PR6eEypxECG574LdD8\nEiJy1Y2cy1iuruXA+74XdGYN4emxQdrhXKzjFY0qKb+OOixrJQelVSVBQvtg\nJD5FcOnObsKf4pZlew7QxSdtTj3ev0jrf6RRyBEp1VuOBITF1Bdvjbzf3CWF\nU+IvGtZTOEtRzH+L1qyK8qcWxbbSBscS6V5sfNFE0cXugAp1Q6kCLbOzDGxW\nUR7t0PIuOJi73Ll8qoVbwEl9JOBWphe0KA+FxabZzZoC1zw/q6xlWMcPbU2X\nGMA+tcx3UOe/X8ggY1WkHt504/f4a7cgLghl2WOAPFNmN+u63/QFplhPD9Ra\nccH0cyBCiQR4zENcW9wV85uwJzuaC8l8WxOrhtaz59nvuz780jJapYXTL9fb\nv0aTTBbp+vCwdAMIAZxiO+jyLp9XFHqSPJFjOKOqhTojsElG2e8gUf78abX9\nqt93mhOjNyPhZ6kiRdEpy0fJV2EGuPrM+v7ZgjUldSQ402XUqxoJVQEYu45R\nFjqIAV353ORXC75Rl9m1s91PJkrWzLBcsld2iEaQSqeMvuzIC+1fPZLASch/\nEjPs\r\n=zeu2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICd08YdTcRMgvMoleNFf1DWhKwwIpPOYEvrh/THmfW88AiAtEbnzJZ6AarVsZoQBZzMndiYnEsckeMdwMnzaW8mNnA=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.5.3_1609951191261_0.6575724616885439"},"_hasShrinkwrap":false},"0.6.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.6.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.4","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.9","@types/jest":"26.0.19","@types/node":"14.14.20","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.5","@typescript-eslint/eslint-plugin":"4.12.0","@typescript-eslint/parser":"4.12.0","eslint":"7.17.0","eslint-config-prettier":"7.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.4","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.6.0","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.6.0","dist":{"shasum":"0b4da76d66401602f1cc78ba00043ceef04e373e","integrity":"sha512-kWJ4MUwIiImafMFzaUOkS8HtkAa98UUlGJXicgC/OYSu0ZzNtnqg1ztolIQW5WxDEYx3U4zS1k6m4XJHXz8QWg==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.6.0.tgz","fileCount":95,"unpackedSize":269931,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9ubGCRA9TVsSAnZWagAA1WoP/R0q7qw3MNVJBqm7tj1d\nXL2N2ktEBXi6ithat/KQ8Vd3/QEPgSbuCvzeDQzYRJ8ZZdRbNW4cBUr1rM31\n2dvfYQ/LEsKC6VazMyJOUhYDy8F454FlQF49ln+ii4jRT3FhM0/IU0cC/Pds\nyvSBpyIg3vA2GKEZHWQ5TAYH7na1AUk4nO7btT16Km/zjm0eTKUwLXHoXxn8\nVsxbHmPRptE3FpTYFADaezopWaipdpKaFXc7isuUk8QMI3VY8Ul0cdFRe5aa\n34SuhcS1Dv1JLWT3mtomNtQDaefvNd3oPQ5gDlv4WopWKCsLaAigyL04HVCT\nMRL0+rKH6s/SmCg+xmoO05IqeyEqRSve8xzcvNGs3SOZ7y8xM6mKvQH0WG3k\nt8onm64rybpLTZ4V39nPqtTrIJB/kV2eP3whx6OMqXmd6WIWdnnjTJYEn2/i\nq6v0v9iiO6aua4u4gjNfzy9ojnViZSIw+31vzLHpitnV0f5H3vkHBReveQ29\nSxx9qNxeEAvKZ9Zdd/PsbDXpQ2yFl3vDhq+qighsfrbK7BeQPVqqBwkvgASM\ngBj9+YoEl2oI9SIs9IyPNTHHd5F+apocMIo6wXTYy27WHmLkF3gFPVcoJAqm\nCRWk+OBixx/4eUMt5vutqSGlcRPCvBxK5kpfpXqiP6WYY6rILibGOy7TKauA\njPgi\r\n=X7Vu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICd6tURt4jkWZ1K9+3tJgjUxpLRNlIgtjQozOgVUMq4lAiAmUnlPcCVD7ZiSb4wRExBeJz9kITrdMRsVUwJz4KW/eA=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.6.0_1610016453904_0.5281666039586581"},"_hasShrinkwrap":false},"0.6.1":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.6.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.4","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.9","@types/jest":"26.0.19","@types/node":"14.14.20","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.5","@typescript-eslint/eslint-plugin":"4.12.0","@typescript-eslint/parser":"4.12.0","eslint":"7.17.0","eslint-config-prettier":"7.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.4","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.6.1","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.6.1","dist":{"shasum":"17a1a437408821dbf37102a187d2d86e3f7821f2","integrity":"sha512-7PCDO+6XQEorUF6xBOecUS1WBRFeD9swWK9f2VAoyHMMI/XatLXb6dWDzvGbxVBSUFvre3UaMPW7M5hqhlG4Qg==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.6.1.tgz","fileCount":95,"unpackedSize":270709,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgCq9zCRA9TVsSAnZWagAAyKcQAJ4Ag1rKtgFRZTUb6VN5\no0cc+txxHem8dbPasvP0vO6MH51HyJLzLYUfU1kG5cV+6JGSNYghqkGVtRaV\nF+WOmWj0pjORNGFlxfB/cLyZpiIpM41cMFB8U0aozqce86Qc6QbtOmR84TMC\n6UY9SOSpM07Zai0sBIZ9E85HLqbP1lwtJi6msejnaPviQ+SetsKYK34nsBvE\npzVRNtM55nu6IeLKd9pKePN9T9py/Hd5W9Fa+n0RqUzwev50mBF4TABPBhCs\nW7JlL3O6kQIk0/RM0tI4UMNj4nIuSl8T0eZtUbkNcBqYgTbLJ3pFYA6kYESv\noBvPunbcIqsWfc3c67o9iDy5tG/UnNv+wk/NLs7iyVxOpnwha6QFOnXEs8dn\nE2+nY+7lr0YriFKwx+MqreUHJ/LoIXTyjaFC6CbhvAhYplAODVilBTt3gw0v\nRgVhUiYJ41A2U3uQcfwijhheOyYFdHPFnDAXGAj1x0MZJQrhZFJjBin1ZQPp\nKIGmFzpAicsdVWO2jC/lhSfoDvaDIq8Biov4YWQaNjFKw6Ks6W2LIi77BeXS\niJCVlY8YJzjirkVRz1ZtRqF0Tw23Jspy7HGefCay/P4JoLGOAvV8FUWko0GH\noDti3B29kaZTIVgZZ5dBWabN8kr9CwLqu2NMWz7JkpZTlmf9nID5X6yFFAip\nfABO\r\n=aGQ+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCQHo2uou8DBhww/IxBCsy+/nGmcFIw9qvWxH2kt8Z4ugIhAKaWRSa/dsFiCcKbETkLfKKnWLDdzENxmUrKZK88i/BK"}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.6.1_1611313011208_0.808975878227175"},"_hasShrinkwrap":false},"0.6.2":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.7.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.6.2","_id":"@fwl/web@0.6.2","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-0yu8/VkB40aaOdBTDTxhp4IOSOEgf7ZvpsKBg5ba/kxGQ5t+4kc1UbKKkfwgnjhIxbeVW94dIOOwkIh2Lc+cRw==","shasum":"94b4232df7eae0a83aa478e7930da04791bf8f13","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.6.2.tgz","fileCount":86,"unpackedSize":380454,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgCuXrCRA9TVsSAnZWagAAzQwP/29QAqN+MzWSf+gKcBqc\nOfow2Ya2HFKA5Ir9jqdfE7SIylQM6A8/GrxN/OXjiJVSRaXZmim2bNACOcaP\nQCJXN4LDSoknXCRkU7S96n0TXsaVQFN3gbuO82KRS6rNizeZ79ygZFKXkUfu\nzzEFmtFNJcVgiqJESY8/BiV/13CwTRtp6aQjXqzgH9CqdpFlz/V+1pCIDnR/\nurLO58AgcUMcb/Y9s7UZY+lrZ/06NLrHw/E74vJu7HWfMUD3WOWzE+R4Hrlm\npt9aL8Ufu4shUJWe6qmik1X6f6g2/tvS0Bdskl4p8Baj7Om8dcGsRppOcvag\nktUxRl+nKV7kcbsOtbwAQE5uehp5sdrjkkXQPk7NseSG7ocNVALu1OVwdZ34\nESYybk0b+heTkY1CzBcnwNT2fMxxa5fJWuMeV2IQNkiSo2hwgeAKxS/frHx5\ntme6CL6TuwCY4HXLapld4ZLW/O+MuxnvRXyh4Wm6jbmdV5+PA22XTzvO1TBn\nTSK1CVAq0ijTcXVC3jVT5+HHjdcLzk6WLuS4b/oBUeHUnku0JtEcQ3rrfyhz\n8/IOqSZx/QuQue2gN7+o9FGXPg7yUGkO5w+5uFhLcIPzgnFtPcIrN3hycy/U\nxqjjZqZ99iqB45TEUCTeeYC4B85PXhFY0KVlOZxoMJdEelrsx2niJMB91Rq4\n8KgI\r\n=a37W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChNA1LGKzPO2bWT8PSCj3RZXQ/C07gJDFuY/VQH29iowIge15yrSDnBicUAe7U0u3/pd0A26Xdt7jygDJBX7ZFAR0="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.6.2_1611326954790_0.43101766669674735"},"_hasShrinkwrap":false},"0.0.0-experimental-83512fc":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.6.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.4","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/express":"4.17.9","@types/jest":"26.0.19","@types/node":"14.14.20","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.5","@typescript-eslint/eslint-plugin":"4.12.0","@typescript-eslint/parser":"4.12.0","eslint":"7.17.0","eslint-config-prettier":"7.1.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.4","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.0.0-experimental-83512fc","readmeFilename":"README.md","readme":"# FWL Web\n\n**Disclaimer**: this package is made for our internal usage and is only open source for convenience so we might not consider Pull Requests or Issues. Feel free to fork though.\n\nThis is part of the Fewlines Web Libraries packages.\n\nIt provides a framework on top of `http` to be used with Express or Next.JS.\n\n## Installation\n\n```shell\nyarn add @fwl/web\n```\n\n## Usage\n\n### Definitions\n\nFor the purpose of this package, here are the definitions we will use:\n\n- **path**: a string, the path for which a _handler_ will be invoked.\n- **handler**: a function that will be invoked for a request. It takes a request and a response as parameters and returns `any`.\n- **route**: the combination of a _path_, a _handler_ and an HTTP method (e.g: the _handler_ that will respond to `GET /resource`)\n- **router**: a collection of _routes_\n- **endpoint**: the combination of _handlers_ that will respond for a given request for all HTTP methods (a sort of _route_ for all methods).\n\n### Handler\n\nAt its simplest, a handler would look like that:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(\n  request: IncomingMessage,\n  response: ServerResponse,\n): void {\n  response.statusCode = HttpStatus.OK;\n  response.end(\"OK\");\n}\n```\n\nHowever, we recommend to use tracing, for easier debugging and it should look like this:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (\n    request: IncomingMessage,\n    response: ServerResponse,\n  ): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.statusCode = HttpStatus.OK;\n      response.end(\"OK\");\n    });\n  };\n}\n```\n\nThis is basic dependency injection via a function.\nWe're exporting a function that takes a dependency (`tracer`) that returns a Handler.\nYou could then use it with a Router `router.get(\"/ping\", pingHandler(tracer))` or with an Endpoint `new Endpoint().get(pingHandler(tracer))`.\n\n### Router\n\nA Router is a class that takes a list of middlewares for its constructor.\nYou can then add routes to it by giving it a path and a handler.\n\n`Router` can be parametized with request and response types, allowing your handlers to use these and not have typing problems.\nThis can let us use Express' `Request` and `Response` types and use all of their methods.\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\n\nexport function start(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nIf you need to add a middleware to one route in particular, you can use `wrapMiddlewares`:\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\n    \"/users\",\n    wrapMiddlewares([someAuthMiddleware], userHandler.createUser(tracer)),\n  );\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nYou can also create several routers (take care to add the basic middlewares too though):\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { errorMiddleware, loggingMiddleware } from \"@fwl/web/dist/middlewares\";\nimport { Router } from \"@fwl/web\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n\n  const withAuthRouter = new Router([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n    someAuthMiddleware,\n  ]);\n  withAuthRouter.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\n### Endpoint\n\n`Endpoint` is thought for Next.JS as its main goal is to deal with file based routing.\nCreating an Endpoint lets you set Handlers to HTTP verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().get(handler);\n```\n\nBy doing that, all requests on this endpoint that are not `GET` will not call the Handler and returns a `403 Method Not Allowed`.\n\nYou can also chain it to allow several verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().put(handler).patch(handler);\n```\n\nApplying middlewares can be done like so:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n);\nexport default new Endpoint().get(wrappedHandler);\n```\n\n### Dealing With Errors\n\nThis package provides one way to deal with errors by combining a `WebError` error type and a middleware.\nHere's an example:\n\n```typescript\nexport class UserNotFoundError extends WebError {}\n\nexport function getUserById(tracer: Tracer) {\n  return (request, response): Promise<void> => {\n    return tracer.span(\"get-user-by-id\", async (span) => {\n      span.setDisclosedAttribute(\"user-id\", request.params.id);\n\n      const user = users.find((user) => user.id === request.params.id);\n\n      if (!user) {\n        throw new UserNotFoundError({\n          error: {\n            code: \"TST_201229_YNXL\",\n            message: \"No user found\",\n          },\n          httpStatus: HttpStatus.NOT_FOUND,\n        });\n      }\n\n      response.statusCode = HttpStatus.OK;\n      sendJSON(response, user);\n    });\n  };\n}\n```\n\nThrowing this error will result in a `404 Not Found` with a JSON body with `code` and `message`.\nThis is done by catching the error in the `errorMiddleware` and setting the response if it's an instance of `WebError`.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n### Middlewares\n\nThis package provides a middleware API different from Express.\nInstead of receiving the Request, Response and a function to go to the next middleware (the last middleware being the handler), we choose to have middlewares receive a handler and gives back a handler.\n\nThe main advantage is that a middleware like that can do operations before the handler, after or both.\nFor instance, that's how the logging middleware is constructed: calling the handler, setting the result aside, logging and returning the result.\n\nTo be able to use those middlewares, you can use `wrapMiddlewares` to create a handler with its associated middlewares:\n\n> ⚠️ Middlewares are set in the order of their execution, meaning that the first middleware you put in the array will be the closest to the Handler.\n> Said differently, if `middlewareX` did something that needed to be done before we could use `middlewareY`, the order should be `[middlewareX, middlewareY]`.\n\n```typescript\nimport { wrappMiddlewares } from \"@fwl/web/dist/middlewares\";\n\nconst wrappedhandler = wrapMiddlewares([middleware1, middleware2], handler);\n```\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n#### Logging Middleware\n\nThis middleware will log each requests with:\n\n- the path,\n- the duration of the request,\n- the HTTP method,\n- the IP address that initiated the request,\n- the status code of the request,\n- the trace ID for the request.\n\nThe log message will either be empty in case of success or contain the error message if an error was thrown somewhere.\n\nIf you're throwing a `WebError`, the status code of this error will be logged.\n\n#### Error Middleware\n\nThis middleware will capture `WebError` thrown in middlewares or handlers and return a formatted Response, with the status code of the error and the error code and message as JSON.\n\nIf you don't want to use `WebError`, you can either don't use it or recode your own for your own errors.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n#### Recovery Middleware\n\nThis middleware only goal is to catch errors and return a 500 Internal Server Error.\nThis way, your server will not crash and leak errors when something unexpected happens.\n\nIt will also add the error message and stacktrace in the trace of the request for debugging purposes.\n\n#### Tracing Middleware\n\nThis middleware should not be used with an Express server as the tracer will automatically create traces.\nIt's not the case with Next.JS though and this middleware only task is to create a trace on which to attach the different spans.\n\n> ⚠️ If it's used, it should be the first middleware of the list since the others will try to create spans.\n\n### Usage for Express\n\nThis package provides a `createApp` function that create an Express Application from an empty Express app and a list of Routers.\nYou also can parametize the Router with Express' `Request` and `Response` type to allow your handlers to use these types without any TypeScript problem.\n\nHere's a basic application creation:\n\n```typescript\n// server.ts\nimport { Logger } from \"@fwl/logging\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\nimport {\n  loggingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport express, { Application, Request, Response } from \"express\";\n\nexport function start(tracer: Tracer, logger: Logger): Application {\n  const router = new Router<Request, Response>([\n    loggingMiddleware(tracer, logger),\n    errorMiddleware(tracer),\n    recoveryMiddleware(tracer),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n\n  return createApp(express(), [router]);\n}\n```\n\nAnd after that, you can have a file that starts your server:\n\n```typescript\n// index.ts\nimport { createLogger, EncoderTypeEnum } from \"@fwl/logging\";\nimport { InMemoryTracer } from \"@fwl/tracing\";\n\nimport * as server from \"./server\";\n\nconst logger = createLogger({\n  service: \"fwl-sparta-api\",\n  encoder: EncoderTypeEnum.JSON,\n});\nconst tracer = new InMemoryTracer();\n\nconst applicationServer = server.start(tracer, logger);\n\napplicationServer.listen(process.env.PORT, () => {\n  logger.log(`Server started on http://localhost:${process.env.PORT}`);\n});\n```\n\nTyping the Router like so allows the Handler to look like this:\n\n```typescript\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\nimport { Request, Response } from \"express\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (request: Request, response: Response): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.status(HttpStatus.OK).end(\"OK\");\n    });\n  };\n}\n```\n\n> Notice we couldn't have use `response.status` with a `ServerResponse` type, this function is from Express.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n### Usage for Next.Js\n\nNext.JS does not allow our tracing library to patch the underlying node packages: that means that we need to have a middleware which creates the first span.\n\nHere's an example of the `hello` API page with an added error:\n\n```typescript\nimport { Endpoint, HttpStatus } from \"@fwl/web\";\nimport { WebError } from \"@fwl/web/dist/errors\";\nimport {\n  loggingMiddleware,\n  wrapMiddlewares,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\nimport logger from \"../../logger\";\nimport getTracer from \"../../tracer\";\n\nconst tracer = getTracer();\n\nconst handler = (\n  request: NextApiRequest,\n  response: NextApiResponse,\n): Promise<void> => {\n  return tracer.span(\"hello handler\", async (span) => {\n    if (request.query.someQueryParam) {\n      span.setDisclosedAttribute(\n        \"someQueryParam\",\n        request.query.someQueryParam,\n      );\n      throw new WebError({\n        error: {\n          code: \"1\",\n          message: \"oups\",\n        },\n        httpStatus: HttpStatus.NOT_ACCEPTABLE,\n      });\n    }\n\n    response.statusCode = 200;\n    response.json({ name: \"John Doe\" });\n  });\n};\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n);\nexport default new Endpoint().get(wrappedHandler).getHandler();\n```\n\nUsage in a React Page is a bit different because we always need to be returning something and you may need to access `context.params`.\nYou can use the `getServerSidePropsWithMiddlewares` function that takes the context, a list of middlewares and a handler.\nHere's an example with the same middlewares as the API Page:\n\n```typescript\nimport {\n  loggingMiddleware,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { getServerSidePropsWithMiddlewares } from \"@fwl/web/dist/next\";\n\nexport const getServerSideProps: GetServerSideProps = async (context) => {\n  return getServerSidePropsWithMiddlewares(\n    context,\n    [\n      tracingMiddleware(tracer),\n      recoveryMiddleware(tracer),\n      errorMiddleware(tracer),\n      loggingMiddleware(tracer, logger),\n    ],\n    () => {\n      // do something with `context.params` here\n      return {\n        props: {},\n      };\n    },\n  );\n};\n```\n","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.0.0-experimental-83512fc","dist":{"shasum":"79f8579013b36348096c33c43ddf604fb3e6fc9d","integrity":"sha512-oJ/D8Vt3Y1jfnvivG8zdgcljS+Yx+BK7Qtbo3wIDXEtGeLlqN/VgIy1SxDZmEap9f7fkJMq15A11kwAvLGHfVw==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.0.0-experimental-83512fc.tgz","fileCount":95,"unpackedSize":272068,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgF/r4CRA9TVsSAnZWagAAj5gP/2LVOTmznrMRHrWfis29\nl0IIm0RiKd4JpUJqezqh6jmFlASHo1AnW229t9de+QpDvd2sV6ur4jjd6ifJ\n0ta1HkrNGCddbyV3WVGFAYIknxUDKic9mA+Q1GXQnWVrzqBMbSRhLZgXL1H0\nDh7/ou+zxs5OZlHQEJVz0RRucgg6GQExY2ST5rxqypgA30m8Mm8FHNzKyw2G\n3GiQvqxx6O4tJmweZgCgzXbS5jk2A08MCGHYcfUYIiEFhwadjuo0cjtT7uaZ\nsr2VJ/bW6JP4lJDoRCXu+V95EYmyvLZThNdW7+t3XI45XuH4NgguH3xMU4c/\nSMtmHIRnlI8W6dIYBPWWNS3HtxuUwO0IKBFssudMlO7S8/sHisiFqmaMgRsw\nWH64sKGlSr6aPKy0na71lrKK5KjgHasLRudqMK1ObUF1zY1/YS2BkPyIXj7f\nOXzM3GLsYIVP2wZAQWPLPnmR0wStejwstvwmv3aE5UgV3TKkn1/uHlqx15tq\nVN/gZIdWhioZltbNYeqk8iBWkwLL/3YGhZ94M/HvjHmQc++CebKrK4ZeI7WI\nUCOMxo/mQ0X6MVCKTgKfZ1ht2DRD0l1KIMuk67VLr1VMeJPHXgvB7TP2V1Tk\nQj6939uM0LFYk+kgONmuCFVCrGC5DYNEoij47JcFkHQmUAjUEX3+itYfjB9A\na/qg\r\n=+6u/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDG25i2PCK6T+42dJXQcESvzZAODdRQ9NeCOWV+IXP3ngIhALJvgecHaSS7+QafzndbynRU1aKP+Bz8p+PPD49EM3Ge"}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.0.0-experimental-83512fc_1612184311685_0.5433324917777951"},"_hasShrinkwrap":false,"deprecated":"this version has been deprecated"},"0.7.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.7.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.7.0","_id":"@fwl/web@0.7.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-bzGHfopynNYiKZY67XxdpV+HBfdEXSvW9+d2Nsv24wIK9BkwyjSH7N929c89wkuByTSHViaz3Ym9PE3P8ksqKA==","shasum":"a9213bbef3321cdaf447ae63b62510422f0664bf","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.7.0.tgz","fileCount":89,"unpackedSize":404304,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgGSxKCRA9TVsSAnZWagAAV/AP/AxjreyGaYNRQi2FLito\n18gk4gtNVUGZzZrx5LOr7RmqQ6ELnZYWtAvtU0Ne9AV2TA2Xg5YTIDlS6ndS\nxDJrnqmuLkKJUeb14vmQ0IoEiuBHu4FQt1c7zsRwE1Z2gUJXG4hSFMfSR1TX\nGQ9txpglBe40ISicvZhKxbmE32cNYXeTpbjaItEIO1A1MgAojpR6sM0XO1tX\nS/m7m/TinG442jQ+EaJn2hNyHHP8382a1ikuTr1+kTLp7ypZ9tLZjRv4tmlU\nQ0hh8+EERYo0Yp3lDrQn6ys7iIYoeIoIG1bg9uatarMtMsIdATOT2ehzLfzU\nnFiq1QUa2QkF0e5SJ6zUlHrloEbxeHQylY9ymr++fAOIXbD2ftCHhSCgdmqJ\n5xvi351x1sG1UYLJgHpeINEm48w/3wzy2jEIYymOyaLuj7qUf28DA2pBbjap\nWWjCwD7DqRDDmfxhAoAnMxwkiLFitQB2Kp5jkOczGuyGMhk43HR3E/7MjqZY\nkzBvhmhgsHUxJ9p598HMIYJHhWQO18NRW2R/MD62OU4aN/iQivqO+6B7SioV\nTYf2go7VFz6XAw6dAIdWneJ8wpKoIjS3B/T+TpaENQvf8zQASnrMAX4T8Rft\n2B+TJ8ac7uaD58HV/Pe3y+Sf4dupa2Z9RJZK5zXchR8dPGGhpY7a8e21gav9\nzCh2\r\n=PUS2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB3y9yYO6daf0PHURSXmdXVONi4C/c+dmjVCh/Wp0JQTAiEAp/8xLcHugJgQUf+o2nRpCBn5F7evnuJUSUQifu6J/l0="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.7.0_1612262473429_0.953237069611458"},"_hasShrinkwrap":false},"0.8.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.7.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.8.0","_id":"@fwl/web@0.8.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-3r5uultfA2mlOmT2zNFiE4EGgEnkW2hG45rxTty5Q6g44DgCOxy+l1nci6pMfTEO5X+XVzy1x0VCAq4iosfZIA==","shasum":"c5cc8f253a0a69ce304c79354c27481fcb82204c","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.8.0.tgz","fileCount":89,"unpackedSize":406544,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgG8hSCRA9TVsSAnZWagAAHygP/jqF+Xa6t93be4XMYBqg\nfHUFzclOQIQ3Teh8Nj9FwDlCqzNaYN1+7OOy1+yCDADEIitzpKsZ76taCVM6\nQD1tdNEK9mP0hIsQ0w3DJKDHXQ/SRhE1pZOQCKIm1KoVEqvaTtkQaYHHko1f\niQDc2fiwyGS3mYkU9SvfCQ3O85/x13EMj9hhX8FZ5HMbcI0BKnXflkEvzTCl\n8n2iYY0mG9CejmqbjAYMrzXqa8F0Vo0ns3nOrXRFKYdQaZtjI2ElHHw1pvX8\nfWGhVDn7q9KIZlYusC3KY0PYgRcHOXHlUPAtKfkONdaFL6yj3/4PvDRVfsa8\nwrxqn2KgsEIZTtGAJOXbljZcQmT7B3GWT2PcSReSUzqwf9SU0a/fWVqbEtl3\nwjmiYgBURNM7vTJkpkYsZN0K99uOtIagU446mJ8395xAMrxwiLNDagPxzc9w\n8w2BCzanb06C90cNczNQF2/jE3PmjEqzolM9h09lqrQdkzCXgjs3bG5zxNn1\nP2gYgJjVg5ylf03ocVrIM0fmwepmt+7DH1QlYCc+hEowGfKeLhMTlguRbrqZ\n5rwnXML+2I1k6izXb9SM2rumbfC4SG7o3PVNGlC1iJZPAG/uEZZPf/ZoFEDB\nQMKGrspYhUeg4QV3LvB78AIWjIHtkrixHrEFg818+iRbSGveABOXe3mS1+cI\nprTG\r\n=8Uw4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFQM2l23GZDNtCIknIEH8PlL42d9nargboEQScHjYQVdAiEAoDgjVsYdkMKB+TNSDnJhdXZfm9q8HNQ8leeW7vQmvQw="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.8.0_1612433489894_0.9634172591410088"},"_hasShrinkwrap":false},"0.0.0-experimental-f87237d":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.8.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.0.0-experimental-f87237d","readmeFilename":"README.md","readme":"# FWL Web\n\n**Disclaimer**: this package is made for our internal usage and is only open source for convenience so we might not consider Pull Requests or Issues. Feel free to fork though.\n\nThis is part of the Fewlines Web Libraries packages.\n\nIt provides a framework on top of `http` to be used with Express or Next.JS.\n\n## Installation\n\n```shell\nyarn add @fwl/web\n```\n\n## Usage\n\n### Definitions\n\nFor the purpose of this package, here are the definitions we will use:\n\n- **path**: a string, the path for which a _handler_ will be invoked.\n- **handler**: a function that will be invoked for a request. It takes a request and a response as parameters and returns `any`.\n- **route**: the combination of a _path_, a _handler_ and an HTTP method (e.g: the _handler_ that will respond to `GET /resource`)\n- **router**: a collection of _routes_\n- **endpoint**: the combination of _handlers_ that will respond for a given request for all HTTP methods (a sort of _route_ for all methods).\n\n### Handler\n\nAt its simplest, a handler would look like that:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(\n  request: IncomingMessage,\n  response: ServerResponse,\n): void {\n  response.statusCode = HttpStatus.OK;\n  response.end(\"OK\");\n}\n```\n\nHowever, we recommend to use tracing, for easier debugging and it should look like this:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (\n    request: IncomingMessage,\n    response: ServerResponse,\n  ): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.statusCode = HttpStatus.OK;\n      response.end(\"OK\");\n    });\n  };\n}\n```\n\nThis is basic dependency injection via a function.\nWe're exporting a function that takes a dependency (`tracer`) that returns a Handler.\nYou could then use it with a Router `router.get(\"/ping\", pingHandler(tracer))` or with an Endpoint `new Endpoint().get(pingHandler(tracer))`.\n\n### Router\n\nA Router is a class that takes a list of middlewares for its constructor.\nYou can then add routes to it by giving it a path and a handler.\n\n`Router` can be parametized with request and response types, allowing your handlers to use these and not have typing problems.\nThis can let us use Express' `Request` and `Response` types and use all of their methods.\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\n\nexport function start(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nIf you need to add a middleware to one route in particular, you can use `wrapMiddlewares`:\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\n    \"/users\",\n    wrapMiddlewares([someAuthMiddleware], userHandler.createUser(tracer)),\n  );\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nYou can also create several routers (take care to add the basic middlewares too though):\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { errorMiddleware, loggingMiddleware } from \"@fwl/web/dist/middlewares\";\nimport { Router } from \"@fwl/web\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n\n  const withAuthRouter = new Router([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n    someAuthMiddleware,\n  ]);\n  withAuthRouter.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\n### Endpoint\n\n`Endpoint` is thought for Next.JS as its main goal is to deal with file based routing.\nCreating an Endpoint lets you set Handlers to HTTP verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().get(handler);\n```\n\nBy doing that, all requests on this endpoint that are not `GET` will not call the Handler and returns a `403 Method Not Allowed`.\n\nYou can also chain it to allow several verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().put(handler).patch(handler);\n```\n\nApplying middlewares can be done like so:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n);\nexport default new Endpoint().get(wrappedHandler);\n```\n\n### Dealing With Errors\n\nThis package provides one way to deal with errors by combining a `WebError` error type and a middleware.\nHere's an example:\n\n```typescript\nexport class UserNotFoundError extends WebError {}\n\nexport function getUserById(tracer: Tracer) {\n  return (request, response): Promise<void> => {\n    return tracer.span(\"get-user-by-id\", async (span) => {\n      span.setDisclosedAttribute(\"user-id\", request.params.id);\n\n      const user = users.find((user) => user.id === request.params.id);\n\n      if (!user) {\n        throw new UserNotFoundError({\n          error: {\n            code: \"TST_201229_YNXL\",\n            message: \"No user found\",\n          },\n          httpStatus: HttpStatus.NOT_FOUND,\n        });\n      }\n\n      response.statusCode = HttpStatus.OK;\n      sendJSON(response, user);\n    });\n  };\n}\n```\n\nThrowing this error will result in a `404 Not Found` with a JSON body with `code` and `message`.\nThis is done by catching the error in the `errorMiddleware` and setting the response if it's an instance of `WebError`.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n### Middlewares\n\nThis package provides a middleware API different from Express.\nInstead of receiving the Request, Response and a function to go to the next middleware (the last middleware being the handler), we choose to have middlewares receive a handler and gives back a handler.\n\nThe main advantage is that a middleware like that can do operations before the handler, after or both.\nFor instance, that's how the logging middleware is constructed: calling the handler, setting the result aside, logging and returning the result.\n\nTo be able to use those middlewares, you can use `wrapMiddlewares` to create a handler with its associated middlewares:\n\n> ⚠️ Middlewares are set in the order of their execution, meaning that the first middleware you put in the array will be the closest to the Handler.\n> Said differently, if `middlewareX` did something that needed to be done before we could use `middlewareY`, the order should be `[middlewareX, middlewareY]`.\n\n```typescript\nimport { wrappMiddlewares } from \"@fwl/web/dist/middlewares\";\n\nconst wrappedhandler = wrapMiddlewares([middleware1, middleware2], handler);\n```\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n#### Logging Middleware\n\nThis middleware will log each requests with:\n\n- the path,\n- the duration of the request,\n- the HTTP method,\n- the IP address that initiated the request,\n- the status code of the request,\n- the trace ID for the request.\n\nThe log message will either be empty in case of success or contain the error message if an error was thrown somewhere.\n\nIf you're throwing a `WebError`, the status code of this error will be logged.\n\n#### Error Middleware\n\nThis middleware will capture `WebError` thrown in middlewares or handlers and return a formatted Response, with the status code of the error and the error code and message as JSON.\n\nIf you don't want to use `WebError`, you can either don't use it or recode your own for your own errors.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n#### Recovery Middleware\n\nThis middleware only goal is to catch errors and return a 500 Internal Server Error.\nThis way, your server will not crash and leak errors when something unexpected happens.\n\nIt will also add the error message and stacktrace in the trace of the request for debugging purposes.\n\n#### Tracing Middleware\n\nThis middleware should not be used with an Express server as the tracer will automatically create traces.\nIt's not the case with Next.JS though and this middleware only task is to create a trace on which to attach the different spans.\n\n> ⚠️ If it's used, it should be the first middleware of the list since the others will try to create spans.\n\n### Usage for Express\n\nThis package provides a `createApp` function that create an Express Application from an empty Express app and a list of Routers.\nYou also can parametize the Router with Express' `Request` and `Response` type to allow your handlers to use these types without any TypeScript problem.\n\nHere's a basic application creation:\n\n```typescript\n// server.ts\nimport { Logger } from \"@fwl/logging\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\nimport {\n  loggingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport express, { Application, Request, Response } from \"express\";\n\nexport function start(tracer: Tracer, logger: Logger): Application {\n  const router = new Router<Request, Response>([\n    loggingMiddleware(tracer, logger),\n    errorMiddleware(tracer),\n    recoveryMiddleware(tracer),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n\n  return createApp(express(), [router]);\n}\n```\n\nAnd after that, you can have a file that starts your server:\n\n```typescript\n// index.ts\nimport { createLogger, EncoderTypeEnum } from \"@fwl/logging\";\nimport { InMemoryTracer } from \"@fwl/tracing\";\n\nimport * as server from \"./server\";\n\nconst logger = createLogger({\n  service: \"fwl-sparta-api\",\n  encoder: EncoderTypeEnum.JSON,\n});\nconst tracer = new InMemoryTracer();\n\nconst applicationServer = server.start(tracer, logger);\n\napplicationServer.listen(process.env.PORT, () => {\n  logger.log(`Server started on http://localhost:${process.env.PORT}`);\n});\n```\n\nTyping the Router like so allows the Handler to look like this:\n\n```typescript\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\nimport { Request, Response } from \"express\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (request: Request, response: Response): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.status(HttpStatus.OK).end(\"OK\");\n    });\n  };\n}\n```\n\n> Notice we couldn't have use `response.status` with a `ServerResponse` type, this function is from Express.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n### Usage for Next.Js\n\nNext.JS does not allow our tracing library to patch the underlying node packages: that means that we need to have a middleware which creates the first span.\n\nHere's an example of the `hello` API page with an added error:\n\n```typescript\nimport { Endpoint, HttpStatus } from \"@fwl/web\";\nimport { WebError } from \"@fwl/web/dist/errors\";\nimport {\n  loggingMiddleware,\n  wrapMiddlewares,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\nimport logger from \"../../logger\";\nimport getTracer from \"../../tracer\";\n\nconst tracer = getTracer();\n\nconst handler = (\n  request: NextApiRequest,\n  response: NextApiResponse,\n): Promise<void> => {\n  return tracer.span(\"hello handler\", async (span) => {\n    if (request.query.someQueryParam) {\n      span.setDisclosedAttribute(\n        \"someQueryParam\",\n        request.query.someQueryParam,\n      );\n      throw new WebError({\n        error: {\n          code: \"1\",\n          message: \"oups\",\n        },\n        httpStatus: HttpStatus.NOT_ACCEPTABLE,\n      });\n    }\n\n    response.statusCode = 200;\n    response.json({ name: \"John Doe\" });\n  });\n};\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n);\nexport default new Endpoint().get(wrappedHandler).getHandler();\n```\n\nUsage in a React Page is a bit different because we always need to be returning something and you may need to access `context.params`.\nYou can use the `getServerSidePropsWithMiddlewares` function that takes the context, a list of middlewares and a handler.\nHere's an example with the same middlewares as the API Page:\n\n```typescript\nimport {\n  loggingMiddleware,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { getServerSidePropsWithMiddlewares } from \"@fwl/web/dist/next\";\n\nexport const getServerSideProps: GetServerSideProps = async (context) => {\n  return getServerSidePropsWithMiddlewares(\n    context,\n    [\n      tracingMiddleware(tracer),\n      recoveryMiddleware(tracer),\n      errorMiddleware(tracer),\n      loggingMiddleware(tracer, logger),\n    ],\n    () => {\n      // do something with `context.params` here\n      return {\n        props: {},\n      };\n    },\n  );\n};\n```\n\n## Utils\n\n### Server side cookies\n\n#### setServerSideCookies\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n- The cookie value (anything serializable)\n- The cookie options, which is an object composed of\n  - `shouldCookieBeSealed: boolean`\n  - `cookieSalt?: string`\n  - ..[and the usual cookies options](https://www.npmjs.com/package/cookie#options-1).\n\n```ts\nawait setServerSideCookies(\n  response,\n  \"cookie-name\",\n  { key: \"value\" },\n  {\n    shouldCookieBeSealed: true,\n    cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n    maxAge: 24 * 60 * 60,\n    path: \"/\",\n    httpOnly: true,\n    secure: true,\n  },\n);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\n\n#### getServerSideCookies\n\nUsed to get the value of a cookie on the server side. This function requires as input:\n\n- The request\n- The cookie options, which is an object composed of:\n  - `cookieName: string`\n  - `isCookieSealed: boolean`\n  - `cookieSalt?: string`\n\n```ts\nconst cookie = await getServerSideCookies<UserCookie>(request, {\n  cookieName: \"cookie-name\",\n  isCookieSealed: true,\n  cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n});\n```\n\n#### deleteServerSideCookie\n\nUsed to delete a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n\n```ts\nawait deleteServerSideCookie(response, \"cookie-name\");\n```\n\n#### setAlertMessagesCookie\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- A string or a list of string as cookie value.\n\n```ts\nsetAlertMessagesCookie(response, [\"foo\", \"bar\"]);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\nNote that you will need to un-serialized the value of the cookie, and that the returned value will be a list of string, even if only one message has been set in the `alert-messages` cookie.\n\n```ts\nconst serializedAlertMessages = await getServerSideCookies(request, {\n  cookieName: \"alert-messages\",\n});\n\nconst unSerializedAlertMessages = JSON.parse(serializedAlertMessages);\n```\n","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.0.0-experimental-f87237d","dist":{"shasum":"cb1721c053f231febb8b0afb33944a7c36f39461","integrity":"sha512-Zv8gs+80T+41FPacHGAy9ALTfpj0GBkOJ/UTLm5dmY5KYn59CTRFjj7kPcd1scYIidtevLDC1DZfmvlOVI1xZA==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.0.0-experimental-f87237d.tgz","fileCount":98,"unpackedSize":291903,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIP1sCRA9TVsSAnZWagAAXH4P/RPzMSTEnt34g0lzpg6L\nVRBRWwc1X/bbru1EAiPlwB0HsEFt9Yo2DFR5Yjol3S25xilUZfrtAcYC43DC\n6bVz8az//2EzEIwruCCw88FxiqBWxA8xGoy6Ba9UvAjAwTMHiRpUgt0yIlJK\nSmspFO2FjBEgOwwykkecEYqkhWv8N0vGnzFSqg8bO/DmgV/h5W/JmfK/7WxZ\nxTNwn0xaXsrDF6w2iMhGONV8S2Ii4czltrSPDVSkNx2oQ+R+OvOP4LOIEZQn\nQPzHNwct2kSLVd99CQRnQyDjdTStwuqfQgQKkmZrGpd7yFk/YEhjWreldAPW\nGcGt1IWZYVdy1kvgupsOgji23F9lx4PCHGHmyRZeQeGNF0xSsHdFenvo5/YE\nizZUrMwHAItjfEeetKkDFOcNYCL8+uTG0AjSV0rRTmucgSRG75TATBQDyj74\nyRQzpr4mHQl8rUWXGS3xBpWpxEnsXhQH8UcBtZNmXdAIA9N5WmHsCHEz02cA\n1CDCh8tJTdppzBEpjNNVwkaQ1o7De4oNndKdvbcfjXHWU1iwKBRPW6oFBEpl\npDezYWQMIc98JAFdSojTyL4pOOthd7EZBhm0uOkta0sLeCYL5pZ4y2ziaUCe\nuWJaLRIqcNnDALVg2o0V7wRwcSXX46gVreHKxX7dYhpspAQg7TuY5zTcKGMD\n4i0I\r\n=flTJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBY7jCGtKiYJ/PRe6P9dbvXYwN8ITi7blwDqK0XXiNckAiEA04ITsnM5IUQllmmP7kdRywNKhqdI5nA7EfKagGih77U="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.0.0-experimental-f87237d_1612774763771_0.6857868103599238"},"_hasShrinkwrap":false,"deprecated":"this version has been deprecated"},"0.0.0-experimental-b210d8a":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.8.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.0.0-experimental-b210d8a","readmeFilename":"README.md","readme":"# FWL Web\n\n**Disclaimer**: this package is made for our internal usage and is only open source for convenience so we might not consider Pull Requests or Issues. Feel free to fork though.\n\nThis is part of the Fewlines Web Libraries packages.\n\nIt provides a framework on top of `http` to be used with Express or Next.JS.\n\n## Installation\n\n```shell\nyarn add @fwl/web\n```\n\n## Usage\n\n### Definitions\n\nFor the purpose of this package, here are the definitions we will use:\n\n- **path**: a string, the path for which a _handler_ will be invoked.\n- **handler**: a function that will be invoked for a request. It takes a request and a response as parameters and returns `any`.\n- **route**: the combination of a _path_, a _handler_ and an HTTP method (e.g: the _handler_ that will respond to `GET /resource`)\n- **router**: a collection of _routes_\n- **endpoint**: the combination of _handlers_ that will respond for a given request for all HTTP methods (a sort of _route_ for all methods).\n\n### Handler\n\nAt its simplest, a handler would look like that:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(\n  request: IncomingMessage,\n  response: ServerResponse,\n): void {\n  response.statusCode = HttpStatus.OK;\n  response.end(\"OK\");\n}\n```\n\nHowever, we recommend to use tracing, for easier debugging and it should look like this:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (\n    request: IncomingMessage,\n    response: ServerResponse,\n  ): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.statusCode = HttpStatus.OK;\n      response.end(\"OK\");\n    });\n  };\n}\n```\n\nThis is basic dependency injection via a function.\nWe're exporting a function that takes a dependency (`tracer`) that returns a Handler.\nYou could then use it with a Router `router.get(\"/ping\", pingHandler(tracer))` or with an Endpoint `new Endpoint().get(pingHandler(tracer))`.\n\n### Router\n\nA Router is a class that takes a list of middlewares for its constructor.\nYou can then add routes to it by giving it a path and a handler.\n\n`Router` can be parametized with request and response types, allowing your handlers to use these and not have typing problems.\nThis can let us use Express' `Request` and `Response` types and use all of their methods.\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\n\nexport function start(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nIf you need to add a middleware to one route in particular, you can use `wrapMiddlewares`:\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\n    \"/users\",\n    wrapMiddlewares([someAuthMiddleware], userHandler.createUser(tracer)),\n  );\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nYou can also create several routers (take care to add the basic middlewares too though):\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { errorMiddleware, loggingMiddleware } from \"@fwl/web/dist/middlewares\";\nimport { Router } from \"@fwl/web\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n\n  const withAuthRouter = new Router([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n    someAuthMiddleware,\n  ]);\n  withAuthRouter.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\n### Endpoint\n\n`Endpoint` is thought for Next.JS as its main goal is to deal with file based routing.\nCreating an Endpoint lets you set Handlers to HTTP verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().get(handler);\n```\n\nBy doing that, all requests on this endpoint that are not `GET` will not call the Handler and returns a `403 Method Not Allowed`.\n\nYou can also chain it to allow several verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().put(handler).patch(handler);\n```\n\nApplying middlewares can be done like so:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n);\nexport default new Endpoint().get(wrappedHandler);\n```\n\n### Dealing With Errors\n\nThis package provides one way to deal with errors by combining a `WebError` error type and a middleware.\nHere's an example:\n\n```typescript\nexport class UserNotFoundError extends WebError {}\n\nexport function getUserById(tracer: Tracer) {\n  return (request, response): Promise<void> => {\n    return tracer.span(\"get-user-by-id\", async (span) => {\n      span.setDisclosedAttribute(\"user-id\", request.params.id);\n\n      const user = users.find((user) => user.id === request.params.id);\n\n      if (!user) {\n        throw new UserNotFoundError({\n          error: {\n            code: \"TST_201229_YNXL\",\n            message: \"No user found\",\n          },\n          httpStatus: HttpStatus.NOT_FOUND,\n        });\n      }\n\n      response.statusCode = HttpStatus.OK;\n      sendJSON(response, user);\n    });\n  };\n}\n```\n\nThrowing this error will result in a `404 Not Found` with a JSON body with `code` and `message`.\nThis is done by catching the error in the `errorMiddleware` and setting the response if it's an instance of `WebError`.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n### Middlewares\n\nThis package provides a middleware API different from Express.\nInstead of receiving the Request, Response and a function to go to the next middleware (the last middleware being the handler), we choose to have middlewares receive a handler and gives back a handler.\n\nThe main advantage is that a middleware like that can do operations before the handler, after or both.\nFor instance, that's how the logging middleware is constructed: calling the handler, setting the result aside, logging and returning the result.\n\nTo be able to use those middlewares, you can use `wrapMiddlewares` to create a handler with its associated middlewares:\n\n> ⚠️ Middlewares are set in the order of their execution, meaning that the first middleware you put in the array will be the closest to the Handler.\n> Said differently, if `middlewareX` did something that needed to be done before we could use `middlewareY`, the order should be `[middlewareX, middlewareY]`.\n\n```typescript\nimport { wrappMiddlewares } from \"@fwl/web/dist/middlewares\";\n\nconst wrappedhandler = wrapMiddlewares([middleware1, middleware2], handler);\n```\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n#### Logging Middleware\n\nThis middleware will log each requests with:\n\n- the path,\n- the duration of the request,\n- the HTTP method,\n- the IP address that initiated the request,\n- the status code of the request,\n- the trace ID for the request.\n\nThe log message will either be empty in case of success or contain the error message if an error was thrown somewhere.\n\nIf you're throwing a `WebError`, the status code of this error will be logged.\n\n#### Error Middleware\n\nThis middleware will capture `WebError` thrown in middlewares or handlers and return a formatted Response, with the status code of the error and the error code and message as JSON.\n\nIf you don't want to use `WebError`, you can either don't use it or recode your own for your own errors.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n#### Recovery Middleware\n\nThis middleware only goal is to catch errors and return a 500 Internal Server Error.\nThis way, your server will not crash and leak errors when something unexpected happens.\n\nIt will also add the error message and stacktrace in the trace of the request for debugging purposes.\n\n#### Tracing Middleware\n\nThis middleware should not be used with an Express server as the tracer will automatically create traces.\nIt's not the case with Next.JS though and this middleware only task is to create a trace on which to attach the different spans.\n\n> ⚠️ If it's used, it should be the first middleware of the list since the others will try to create spans.\n\n### Usage for Express\n\nThis package provides a `createApp` function that create an Express Application from an empty Express app and a list of Routers.\nYou also can parametize the Router with Express' `Request` and `Response` type to allow your handlers to use these types without any TypeScript problem.\n\nHere's a basic application creation:\n\n```typescript\n// server.ts\nimport { Logger } from \"@fwl/logging\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\nimport {\n  loggingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport express, { Application, Request, Response } from \"express\";\n\nexport function start(tracer: Tracer, logger: Logger): Application {\n  const router = new Router<Request, Response>([\n    loggingMiddleware(tracer, logger),\n    errorMiddleware(tracer),\n    recoveryMiddleware(tracer),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n\n  return createApp(express(), [router]);\n}\n```\n\nAnd after that, you can have a file that starts your server:\n\n```typescript\n// index.ts\nimport { createLogger, EncoderTypeEnum } from \"@fwl/logging\";\nimport { InMemoryTracer } from \"@fwl/tracing\";\n\nimport * as server from \"./server\";\n\nconst logger = createLogger({\n  service: \"fwl-sparta-api\",\n  encoder: EncoderTypeEnum.JSON,\n});\nconst tracer = new InMemoryTracer();\n\nconst applicationServer = server.start(tracer, logger);\n\napplicationServer.listen(process.env.PORT, () => {\n  logger.log(`Server started on http://localhost:${process.env.PORT}`);\n});\n```\n\nTyping the Router like so allows the Handler to look like this:\n\n```typescript\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\nimport { Request, Response } from \"express\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (request: Request, response: Response): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.status(HttpStatus.OK).end(\"OK\");\n    });\n  };\n}\n```\n\n> Notice we couldn't have use `response.status` with a `ServerResponse` type, this function is from Express.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n### Usage for Next.Js\n\nNext.JS does not allow our tracing library to patch the underlying node packages: that means that we need to have a middleware which creates the first span.\n\nHere's an example of the `hello` API page with an added error:\n\n```typescript\nimport { Endpoint, HttpStatus } from \"@fwl/web\";\nimport { WebError } from \"@fwl/web/dist/errors\";\nimport {\n  loggingMiddleware,\n  wrapMiddlewares,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\nimport logger from \"../../logger\";\nimport getTracer from \"../../tracer\";\n\nconst tracer = getTracer();\n\nconst handler = (\n  request: NextApiRequest,\n  response: NextApiResponse,\n): Promise<void> => {\n  return tracer.span(\"hello handler\", async (span) => {\n    if (request.query.someQueryParam) {\n      span.setDisclosedAttribute(\n        \"someQueryParam\",\n        request.query.someQueryParam,\n      );\n      throw new WebError({\n        error: {\n          code: \"1\",\n          message: \"oups\",\n        },\n        httpStatus: HttpStatus.NOT_ACCEPTABLE,\n      });\n    }\n\n    response.statusCode = 200;\n    response.json({ name: \"John Doe\" });\n  });\n};\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n);\nexport default new Endpoint().get(wrappedHandler).getHandler();\n```\n\nUsage in a React Page is a bit different because we always need to be returning something and you may need to access `context.params`.\nYou can use the `getServerSidePropsWithMiddlewares` function that takes the context, a list of middlewares and a handler.\nHere's an example with the same middlewares as the API Page:\n\n```typescript\nimport {\n  loggingMiddleware,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { getServerSidePropsWithMiddlewares } from \"@fwl/web/dist/next\";\n\nexport const getServerSideProps: GetServerSideProps = async (context) => {\n  return getServerSidePropsWithMiddlewares(\n    context,\n    [\n      tracingMiddleware(tracer),\n      recoveryMiddleware(tracer),\n      errorMiddleware(tracer),\n      loggingMiddleware(tracer, logger),\n    ],\n    () => {\n      // do something with `context.params` here\n      return {\n        props: {},\n      };\n    },\n  );\n};\n```\n\n## Utils\n\n### Server side cookies\n\n#### setServerSideCookies\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n- The cookie value (anything serializable)\n- The cookie options, which is an object composed of\n  - `shouldCookieBeSealed: boolean`\n  - `cookieSalt?: string`\n  - ..[and the usual cookies options](https://www.npmjs.com/package/cookie#options-1).\n\n```ts\nawait setServerSideCookies(\n  response,\n  \"cookie-name\",\n  { key: \"value\" },\n  {\n    shouldCookieBeSealed: true,\n    cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n    maxAge: 24 * 60 * 60,\n    path: \"/\",\n    httpOnly: true,\n    secure: true,\n  },\n);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\n\n#### getServerSideCookies\n\nUsed to get the value of a cookie on the server side. This function requires as input:\n\n- The request\n- The cookie options, which is an object composed of:\n  - `cookieName: string`\n  - `isCookieSealed: boolean`\n  - `cookieSalt?: string`\n\n```ts\nconst cookie = await getServerSideCookies<UserCookie>(request, {\n  cookieName: \"cookie-name\",\n  isCookieSealed: true,\n  cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n});\n```\n\n#### deleteServerSideCookie\n\nUsed to delete a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n\n```ts\nawait deleteServerSideCookie(response, \"cookie-name\");\n```\n\n#### setAlertMessagesCookie\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- A string or a list of string as cookie value.\n\n```ts\nsetAlertMessagesCookie(response, [\"foo\", \"bar\"]);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\nNote that you will need to un-serialized the value of the cookie, and that the returned value will be a list of string, even if only one message has been set in the `alert-messages` cookie.\n\n```ts\nconst serializedAlertMessages = await getServerSideCookies(request, {\n  cookieName: \"alert-messages\",\n});\n\nconst unSerializedAlertMessages = JSON.parse(serializedAlertMessages);\n```\n","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.0.0-experimental-b210d8a","dist":{"shasum":"496d29e774727b2d23361b9e3d6f0c5e5002b1a1","integrity":"sha512-5EmA6OiTvXRczgSP80hNKjmLHxteT2Yb/9zobjCYtXmFvXm3ZmOQa0kVi36f1tjWApSZlcCZStpwSvgxuWvnWQ==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.0.0-experimental-b210d8a.tgz","fileCount":98,"unpackedSize":291959,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIQ34CRA9TVsSAnZWagAATkwP/i4SqdVnunx5VczbiQiO\ntQ8VutLkFZ657SwEDRdIY0FElDOe/C/p+VarRwLuURUYMpaA/KyXANe6AqG+\ntq6NcOr4BgdSd6oGBb3xK8wX+OpWAfYHZC7aAnWw7T2DaxWXSIfFJWWXnw53\nyA7AcudJQ9iXGKjzKiPWfCQdewpZr/MsxqEFOKUlTChlcH2hmudCaRS8/Kv/\nFUcaCASYw8guXawiLEWEmwVoMZjqBjkEmT/ycbP/cbsO/9IehERC5fwqM4Pi\nfSfG4c3pI0SLUA5ZZacM5XwGS7BIUy0GVTwrnc704ZPgh2KJYfb6LgPs6OOX\n+J+ZGTCylx9r1O9d2A04luZDwmBiFALdBo6dwI2f9CVNU0PKI4is3T9AuxEv\nUf2jSgaMJP7KuTLiKVyWG1AxW2Pb1kU1eGTHxSM0U2p5I2EIeGwq1M5OM40e\nvpPB//QGdu4gNRdF4EZgHRhPeImpE7ipXHrUBEy27OGZjyO2EO3K2kCYo9Db\n1q+avXY+XfpYQEMW0BX1R1FprwAQbiQG3Pob8bm9uGdz9ojes1J3ZxfkWTMJ\nCAvW7bAWEKEQedumoJq/PkKvs8K1U2/SxeJ32h+EGI9JMsYV57Xx6iTrJKRk\nvoHczuYWl2Z3oq9cl6nB9pVdrZYGYUbN1Mykx/W/Rjd0X+5vVSmoXEYHjRPA\nO+dI\r\n=jaQZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEK/8PqZj2rQSW7rxuxKWx1vlhW22CwSRV42+JvxFhLVAiAoYVtUfvq+afMbSQxnLWtqp7E9VvYdj9I0VPSrI6lYtg=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.0.0-experimental-b210d8a_1612778999671_0.1578324889142808"},"_hasShrinkwrap":false,"deprecated":"This version has been deprecated"},"0.9.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.8.1","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.9.0","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.9.0","dist":{"shasum":"57e5d8e84c351ba8bb5fe1e75bb73ae1d8bac088","integrity":"sha512-YygaQyrbSzXtpsCcPtJF005jhbps2Wb808ziGq2KVQ9BPKryYoXmGVneLsfYjyrl+BSPgFhu94qYVb1kzU3p+w==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.9.0.tgz","fileCount":98,"unpackedSize":292351,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIW6WCRA9TVsSAnZWagAAHVcP/RhS3AeGczzxrvY2FBsy\nXaPHASq95MjLHk1bGYjGoN7c4IDXsL00C6jnntluJQY02KmGPfoVo+F339lQ\n4Ve42NHWT1MtoCulslNn6+fzHpofVK5tCFTvMzAk/J1jWf+wA+jZ4xdWB7vM\nX0NjzDqoIoyR1DiswgDbwcnqobS7NBz6yEWcVMoL5B32ppMxO8exsnC9KXHH\nnH9oabr129JJp93eHHYbyJ8taanX15C2cXe5i8vfAOvPKins4gZf0NP7ljvs\ndx9S95vokqE70DaYszhK8ugHsEAQquieRFmIz3+V/qKJxqBiAeZzAhyZERxE\nkzn4VTNNlbJzuSN5OMqsNYgYBkCGBmMrxf1qg4Ak7EAved30ZWRtVgc1dcMu\nt6W3EShKqqx3suWWju2VOL7xE5GJ11Qw1n45ocZMcZMeLrC2OGXkqpcFcl7e\n57NoJhqXambdNNktGa2hRPRrhPJATUec+vEonK/fTujz81wzuimzBm3c7wSq\nzUtHzY6rqOs8guHiVspZdJ2xct87ZPlZc/2jUgfcGpRvnY9icICer9jTt4GC\nhvtfbJQppX/3Mjh0OwufzGAVoP7+njcecdAUugFMfZMNWwEnPOsefr9DdcUQ\n0wvX8QjPA+haOgANFzxHtbUorpNVY5K1S/6prZI2Xuop6JkSkcb5BvpWD5ED\nkvzr\r\n=wPBi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFDH8V84LNasrTUUjqzFjgk6NVyqenCF+YvUF+TjGz3oAiArWhqzU86PzfYgdtgJIvupgod9nHzZZISfkMjZIRo7ng=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.9.0_1612803733799_0.7070445412987194"},"_hasShrinkwrap":false},"0.10.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.10.0","_id":"@fwl/web@0.10.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-3iy9pk+qcdTMcS35rDBVRXSq2AW70jnqvUs0qSSmQjTCvigqZT6wacSQBd6QaApvAJFhEklP3kGZdGKresec2w==","shasum":"e6e83da92783ca6c7194cad22e0b87e43c8cbe5e","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.10.0.tgz","fileCount":89,"unpackedSize":289843,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIqiqCRA9TVsSAnZWagAAKyYP/jctJpOBdTOOVOKzxvPA\n3kDuDBfFXVALPB4tdRm3RPfhOdzxeIpi9RzOoAC5wvb9ipLCQY32Az9bb3wM\nSCSLi0XVjKUr/hcn/KTuXjnyfiVoy4A/Q3kxlieV82vmueweeCxCTgZjWzTl\nuMnCI2TrANyXah9ZAQ7yLqkkKmbZwaO7tIk7SxUCupbNDvKwQa0ipm7sEQX8\nBXyV/kspxLkkKBQbOLsCSu1fhGKIcaZ0nAUun7e2kRQEVrziyWI9Lny6lED6\ntbe97oT4QmDwDjsD0zb1DW7Bsvkt7MGPOqw7AAR+QctGfRkW4EuBWOBScPon\n5KjbsALIeLrrmWoEPzAen2UoBFwaYE8P+fz7YyO2MazfjGpgQg2b28azZsZe\nTofGUO4kaRycj5qE0iCrcnN7TeBrdfiAZv8Rji2qBbWH/4nt79rKQI7q9ak9\n1hFSoTrxVkVKkPec5AQaenoprYfe2ed7QSN2MvyJU1JqoapRJY68kxSh9Gvm\nBUTsc0ttq/D6jDjORrQOlX5OV7Uhnr3CWWqL2JDuGhTB10mN43G7tUoguNBx\nq/B/Xdr4FbQVz1CGRDXrsTQ9B+4tF0nqYy/g76nllf/o2JtzyvMIuuwTVMaU\ndGoRycuCGfPf8WfNr8187RvS3Mo+EMgYyIde4XE5Xu6Tjpf9nZ24ZmtGfEUr\nfPzP\r\n=GsTf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCd8QrX93RJ6MAtJd86DqHi3XAGZ8szP4/zJum26nspqwIgIBGKPmJH75IITNGM5sgUf3maut+jflcmS2rVBg0nm+M="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.10.0_1612884137808_0.6408891790067148"},"_hasShrinkwrap":false},"0.10.1":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.10.1","_id":"@fwl/web@0.10.1","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-+2D009oj0LUCitCcFNrkNkC+1zxtaqXW6zUPH9ek/qHuhHS4WOpIuy2xErV99zbXw01/ezmc4tRNA6MyKxVBlQ==","shasum":"de90ddc463e95681a6d282d771d59bf9d427d84d","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.10.1.tgz","fileCount":89,"unpackedSize":408351,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgK+faCRA9TVsSAnZWagAArBEP/iiF4jEQNZn/nt0KBTXF\n7hpBKZAvlFo7SYB6dyxnw9DkZ2kmmzEKpJh7dgQ9+6JW7PY8XndmQZo95mC5\n5MA9v42GrGx8sa6U7bY/TrLGgeLgX1NmRxWB9mOKOE3BW7aL/uyAe113hxSA\nq0fZ0z55hzZnbDMKBuOcch5B3dPx5BBPBE1PeOmXxQ53kdgCxbr78zdkC9/o\nTt7Y1eSynhw9itg20pdftD4DkFHWEmfhdc1H+9gRddwvmbTyLiriJdMa2GWW\nnpgtFb1RuUQ5I3xgxTnz5afUhrF++rTMl6VlqPlCW/P/gU/XXSlPzXK+aByB\nEYRukptDGKVk6I0mdwQwJfj6vIkhm/Ignc609Nxp0nA8wk+CzVsGG6VQZPd0\ngKC9N2BC6FOtfmjLGKy7v2FxYZFXnIScVDy+Wk980ohhXzIlpfwZ8WnUeblg\nM080fE+U6jtHuonYsZ1W7yg0kJhR2H0Sxxtvb3cgVsDSQ3AZu4E3fC7QQOIU\nnR6pWdPgI8FNU+/cdDw+YqTguccXJGS5cyn6gWKfMpy+K/VNrvWK3TcmrFYo\n92WDSsaQREFViZewidak94FyK6D1t+DVoYH01bJBLJLe9QPNSltVph3AJ4yB\ngxq9zYhMJnvkjPoLC6NgZiyyNP4Js4aNIZmkqRtFduvKO2eWAtpYBVPVbD/o\nk4/s\r\n=iFfB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDJE/7PV41qO0gRrzzp9hR9/WdOsUYiVQH5HfsB+4OpvgIgdI/V72EoYWH5vrm47YmhKOBMxo1axtnW2kE1hZpp3hA="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.10.1_1613490138229_0.6323180627430922"},"_hasShrinkwrap":false},"0.10.2":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","eslint":"7.18.0","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.10.2","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.10.2","dist":{"shasum":"41f3b89cbc87b5c4e0a0b6ec70270070256426be","integrity":"sha512-9h88WMkdhFuV8LsBh0CsdBGrFCgcUG5W4PYS7dCLeQbA5zn2AVdiH34urpj4SGXqA76bFWgkQt9EXj8RdNtm5g==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.10.2.tgz","fileCount":104,"unpackedSize":332658,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLVG9CRA9TVsSAnZWagAAr4gQAJ1gOgP6vQymd54zYwMH\n9AXu+4XwViXI9DmtFovsm7OCC/rxhpftEoO0gYJbj6U7XrcExC4SUf4DVEDh\nS2h6L7UZlOHw9n8DZKK7fQmY3EsPKADGCHrcIZJpxdXm6V71hBAeeyZgahWw\nWVUjs0DzbU3jn7V9Lqc2nDgKaU0IBUWvBeTtWx+Tk5M+mAZVNiDXn3efXfL0\n2N0F4J5qNPuBxiX7Z96ZuFGeQ75MqimxZ6GYZLfUnRqw9dW14JrKKy1G8XKt\nQq91hhkRYjNBPeXJYSYT8wsgrvAp2mhTVehsmazv9R2g6Wlb6UgbfGDbsS1w\nlkmwnKGZahHJchEh5xkgoI7QDUMPkr3ltifoWX1/ZQWeVFpZc7Vbdo9JBqZs\nAvSasccxODxx3eMe9SO0zB0rSOxpGDYGPZOKygoqkQUYcMFpB2BmNUlIHndH\ntzqGmcwB/oVsfWnqfqaR/NRWMKzXkIah46oYBDUs81KEy1fhdIG30Oz6Dyha\n47DMSrwBk0qu3Uqa/rY1mr8ZyFwrTD19YooQu9MLCY2rVsz0g8Zgi39UiPJs\niXo+CqWFz6gn6hjGifwd0Ncw5Ckfh0rPdzxeueM4iOciJnyiu//Gm3JY/P0C\nnEaQqXuyBqRSES9rtDQStlDdzp5VKW7POzBuCWPB/iaRwkPfbdeQYRT6oqrC\nyVAB\r\n=GpY0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFSsnlgme/zu2b4HHArzvy0N7yjHRRD8drH6gfVcTlQWAiAC3pXiFrbKN2VWwi//E9SE62cWbUtt+hG5afH7sSRTbg=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.10.2_1613582780563_0.6695740740758067"},"_hasShrinkwrap":false},"0.10.3":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","eslint":"7.18.0","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.10.3","_id":"@fwl/web@0.10.3","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-j+gqEzGFvZR2wtfDxQSO0qT0i0T25fVPDXUtuI3azW1gk8hxXoCGa7S7gDtmR2XjYoHJMF9Hm55LPeMFOj02VQ==","shasum":"cdbeee29f41c2b3f94041542a1bbf501e8ae6840","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.10.3.tgz","fileCount":95,"unpackedSize":452557,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgLopaCRA9TVsSAnZWagAAwo0P/AxzzEs9zbC5DoJ2sq4p\njMTHqbWz9KEYs9gvspIrYEv8rsTT/ylJKJWbn+7SBrQ5G6QRzdd6wj/RG4/p\nXcFeiuZM+fZ7Ul9ymF6hln2n1A9zQ2aqo4HIasW77GLSUBNsp36rntdED3QG\nQCAm68CwgNCx/WOXprrH2xcUUm5nzUp23ZAhATeEwvIwhZDDgVnhO/VSmW6S\nY6uwFMAFasprwLUPRewfN9O4x28qPM0g0XQUDRVYNTteqePyodr2WSNrLLOg\nGkUYcrUo4o2cg6hncTrlCNjYZGsb37jUfOd1HJRYi2cfnxVt2AbtoYXT3IWx\nubiCqnPzHNHyjboRtQT2UP7bn2eZT0mc8ucaf8zV/nePt8oYZvgRw0GSEuSA\nLsrTlqRoq8JO+CM5FWQMMpSJZC7iRQiD5dz8idXhanqC/ubOjrYeqx1eblFT\nxxxvh/4Op4U7jqkXeU+2NjUs2crE2pY2aYfpNhlLL14WFVxAps0Ds0dAu3ia\nIDd231x2WvolaI20bJ8mDhHmWjVOvRy88iBwMwFTQJ+fPMbd+77bDRoH5Syw\ngqWxIpDFB2NiBxVNMQod8uWn0qRAsWHfgeemJnv66t7F9ERksJQUWluH0mNG\n6SL6Yn5bfPTqneXhoDDEK6TEdh624L9E7bi8iOzOnok7xc0CrempPMEQ+CrE\nSRjy\r\n=/uFI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCw3uFHJuek0I/RlcHd6RKcdl7mZsiFM5f5m7ROBD3ZmQIhAMGqb2LVeowTsxWXVKYiHUzMfjbcsIMZVkwRLnxWcPxR"}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.10.3_1613662810311_0.06981629105854048"},"_hasShrinkwrap":false},"0.10.4":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","eslint":"7.18.0","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.10.4","_id":"@fwl/web@0.10.4","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-cjhgnLCNrmTBi4EpFfAS7lZfh0njK/8y+Zzj6y52JNgkoW3X5wfaxvla26hSC5gx+7Sas1WfC/Uy+d4oXg1Saw==","shasum":"b0daf90dd4beb3e7d0052593e67468ec7269e0ca","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.10.4.tgz","fileCount":95,"unpackedSize":454277,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgL7hYCRA9TVsSAnZWagAA6YMQAJhoUAJbD6v6hXj9CW9U\nLmmnYQq9j9HPsZpV8N8dXatNWJEw0ey6/KLwmoAywcSH1ggv8JOArYngAMaj\nRz0CR7foXhQSxkWEDnx+r/QOHbrE/yHMPwxri4wwstFi+emVibDM3S+bi3b+\nznG518hmy4vJdzemhFaKZhWSsa58Oaupm4/HCBoTzI9C40SoiGt7cjrEG+Ki\n0pqVx7BYMYr2UkV+tclqzcWvicdaSZ8qIue4/4ZZSBz+Am1XVkIDDUaSHwOG\nGQJNMOINUqxKjwC5Yme7dLNbIVqYbvjw+OdvTMS4qyb3oiB0ISQvcyrMD2ub\n+8m/iaUqR2yRypXlgfQkvg8v9uSJYc34Wz75h0sVI416bcN1t4LaXGo4aTSm\nBNqzAk8TOU7HHQ6gOykpUpBXCjzP60aGR746OUcr3Zd+4SOxVlHo+MdrAwGS\ntUtZkh24hm/fA5pw6jPrCn9IacJqwx+1Chdotr9P4ynMS6lSneLecJfO8+3G\nE4oTx3ZhMyLeNRrMb2ZiZUFZKIEjcgYWjsbphW9Isb/oy7KCCToNiYp4uxkG\n4gLnLG624zBTDH5Yw2WwdGReCwPadgitq+EzHTNDGtpDkSgnlknOl7qUnE27\ndeswWwdpd9Kl8sdvpli3c+Wc+RHvWI4JotMepdRpjtPuxtTefQzqc+qdKeAs\nINlJ\r\n=83VF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDlKJgXQjrBeUQXtUwlH48cbjSg6sFwey3xb/55k97HgwIgEM5Pgdh8buFxA9/OK9By7fGRskhW5As53fYTlvYmTpg="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.10.4_1613740119822_0.5142675007574358"},"_hasShrinkwrap":false},"0.0.0-experimental-2821681":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","cookie":"0.4.1","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.0.0-experimental-2821681","readmeFilename":"README.md","readme":"# FWL Web\n\n**Disclaimer**: this package is made for our internal usage and is only open source for convenience so we might not consider Pull Requests or Issues. Feel free to fork though.\n\nThis is part of the Fewlines Web Libraries packages.\n\nIt provides a framework on top of `http` to be used with Express or Next.JS.\n\n## Installation\n\n```shell\nyarn add @fwl/web\n```\n\n## Usage\n\n### Definitions\n\nFor the purpose of this package, here are the definitions we will use:\n\n- **path**: a string, the path for which a _handler_ will be invoked.\n- **handler**: a function that will be invoked for a request. It takes a request and a response as parameters and returns `any`.\n- **route**: the combination of a _path_, a _handler_ and an HTTP method (e.g: the _handler_ that will respond to `GET /resource`)\n- **router**: a collection of _routes_\n- **endpoint**: the combination of _handlers_ that will respond for a given request for all HTTP methods (a sort of _route_ for all methods).\n\n### Handler\n\nAt its simplest, a handler would look like that:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(\n  request: IncomingMessage,\n  response: ServerResponse\n): void {\n  response.statusCode = HttpStatus.OK;\n  response.end(\"OK\");\n}\n```\n\nHowever, we recommend to use tracing, for easier debugging and it should look like this:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (\n    request: IncomingMessage,\n    response: ServerResponse\n  ): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.statusCode = HttpStatus.OK;\n      response.end(\"OK\");\n    });\n  };\n}\n```\n\nThis is basic dependency injection via a function.\nWe're exporting a function that takes a dependency (`tracer`) that returns a Handler.\nYou could then use it with a Router `router.get(\"/ping\", pingHandler(tracer))` or with an Endpoint `new Endpoint().get(pingHandler(tracer))`.\n\n### Router\n\nA Router is a class that takes a list of middlewares for its constructor.\nYou can then add routes to it by giving it a path and a handler.\n\n`Router` can be parametized with request and response types, allowing your handlers to use these and not have typing problems.\nThis can let us use Express' `Request` and `Response` types and use all of their methods.\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\n\nexport function start(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nIf you need to add a middleware to one route in particular, you can use `wrapMiddlewares`:\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\n    \"/users\",\n    wrapMiddlewares([someAuthMiddleware], userHandler.createUser(tracer))\n  );\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nYou can also create several routers (take care to add the basic middlewares too though):\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { errorMiddleware, loggingMiddleware } from \"@fwl/web/dist/middlewares\";\nimport { Router } from \"@fwl/web\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n\n  const withAuthRouter = new Router([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n    someAuthMiddleware,\n  ]);\n  withAuthRouter.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\n### Endpoint\n\n`Endpoint` is thought for Next.JS as its main goal is to deal with file based routing.\nCreating an Endpoint lets you set Handlers to HTTP verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().get(handler);\n```\n\nBy doing that, all requests on this endpoint that are not `GET` will not call the Handler and returns a `403 Method Not Allowed`.\n\nYou can also chain it to allow several verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().put(handler).patch(handler);\n```\n\nApplying middlewares can be done like so:\n\n```typescript\n// pages/api/hello.ts\n\nimport { Endpoint } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n  \"/api/hello\"\n);\nexport default new Endpoint().get(wrappedHandler);\n```\n\n### Dealing With Errors\n\nThis package provides one way to deal with errors by combining a `WebError` error type and a middleware.\nHere's an example:\n\n```typescript\nexport class UserNotFoundError extends WebError {}\n\nexport function getUserById(tracer: Tracer) {\n  return (request, response): Promise<void> => {\n    return tracer.span(\"get-user-by-id\", async (span) => {\n      span.setDisclosedAttribute(\"user-id\", request.params.id);\n\n      const user = users.find((user) => user.id === request.params.id);\n\n      if (!user) {\n        throw new UserNotFoundError({\n          error: {\n            code: \"TST_201229_YNXL\",\n            message: \"No user found\",\n          },\n          httpStatus: HttpStatus.NOT_FOUND,\n        });\n      }\n\n      response.statusCode = HttpStatus.OK;\n      sendJSON(response, user);\n    });\n  };\n}\n```\n\nThrowing this error will result in a `404 Not Found` with a JSON body with `code` and `message`.\nThis is done by catching the error in the `errorMiddleware` and setting the response if it's an instance of `WebError`.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\nYou can also specify the optionnal `parentError` attribute when throwing a new `WebError`. When this attribute is provided with the `error` that triggered the `WebError`, both the `errorMiddleware` and `loggingMiddleware`\nwill use it to display its name and message along with the `WebError`'s ones in traces and logs.\n\n```typescript\ntry {\n  await insertUser(user);\n} catch (error) {\n  if (error instanceof UserAlreadyExistsError) {\n    throw new WebError({\n      error: {\n        code: \"user_already_exists\",\n        message: \"User already exists\",\n      },\n      httpStatus: HttpStatus.BAD_REQUEST,\n      parentError: error,\n    });\n  }\n  throw error;\n}\n```\n\n### Middlewares\n\nThis package provides a middleware API different from Express.\nInstead of receiving the Request, Response and a function to go to the next middleware (the last middleware being the handler), we choose to have middlewares receive a handler and gives back a handler.\n\nThe main advantage is that a middleware like that can do operations before the handler, after or both.\nFor instance, that's how the logging middleware is constructed: calling the handler, setting the result aside, logging and returning the result.\n\nTo be able to use those middlewares, you can use `wrapMiddlewares` to create a handler with its associated middlewares:\n\n> ⚠️ Middlewares are set in the order of their execution, meaning that the first middleware you put in the array will be the closest to the Handler.\n> Said differently, if `middlewareX` did something that needed to be done before we could use `middlewareY`, the order should be `[middlewareX, middlewareY]`.\n\n```typescript\nimport { wrappMiddlewares } from \"@fwl/web/dist/middlewares\";\n\nconst wrappedhandler = wrapMiddlewares(\n  [middleware1, middleware2],\n  handler,\n  routePath\n);\n```\n\nThe last parameter `routePath` is optionnal but will be used to name the tracing span on requests.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n#### Logging Middleware\n\nThis middleware will log each requests with:\n\n- the path,\n- the duration of the request,\n- the HTTP method,\n- the IP address that initiated the request,\n- the status code of the request,\n- the trace ID for the request.\n\nThe log message will either be empty in case of success or contain the error message if an error was thrown somewhere.\n\nIf you're throwing a `WebError`, the status code of this error will be logged.\n\n#### Error Middleware\n\nThis middleware will capture `WebError` thrown in middlewares or handlers and return a formatted Response, with the status code of the error and the error code and message as JSON.\n\nIf you don't want to use `WebError`, you can either don't use it or recode your own for your own errors.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n#### Recovery Middleware\n\nThis middleware only goal is to catch errors and return a 500 Internal Server Error.\nThis way, your server will not crash and leak errors when something unexpected happens.\n\nIt will also add the error message and stacktrace in the trace of the request for debugging purposes.\n\n#### Tracing Middleware\n\nThis middleware will create a trace per request, it should be used when we want to use tracing with Express and Next.JS.\n\n> ⚠️ If it's used, it should be the first middleware of the list since the others will try to create spans.\n\n#### Rate Limiting Middleware\n\nThis middleware can be placed right after the Tracing middleware and will block or slow down requests if there are too many hits from the same IP address.\nIt takes the `tracer` and `logger` as parameter as well as an `options` object:\n\n- `windowMs` a number of milliseconds, the window of time during which the count for one IP will be logged in. If you're blocked, you will need to wait that much time before attempting new requests.\n- `requestsUntilBlock` is the number of requests that are accepted in the `windowMs` time before blocking this IP.\n- `slowdown` is optional, if set, the middleware will first artificially makes the requests slower. It's an object with:\n  - `requestsUntilDelay` is the number of requests that are accepted before starting to slow down subsequent requests.\n  - `incrementalDelayBetweenRequestMs` is the number of milliseconds that will be added to each requests when slowing down. Each requests will be incrementally longer (for instance, if this value is `500`, the first slow down request will take 500ms longer, the following 1000ms longer, the following 1500ms etc...).\n    > The maximum delay will be 3 seconds.\n- `memcachedClient` is optional. If you want to use Memcache as a storage for this middleware, you will need to provide a `memjs.Client` object.\n\nIf this middleware is used and a request is blocked, it will act as the logging middleware.\nIf the request is slowed down, it will add a span that represent this artifical slow down.\n\n### Usage for Express\n\nThis package provides a `createApp` function that create an Express Application from an empty Express app and a list of Routers.\nYou also can parametize the Router with Express' `Request` and `Response` type to allow your handlers to use these types without any TypeScript problem.\n\nHere's a basic application creation:\n\n```typescript\n// server.ts\nimport { Logger } from \"@fwl/logging\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\nimport {\n  loggingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport express, { Application, Request, Response } from \"express\";\n\nexport function start(tracer: Tracer, logger: Logger): Application {\n  const router = new Router<Request, Response>([\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n\n  return createApp(express(), [router]);\n}\n```\n\nAnd after that, you can have a file that starts your server:\n\n```typescript\n// index.ts\nimport { createLogger, EncoderTypeEnum } from \"@fwl/logging\";\nimport { InMemoryTracer } from \"@fwl/tracing\";\n\nimport * as server from \"./server\";\n\nconst logger = createLogger({\n  service: \"fwl-sparta-api\",\n  encoder: EncoderTypeEnum.JSON,\n});\nconst tracer = new InMemoryTracer();\n\nconst applicationServer = server.start(tracer, logger);\n\napplicationServer.listen(process.env.PORT, () => {\n  logger.log(`Server started on http://localhost:${process.env.PORT}`);\n});\n```\n\nTyping the Router like so allows the Handler to look like this:\n\n```typescript\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\nimport { Request, Response } from \"express\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (request: Request, response: Response): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.status(HttpStatus.OK).end(\"OK\");\n    });\n  };\n}\n```\n\n> Notice we couldn't have use `response.status` with a `ServerResponse` type, this function is from Express.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n### Usage for Next.Js\n\nNext.JS does not allow our tracing library to patch the underlying node packages: that means that we need to have a middleware which creates the first span.\n\nHere's an example of the `hello` API page with an added error:\n\n```typescript\nimport { Endpoint, HttpStatus } from \"@fwl/web\";\nimport { WebError } from \"@fwl/web/dist/errors\";\nimport {\n  loggingMiddleware,\n  wrapMiddlewares,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\nimport logger from \"../../logger\";\nimport getTracer from \"../../tracer\";\n\nconst tracer = getTracer();\n\nconst handler = (\n  request: NextApiRequest,\n  response: NextApiResponse\n): Promise<void> => {\n  return tracer.span(\"hello handler\", async (span) => {\n    if (request.query.someQueryParam) {\n      span.setDisclosedAttribute(\n        \"someQueryParam\",\n        request.query.someQueryParam\n      );\n      throw new WebError({\n        error: {\n          code: \"1\",\n          message: \"oups\",\n        },\n        httpStatus: HttpStatus.NOT_ACCEPTABLE,\n      });\n    }\n\n    response.statusCode = 200;\n    response.json({ name: \"John Doe\" });\n  });\n};\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler\n);\nexport default new Endpoint().get(wrappedHandler).getHandler();\n```\n\nUsage in a React Page is a bit different because we always need to be returning something and you may need to access `context.params`.\nYou can use the `getServerSidePropsWithMiddlewares` function that takes the context, a list of middlewares and a handler.\nHere's an example with the same middlewares as the API Page:\n\n```typescript\nimport {\n  loggingMiddleware,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { getServerSidePropsWithMiddlewares } from \"@fwl/web/dist/next\";\n\nexport const getServerSideProps: GetServerSideProps = async (context) => {\n  return getServerSidePropsWithMiddlewares(\n    context,\n    [\n      tracingMiddleware(tracer),\n      recoveryMiddleware(tracer),\n      errorMiddleware(tracer),\n      loggingMiddleware(tracer, logger),\n    ],\n    () => {\n      // do something with `context.params` here\n      return {\n        props: {},\n      };\n    }\n  );\n};\n```\n\n## Utils\n\n### Server side cookies\n\n#### setServerSideCookies\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n- The cookie value (anything serializable)\n- The cookie options, which is an object composed of\n  - `shouldCookieBeSealed: boolean`\n  - `cookieSalt?: string`\n  - ..[and the usual cookies options](https://www.npmjs.com/package/cookie#options-1).\n\n```ts\nawait setServerSideCookies(\n  response,\n  \"cookie-name\",\n  { key: \"value\" },\n  {\n    shouldCookieBeSealed: true,\n    cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n    maxAge: 24 * 60 * 60,\n    path: \"/\",\n    httpOnly: true,\n    secure: true,\n  }\n);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\n\n#### getServerSideCookies\n\nUsed to get the value of a cookie on the server side. This function requires as input:\n\n- The request\n- The cookie options, which is an object composed of:\n  - `cookieName: string`\n  - `isCookieSealed: boolean`\n  - `cookieSalt?: string`\n\n```ts\nconst cookie = await getServerSideCookies<UserCookie>(request, {\n  cookieName: \"cookie-name\",\n  isCookieSealed: true,\n  cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n});\n```\n\n#### deleteServerSideCookie\n\nUsed to delete a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n\n```ts\nawait deleteServerSideCookie(response, \"cookie-name\");\n```\n\n#### setAlertMessagesCookie\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- A string or a list of string as cookie value.\n\n```ts\nsetAlertMessagesCookie(response, [\"foo\", \"bar\"]);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\nNote that you will need to un-serialized the value of the cookie, and that the returned value will be a list of string, even if only one message has been set in the `alert-messages` cookie.\n\n```ts\nconst serializedAlertMessages = await getServerSideCookies(request, {\n  cookieName: \"alert-messages\",\n});\n\nconst unSerializedAlertMessages = JSON.parse(serializedAlertMessages);\n```\n","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.0.0-experimental-2821681","dist":{"shasum":"7ff9cc801a2e36b5d77b30254661920754a54c00","integrity":"sha512-YCOJAdtKHhrEx8wFtYsLjGoLGZqBLYfFBq7tm0nzA1k9pxerPhYARsJ7dtY6xcLJ35lsbKh0hANJvMbLM2i6pg==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.0.0-experimental-2821681.tgz","fileCount":104,"unpackedSize":455000,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNRRuCRA9TVsSAnZWagAAQ3UP+wcx7jqG4H+E3zmXtetd\n0ew9wgD31QEWSZjO/ulbCfpdd9Co+GZvnGWLtkrFyEIfCfVfC/BrlIwdRzBz\n5aEMipsH9it/zbetMoLlcar9Jr1LGWB5gpRPZ0HvbRZiRG8RNxMtZR04uNMI\nbvredbdBr3bxn2kNc5JM3RytqSQqfsEG7KqMi4kBNIczZsdCskC5x5Mkp6KI\nhbNZX+qcjc9K+gC77YruBGVNvonG591clt9gYFjsPXfHHO0y/bydMJU+eh+M\n6lXMf8LSJy4IdWP4+R5mr5E57TVbMltf+/CCck2LRQ7CEQuZXqIVaPo9Ha+Z\nJZtnmGGQgUd0lRlATUvBO7iTR83h5WmimsFACY8MbSsD92Ckkkfe9UfG9npQ\nHvrxRPf5rM/Y2e+oKsbgKs1blXz7FxRjvZUe3j0gsklnNOz2O9speeZthk8R\n+7VKHqeNfOqipoAfdAqz0hzRCGYGxqBmR/L4ED3DVzb8ILpfl3iYH12bSdqr\nZPiDW+oIc3VtjD5cINwlVgbzQZhjcojtrA51toEPUO0oShusYMLGAeyAvAEl\nXg6PvlVE77W8h8sCZ0IET7/5g7HM9oAZUlmKi3X9t20SoZMXwRZLVG0vTEUq\nWUF0sE1EdaO0aP+jQXOegfL8gqH67rqK/eESAj0p/0oSEHMhoKsdeBirLi05\n/wR1\r\n=yJ9m\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICH9rihZz6bNbL//cCPUIJDhMekjWwfB9mp7bzrBde8XAiEAhCxarcJ6SIBxAAZiYbZY3c8Zq0zJr+ou5ZCwdJowoiI="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.0.0-experimental-2821681_1614091374348_0.1232213547768728"},"_hasShrinkwrap":false,"deprecated":"experimental version no longer needed"},"0.0.0-experimental-8542abe":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.0.0-experimental-8542abe","readmeFilename":"README.md","readme":"# FWL Web\n\n**Disclaimer**: this package is made for our internal usage and is only open source for convenience so we might not consider Pull Requests or Issues. Feel free to fork though.\n\nThis is part of the Fewlines Web Libraries packages.\n\nIt provides a framework on top of `http` to be used with Express or Next.JS.\n\n## Installation\n\n```shell\nyarn add @fwl/web\n```\n\n## Usage\n\n### Definitions\n\nFor the purpose of this package, here are the definitions we will use:\n\n- **path**: a string, the path for which a _handler_ will be invoked.\n- **handler**: a function that will be invoked for a request. It takes a request and a response as parameters and returns `any`.\n- **route**: the combination of a _path_, a _handler_ and an HTTP method (e.g: the _handler_ that will respond to `GET /resource`)\n- **router**: a collection of _routes_\n- **endpoint**: the combination of _handlers_ that will respond for a given request for all HTTP methods (a sort of _route_ for all methods).\n\n### Handler\n\nAt its simplest, a handler would look like that:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(\n  request: IncomingMessage,\n  response: ServerResponse\n): void {\n  response.statusCode = HttpStatus.OK;\n  response.end(\"OK\");\n}\n```\n\nHowever, we recommend to use tracing, for easier debugging and it should look like this:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (\n    request: IncomingMessage,\n    response: ServerResponse\n  ): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.statusCode = HttpStatus.OK;\n      response.end(\"OK\");\n    });\n  };\n}\n```\n\nThis is basic dependency injection via a function.\nWe're exporting a function that takes a dependency (`tracer`) that returns a Handler.\nYou could then use it with a Router `router.get(\"/ping\", pingHandler(tracer))` or with an Endpoint `new Endpoint().get(pingHandler(tracer))`.\n\n### Router\n\nA Router is a class that takes a list of middlewares for its constructor.\nYou can then add routes to it by giving it a path and a handler.\n\n`Router` can be parametized with request and response types, allowing your handlers to use these and not have typing problems.\nThis can let us use Express' `Request` and `Response` types and use all of their methods.\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\n\nexport function start(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nIf you need to add a middleware to one route in particular, you can use `wrapMiddlewares`:\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\n    \"/users\",\n    wrapMiddlewares([someAuthMiddleware], userHandler.createUser(tracer))\n  );\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nYou can also create several routers (take care to add the basic middlewares too though):\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { errorMiddleware, loggingMiddleware } from \"@fwl/web/dist/middlewares\";\nimport { Router } from \"@fwl/web\";\n\nexport function createApp(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n\n  const withAuthRouter = new Router([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n    someAuthMiddleware,\n  ]);\n  withAuthRouter.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\n### Endpoint\n\n`Endpoint` is thought for Next.JS as its main goal is to deal with file based routing.\nCreating an Endpoint lets you set Handlers to HTTP verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().get(handler);\n```\n\nBy doing that, all requests on this endpoint that are not `GET` will not call the Handler and returns a `403 Method Not Allowed`.\n\nYou can also chain it to allow several verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().put(handler).patch(handler);\n```\n\nApplying middlewares can be done like so:\n\n```typescript\n// pages/api/hello.ts\n\nimport { Endpoint } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n  \"/api/hello\"\n);\nexport default new Endpoint().get(wrappedHandler);\n```\n\n### Dealing With Errors\n\nThis package provides one way to deal with errors by combining a `WebError` error type and a middleware.\nHere's an example:\n\n```typescript\nexport class UserNotFoundError extends WebError {}\n\nexport function getUserById(tracer: Tracer) {\n  return (request, response): Promise<void> => {\n    return tracer.span(\"get-user-by-id\", async (span) => {\n      span.setDisclosedAttribute(\"user-id\", request.params.id);\n\n      const user = users.find((user) => user.id === request.params.id);\n\n      if (!user) {\n        throw new UserNotFoundError({\n          error: {\n            code: \"TST_201229_YNXL\",\n            message: \"No user found\",\n          },\n          httpStatus: HttpStatus.NOT_FOUND,\n        });\n      }\n\n      response.statusCode = HttpStatus.OK;\n      sendJSON(response, user);\n    });\n  };\n}\n```\n\nThrowing this error will result in a `404 Not Found` with a JSON body with `code` and `message`.\nThis is done by catching the error in the `errorMiddleware` and setting the response if it's an instance of `WebError`.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\nYou can also specify the optionnal `parentError` attribute when throwing a new `WebError`. When this attribute is provided with the `error` that triggered the `WebError`, both the `errorMiddleware` and `loggingMiddleware`\nwill use it to display its name and message along with the `WebError`'s ones in traces and logs.\n\n```typescript\ntry {\n  await insertUser(user);\n} catch (error) {\n  if (error instanceof UserAlreadyExistsError) {\n    throw new WebError({\n      error: {\n        code: \"user_already_exists\",\n        message: \"User already exists\",\n      },\n      httpStatus: HttpStatus.BAD_REQUEST,\n      parentError: error,\n    });\n  }\n  throw error;\n}\n```\n\n### Middlewares\n\nThis package provides a middleware API different from Express.\nInstead of receiving the Request, Response and a function to go to the next middleware (the last middleware being the handler), we choose to have middlewares receive a handler and gives back a handler.\n\nThe main advantage is that a middleware like that can do operations before the handler, after or both.\nFor instance, that's how the logging middleware is constructed: calling the handler, setting the result aside, logging and returning the result.\n\nTo be able to use those middlewares, you can use `wrapMiddlewares` to create a handler with its associated middlewares:\n\n> ⚠️ Middlewares are set in the order of their execution, meaning that the first middleware you put in the array will be the closest to the Handler.\n> Said differently, if `middlewareX` did something that needed to be done before we could use `middlewareY`, the order should be `[middlewareX, middlewareY]`.\n\n```typescript\nimport { wrappMiddlewares } from \"@fwl/web/dist/middlewares\";\n\nconst wrappedhandler = wrapMiddlewares(\n  [middleware1, middleware2],\n  handler,\n  routePath\n);\n```\n\nThe last parameter `routePath` is optionnal but will be used to name the tracing span on requests.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n#### Logging Middleware\n\nThis middleware will log each requests with:\n\n- the path,\n- the duration of the request,\n- the HTTP method,\n- the IP address that initiated the request,\n- the status code of the request,\n- the trace ID for the request.\n\nThe log message will either be empty in case of success or contain the error message if an error was thrown somewhere.\n\nIf you're throwing a `WebError`, the status code of this error will be logged.\n\n#### Error Middleware\n\nThis middleware will capture `WebError` thrown in middlewares or handlers and return a formatted Response, with the status code of the error and the error code and message as JSON.\n\nIf you don't want to use `WebError`, you can either don't use it or recode your own for your own errors.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n#### Recovery Middleware\n\nThis middleware only goal is to catch errors and return a 500 Internal Server Error.\nThis way, your server will not crash and leak errors when something unexpected happens.\n\nIt will also add the error message and stacktrace in the trace of the request for debugging purposes.\n\n#### Tracing Middleware\n\nThis middleware will create a trace per request, it should be used when we want to use tracing with Express and Next.JS.\n\n> ⚠️ If it's used, it should be the first middleware of the list since the others will try to create spans.\n\n#### Rate Limiting Middleware\n\nThis middleware can be placed right after the Tracing middleware and will block or slow down requests if there are too many hits from the same IP address.\nIt takes the `tracer` and `logger` as parameter as well as an `options` object:\n\n- `windowMs` a number of milliseconds, the window of time during which the count for one IP will be logged in. If you're blocked, you will need to wait that much time before attempting new requests.\n- `requestsUntilBlock` is the number of requests that are accepted in the `windowMs` time before blocking this IP.\n- `slowdown` is optional, if set, the middleware will first artificially makes the requests slower. It's an object with:\n  - `requestsUntilDelay` is the number of requests that are accepted before starting to slow down subsequent requests.\n  - `incrementalDelayBetweenRequestMs` is the number of milliseconds that will be added to each requests when slowing down. Each requests will be incrementally longer (for instance, if this value is `500`, the first slow down request will take 500ms longer, the following 1000ms longer, the following 1500ms etc...).\n    > The maximum delay will be 3 seconds.\n- `memcachedClient` is optional. If you want to use Memcache as a storage for this middleware, you will need to provide a `memjs.Client` object.\n\nIf this middleware is used and a request is blocked, it will act as the logging middleware.\nIf the request is slowed down, it will add a span that represent this artifical slow down.\n\n### Usage for Express\n\nThis package provides a `createApp` function that create an Express Application from an empty Express app and a list of Routers.\nYou also can parametize the Router with Express' `Request` and `Response` type to allow your handlers to use these types without any TypeScript problem.\n\nHere's a basic application creation:\n\n```typescript\n// server.ts\nimport { Logger } from \"@fwl/logging\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\nimport {\n  loggingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport express, { Application, Request, Response } from \"express\";\n\nexport function start(tracer: Tracer, logger: Logger): Application {\n  const router = new Router<Request, Response>([\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n\n  return createApp(express(), [router]);\n}\n```\n\nAnd after that, you can have a file that starts your server:\n\n```typescript\n// index.ts\nimport { createLogger, EncoderTypeEnum } from \"@fwl/logging\";\nimport { InMemoryTracer } from \"@fwl/tracing\";\n\nimport * as server from \"./server\";\n\nconst logger = createLogger({\n  service: \"fwl-sparta-api\",\n  encoder: EncoderTypeEnum.JSON,\n});\nconst tracer = new InMemoryTracer();\n\nconst applicationServer = server.start(tracer, logger);\n\napplicationServer.listen(process.env.PORT, () => {\n  logger.log(`Server started on http://localhost:${process.env.PORT}`);\n});\n```\n\nTyping the Router like so allows the Handler to look like this:\n\n```typescript\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\nimport { Request, Response } from \"express\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (request: Request, response: Response): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.status(HttpStatus.OK).end(\"OK\");\n    });\n  };\n}\n```\n\n> Notice we couldn't have use `response.status` with a `ServerResponse` type, this function is from Express.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n### Usage for Next.Js\n\nNext.JS does not allow our tracing library to patch the underlying node packages: that means that we need to have a middleware which creates the first span.\n\nHere's an example of the `hello` API page with an added error:\n\n```typescript\nimport { Endpoint, HttpStatus } from \"@fwl/web\";\nimport { WebError } from \"@fwl/web/dist/errors\";\nimport {\n  loggingMiddleware,\n  wrapMiddlewares,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\nimport logger from \"../../logger\";\nimport getTracer from \"../../tracer\";\n\nconst tracer = getTracer();\n\nconst handler = (\n  request: NextApiRequest,\n  response: NextApiResponse\n): Promise<void> => {\n  return tracer.span(\"hello handler\", async (span) => {\n    if (request.query.someQueryParam) {\n      span.setDisclosedAttribute(\n        \"someQueryParam\",\n        request.query.someQueryParam\n      );\n      throw new WebError({\n        error: {\n          code: \"1\",\n          message: \"oups\",\n        },\n        httpStatus: HttpStatus.NOT_ACCEPTABLE,\n      });\n    }\n\n    response.statusCode = 200;\n    response.json({ name: \"John Doe\" });\n  });\n};\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler\n);\nexport default new Endpoint().get(wrappedHandler).getHandler();\n```\n\nUsage in a React Page is a bit different because we always need to be returning something and you may need to access `context.params`.\nYou can use the `getServerSidePropsWithMiddlewares` function that takes the context, a list of middlewares and a handler.\nHere's an example with the same middlewares as the API Page:\n\n```typescript\nimport {\n  loggingMiddleware,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { getServerSidePropsWithMiddlewares } from \"@fwl/web/dist/next\";\n\nexport const getServerSideProps: GetServerSideProps = async (context) => {\n  return getServerSidePropsWithMiddlewares(\n    context,\n    [\n      tracingMiddleware(tracer),\n      recoveryMiddleware(tracer),\n      errorMiddleware(tracer),\n      loggingMiddleware(tracer, logger),\n    ],\n    () => {\n      // do something with `context.params` here\n      return {\n        props: {},\n      };\n    }\n  );\n};\n```\n\n## Utils\n\n### Server side cookies\n\n#### setServerSideCookies\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n- The cookie value (anything serializable)\n- The cookie options, which is an object composed of\n  - `shouldCookieBeSealed: boolean`\n  - `cookieSalt?: string`\n  - ..[and the usual cookies options](https://www.npmjs.com/package/cookie#options-1).\n\n```ts\nawait setServerSideCookies(\n  response,\n  \"cookie-name\",\n  { key: \"value\" },\n  {\n    shouldCookieBeSealed: true,\n    cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n    maxAge: 24 * 60 * 60,\n    path: \"/\",\n    httpOnly: true,\n    secure: true,\n  }\n);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\n\n#### getServerSideCookies\n\nUsed to get the value of a cookie on the server side. This function requires as input:\n\n- The request\n- The cookie options, which is an object composed of:\n  - `cookieName: string`\n  - `isCookieSealed: boolean`\n  - `cookieSalt?: string`\n\n```ts\nconst cookie = await getServerSideCookies<UserCookie>(request, {\n  cookieName: \"cookie-name\",\n  isCookieSealed: true,\n  cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n});\n```\n\n#### deleteServerSideCookie\n\nUsed to delete a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n\n```ts\nawait deleteServerSideCookie(response, \"cookie-name\");\n```\n\n#### setAlertMessagesCookie\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- A string or a list of string as cookie value.\n\n```ts\nsetAlertMessagesCookie(response, [\"foo\", \"bar\"]);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\nNote that you will need to un-serialized the value of the cookie, and that the returned value will be a list of string, even if only one message has been set in the `alert-messages` cookie.\n\n```ts\nconst serializedAlertMessages = await getServerSideCookies(request, {\n  cookieName: \"alert-messages\",\n});\n\nconst unSerializedAlertMessages = JSON.parse(serializedAlertMessages);\n```\n","licenseText":"The MIT License (MIT)\n\nCopyright (c) 2020 Fewlines SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","_id":"@fwl/web@0.0.0-experimental-8542abe","dist":{"shasum":"6e34932deea3cdc1e5287b4f212cbcf9c55f6e5f","integrity":"sha512-LxM9RQpZdlU+k1YLHN4nLl7xDKAjfGExQskGeDdxUOzdftja7u7thg7Q57YzQAtltDEeKz2DBTzyt0rvXOOzBw==","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.0.0-experimental-8542abe.tgz","fileCount":104,"unpackedSize":455000,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNReGCRA9TVsSAnZWagAAc/kP/1EtuIg6j6AGvRqd+haB\nkmRlF2/uzyUrF/OO23/EQ6IHx+9xc73VAdmVoFHa5WA3YpX2zjLwvUdpOja3\nCm7SaaXIjqWhpJ2WTfnRlNLq9Ykpefk8AXAfI2PKUhgMGJMCxJHp/Hvil1pp\nYYSaOJfmbNKLGjP8YxQBHsSAJlS4kCNyNxc874VkG/JxJRnJeCY2kFk5XRBW\nR54nYmITkvClGgsPSBaE0hE/KFEk+JaeDbHGBzmR565t4/U5QUEnfTC3/xNT\nPshBq9gLvs7uTtbrncPG18QzluDqUOhprMNnBJVoQS77zyMoW6ueSu66s7iI\nDSnkMdsMWwm8nF6YtUTE+e86Q6FdmHQWmCoyeabVWJ0t2cx9HtFjR8mVT8Rj\nFzIWx62f3LM5xa15qNAlUM4i3c7iEJwHaO3DyyPxx2rgqvtAsljFBVXH+/8d\nJuSyCFYrQEg/Vdk0Sz8j2GvS1cLbEEl2d5gWb8j2c898ckB7vt58i3l74wDi\nXiwadgudv8b8VFQBI0yR9KcGD5dhEWHy80kQugDOOp9TtbWhX0i6IwzZcL9q\np9M/Dkq8qDe2SFKgZK06yIZZkC0aWTcHNjUvBDg2ejXx6TqDLct4w9LfQuOJ\nIYUM4apogzDd+HzrLH2+Qo7k1ioqHlBW1XOen42pWtlUCxv9+59da/RfV9br\n9b7T\r\n=m9K8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCr3B3MOXh8Koxy5bqJ7Z+n77SyCns4h7XJZ0bWHpRKDQIhANp8tdknFsV881ViiL+aGWpXLmuWNnbbMHecNwyJba/1"}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.0.0-experimental-8542abe_1614092165470_0.25440946001820164"},"_hasShrinkwrap":false,"deprecated":"experimental version no longer needed"},"0.10.5":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.10.5","_id":"@fwl/web@0.10.5","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-iAb4k20lvznww0K/2kXuGCkcy2q9HsNfE8AKjrTGj1y6w6RnUid2WL2iMNvPmAXgQRSTGRfbc/0l3BzSeaI7gQ==","shasum":"d56500086a93603976d95468f72ec1ebeae88f98","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.10.5.tgz","fileCount":95,"unpackedSize":455048,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNR+eCRA9TVsSAnZWagAAuX0QAI3GEuhywBmz4PNhFnjD\nVtn4u/kqmjuq1RONN6Vlg9SrdO5m+ylyBr3g4jb0rH7TyrboxNaIRc8oQ29i\nlKP2lB1DikdyDs1CIhNdl6KlSqcPGcQTFjTtpCG7TZN7fpAxmd1eFFp/pjvN\nfhUWo2Dxv/DGvoPmZtb85HrsKP6KXRCoQN3CMciePgjqpV9NmLMXzqiop9K+\nvTjoaRgw54vbX033V0kYSf/8yJMrVOrZiPj/G8EYh6ue/nAJVxtJZ30+zJgl\nwvSnV8ZXhTx0HLVP+SUhIL7/4DD1E1XlVoTpE03N1187BzWkb+W2uCPTsnkV\nLEljHhrlJWDQLKouYRDPeb7DtiqR4M1oYFzE9TRk+1u6jj4oGWB63oqRVkiL\narx9ApgiKvQ0iOtF68eFuWz4O9+uHET8SqvrDDBhG2PWp+DB6/AT5KAxrSkV\nCMlrib3Vgiqwm9q2cF44vL/JM6L7AJxXfvLDVhsc/LJL+IdFYrP6zTIWmeIh\nHpvKaiuAJaEUmpOvQ9s4MsZUFSjrAwEUR4zMOc6sO3AQEpckun8sSmw/iKRb\n4V6sTZe/365h/OYnJASUZqYZ8/i5DlxxkS4NiHzidW4NAi045kXeib2J6aps\n9WSU/Mvb7fGvvktEm1HTzUh5aFgIgS6XD3Z2avM5cBE5bCKeETtZBiHqXIm+\ntdO1\r\n=Xivk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICZTb1cH6gi8kzTHNEPjQF7Uy5kkB8w6jnoOopLjxoaRAiACyx7YeBxdVnfXhkZ4RJqUjznDs/Aecw1GPA5ZeD5b6A=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.10.5_1614094238255_0.2584678980893129"},"_hasShrinkwrap":false},"0.11.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.0.0","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.11.0","_id":"@fwl/web@0.11.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-DPN6nPbBvE71W2hUZgpck0lPyDHPi08HZhu0S/jWLQsqyEalRIrndHAWPLR5nTZTpd1Fa8ajtouSgV27rmRNCA==","shasum":"98175315599bfe88e4822093a511fc0241666d12","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.11.0.tgz","fileCount":98,"unpackedSize":459031,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgRe7wCRA9TVsSAnZWagAAugcP/0kkYboyCeEJjY6S1GXO\nFtuMHyPYoG2SJyGFIlneu7eoBTTP1dOgoX49B+m5JVZaGOuW9IAhqyADYolc\nMr9291znlVwhaGadZYdMtwIc/uEAdJlb+zwAQwBC7MdqYSKRKx5+mnmTQR/K\nhpbS/ylJl+K+91dYDj17gAtWTeGzyKTyOv8RVCHbWuRgyu1uA/zY4m28zjVK\n3v+UxYWHbMU9wfU55sg72S4f4AEv8krnblobIX9m2njUIIExWQ2xWDUNve4C\nn4sWeZSbMSHTtcaaa5QRrOZBa0hoQk5vbjciHdulMPzLq6mz9POH0Wc9ASdV\nP70w8ckFoniixR959TMV9kTL/otNMjUewADSgvfgL5z85rhqYEjmH5+rW9u4\n9WtiQPAUXqtOgLkgFe5k4ZVj2HI8SBxRIOUIHtIRBNovclyecYm+QfCBT65n\niLNOgTR9Slb3/Cvl2uMUNQ9uhZ5OxBcsNTfA7KWizmAL6f+Kg7fhW5vr6WBz\n7lGYqy2ccJA7QWbzZ8sbtdp2GnyF0MyY/Ss9qKMWRNp3QBm/Be8nBsU8llVQ\nqaR8rAqSQ7wdMMhdPRglN/4LQY+3Nx8wqomK5Q0y8bPC7x/obsqqCwGtSpzR\nDeKXPaBUw4AwON3G+uVUnftt+FwXriLBHVILrRrg+4gp5Dh27ZAr+Xqyu7V7\nSG+/\r\n=kpRA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF+2Gm7SGeyWpmzj6u2MukYIV76p7TGxMndh6AevMi/0AiBT8i40Q/2DgsJBvhn3u+dzyZyCo2lbfzYzfjmEN0OPdw=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.11.0_1615195888059_0.787601288600368"},"_hasShrinkwrap":false},"0.11.1":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.1.2","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.11.1","_id":"@fwl/web@0.11.1","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-AAKjrNAHrYCL7tRMlOYu85T6tFmAzYmoPYhK5zgSsRZ+GI5osj4HdLMEoEAeW/+ag0Tlnlz9iJn/0M9CmkDAIQ==","shasum":"48e64f06669e32632c92e3491af32312215e8199","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.11.1.tgz","fileCount":95,"unpackedSize":459075,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgRheECRA9TVsSAnZWagAA1cQQAJMW5dzEpDu3By5TA7Q9\nHAQbbbbqHa+YjD/PCT38uYj7GWljwuNwf8RjlFn0UkcUPomKIzzOMuMETChr\nC5zwGgCBfihmIx8ZFGkFkbtIazOMlYSeFoWMVviZuNdgUW4Du+lu/ry3QeVI\nsrSqwCURufVkt6o6ClH/q5IyaK0HWsrY3VoT4oKyDxu2RG5mXmm6xarTC4cB\nZrYRrEwoDGbtuQNfK8TzGyonyKiAo1IJ8xLVlSyoI4GSmbzdVg+DCqTNvZwy\nIBGTWJ+TkL59ksfLQ2MfL+SXwURb1aZIoBuZIlaoVPIhallePz5Zb+OVSZuk\nvwwmvRw2x0W2Ae6BpN8VsdX3ntD2JulLyfAW+L1NYbch7666Q2i1cagjV1eh\nlbpSqrJEgXUf27c0ZYnrVUgwaGUXExU5N/w9F3lUYbbnd+d4qGdPUqR9DL33\nzdVxpBtw7BvCbYFkrwyLGyWQB7DB8vw2JB++pAsIm+8zO3j3+Xb+N/CtLt1A\nNs0yfWFTYPyjyLdUPZU/ENqlYQs6XhS1Wq5tP4vNbNZ1Gu3IYmhTBvxBPf2h\nWIYsEPakH4VvLkk/8y6UoywT2muJck5Ttj1Qs4/Pd2J8VgjE/5Fr3rR4A1Ve\nefEtY+j5/btSki5OvdDDvCMOf7kamVWpcrO5zHc3cTyW8C+o0Umk+qmlA7Fw\nh41W\r\n=AYFS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDJwhCJ2YelWCATd+bJmTQ3c41nNzrvmI3d4gOt1IewPgIhALbL8xfaPOTo4MFgyFi725TouMiq8YmagILr84RF15+P"}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.11.1_1615206275705_0.9866688769757934"},"_hasShrinkwrap":false},"0.11.2":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.1.2","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","supertest":"6.1.3","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.11.2","_id":"@fwl/web@0.11.2","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-m+6BOPIY8w15T0eUpSria63H5ffWrSzpncgQUU3VhYzxhSk1b4E9DOpp4hVLwk7UlsK8PQo+r+W9l3Evc6yulg==","shasum":"81f1c5db2198906f6ef077c2d79e90c2ff4841ec","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.11.2.tgz","fileCount":95,"unpackedSize":463987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgU3SbCRA9TVsSAnZWagAA7wcP/jlJkQcyF7cM7r3ycMja\n8Ru0tuhqBqJ8W/5ndhtvNyRjoGviR0bFgubDl8FeJrbYL2IvZ8yzpQJOn7D8\n3kHxDXyVeXbL16u46FvklAJuGXvLZw8qrhNHDm8vxHCHVuZY/oP7o5+7I16n\nm842PMMmsHMtppZnSbqnWJ9a8XZ/flMWyFz84ONMFr7pXUvQr6NP3hOl0bp3\nWbgyt40t90OxUtAQntnYSnqITCZBpXnFk36A/OP6qlE/0lmGnGaUB9lpjus3\nuftPSL6YM3nM6jwcb6UG8MsI9FzKCHaZCcUdxbTPW3JQLNqcOaLx4a3T/aes\n7q+6sEjC5FzgGvBWSZVx5NJAXPVDvMUEYG5lLZPE7jbFufYO1syRjKyVEEPT\nLb0IIL/Ocru5Rz4tvdDYjV/8P00rCyhFZ0qHASN7tCwEdvSYmbqhfcCzuHzM\nb05lmkM0M6+FreUiOqi+xx//qDpSEUFw57VqLf/HZyTYMCVuh7qTjPtDqh8j\nH1+ciigOJFF8g+V/3H24OpdAbdvHdR3Nqu1SOl0TVtYzGaANbWonDllQCOXK\n6RLfZU593JRrHRJ1K5C3hLM3Dxgk/AwHtB2vErDRfuLaVCla3ypywZL9bVjL\nJH371Otv5AImcCvhpWRMFWVr2QTJdqp2VOR6HYci3vDwllzIR+58Ht1unJ1U\nqRG4\r\n=QQdK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDlYC9FhG1tbOqPPv7WRMRyGxpwBfX83NaCRX/4U0uycwIgMzBx7obH3a8ln6XYhHmfvcHh4tRjpvW1sk+Luj+t8/o="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.11.2_1616082074895_0.13252692144914402"},"_hasShrinkwrap":false},"0.11.3":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.1","@fwl/tracing":"0.9.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.9.6","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.1.2","@types/cookie":"0.4.0","@types/express":"4.17.11","@types/jest":"26.0.20","@types/memjs":"1.2.1","@types/node":"14.14.22","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.5","@types/send":"0.14.6","@typescript-eslint/eslint-plugin":"4.14.0","@typescript-eslint/parser":"4.14.0","eslint":"7.18.0","eslint-config-prettier":"7.2.0","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"4.17.1","jest":"26.6.3","memjs":"1.3.0","mock-http":"1.1.0","next":"10.0.5","prettier":"2.2.1","supertest":"6.1.3","ts-jest":"26.4.4","ts-node":"9.1.1","typescript":"4.1.3"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.11.3","_id":"@fwl/web@0.11.3","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-SCPCXE3mQ4xwkijdWrAujy1S+NG5n+v30/YVi0z5hydoBZb0jwOxVdBophtMneiK+akWNQpk+eYqZWFZQZGT8A==","shasum":"23083ebf834df570927be835e52579ef19bf2dec","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.11.3.tgz","fileCount":95,"unpackedSize":464724,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVGolCRA9TVsSAnZWagAAl7YQAI5NgMQcw7wlFQ1i1BWT\n/zk8rPSKIdGzFtOCLrBKfC9kVnTWEDOL/MMdCYO31VgggyxHLgCPizjUYhM2\nstuPnRa5ylqtMi5tAfsMo7/HfHe7K5190FmmbSG+XUN3ndhuvFGIHrbQRl9E\nuV1Zm96l1H57x8G6ZBuk8ENnA7bzZ8LUbA5WvpaDMLRWHKPoKvAyLqdJV56B\nfw0mB+wzF55bd0sGtH8c8uYD4wALY+0hDOR5CaYSdcNRZ2PleSuMrMMf/Nva\n8bbrPKT7aID5kDi12UlROIhG2Qj30IifYAw+lxl/HiTe5fqxbhBXPcqeN5RP\nQ7OvzXg82CZ9zq8ZoKDJ/tRzToC/3jvlRokeoT0jc/iYBgjfTwUqWpOTgdDI\n5J+uYH/1TDORQOGy7JoZi79Q2PLxF8cTHsu0Zt7HlX1qUS5DYDrFmEvXxbkT\njO/Yo14ooKGsmf+xoei2t+hqvwnrLTD6uuAA3a/9Wwxj0vEtdX2dg6grMSda\n4qscGUJpVkOjHHY7ou6lI3uscEw+B7V08iStOXwc3Vdo3e9NI1LNUIeTpIHT\ns45hgrS3Hd7QgMRMQkKpkpOZUV+4p5TMvHLY3/eSA5wXZwFlK0k0Lfc8/x+1\nqKwerLflIn7jgbjmWPTXj3Gh9Sn7/jqUNQU+VOBzTKH34aXn/z7/oQDBnHlC\nPV9X\r\n=tqsW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDnEubOj5onjchqy00XFyE68ErrgYt4SvL9XZUo4gexsQIhALRHeXJH2MG4gGx7bJX+qHSnkJGW9xfbWVKsib4NX7W3"}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.11.3_1616144932635_0.19573841470885034"},"_hasShrinkwrap":false},"0.11.4":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.2","@fwl/tracing":"0.10.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.10.1","send":"0.17.1"},"devDependencies":{"@fewlines/eslint-config":"3.1.2","@types/cookie":"0.4.0","@types/express":"4.17.12","@types/jest":"26.0.23","@types/memjs":"1.2.2","@types/node":"15.6.1","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.6","@types/send":"0.14.7","@typescript-eslint/eslint-plugin":"4.25.0","@typescript-eslint/parser":"4.25.0","eslint":"7.27.0","eslint-config-prettier":"8.3.0","eslint-plugin-import":"2.23.3","eslint-plugin-prettier":"3.4.0","express":"4.17.1","jest":"27.0.1","memjs":"1.3.0","mock-http":"1.1.0","next":"10.2.3","prettier":"2.3.0","supertest":"6.1.3","ts-jest":"27.0.1","ts-node":"10.0.0","typescript":"4.3.2"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.11.4","_id":"@fwl/web@0.11.4","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-Cj6CKDcgE9ao0VtT6U7SpvC9ac9fcok2cHy8073IB4EPab0aORkckSZuSAWqJmPBlSRjTaElurWt5v3UR2p53A==","shasum":"bb605a729e3f310cbcac8ad1258643e275b2c754","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.11.4.tgz","fileCount":95,"unpackedSize":238799,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgr4j4CRA9TVsSAnZWagAAaNMP/0ZQznLX/yeXNGkh4wir\n95qZlYdhsDWv0eBiSNfKrRz0ivjLjXtWwkCfYnP+jwvWTffhbet+iUpUaVQS\nOFiInwlpq/tl1hHpfeOaAWh9JanM56rH5jhB8huy1N2RO83w4aM3qrWzdz8k\n6OdhRqXqgb6jpzF9ilAuW2ZqZW0cteHVaMMlPYKY2trIGE6LB/mmtxxB4XXp\ny85CbjIR4RlVykTzbsvPE3kqYk1QV5EKMcWqzQ49wddaZFJARh6GuejopMem\ncd4K/hryJuzoowCL+MbVHg3M1XoMuuKv3O9GnmOf7Mw/NgfwqGclouPu9mIU\njFR2vgOv3+7LzgHPlBaxEZCYFwnAgzYYpCLupI2fualC1/06QvnLHtI5Y1U0\nYSRLeHLj2jRTuG9wzQzBgW8z9PUqFwpHHNRnoHvE5/Yker8SKffFDgCObCe9\nzIunfSud/ODHItEkf+b6nsJDsa7cOXOe5ogCndv8mYOexV7zSyZfnhnFx+xY\nXSaMA99+DZLBjpMS3Z7wr8kq2WYoz50d9pD9qdjveS8bSrkdyqJ13TRYk90M\n8IbrIvU4w7EO/sRt5ftzvzWaLSJxMfyv7DE0cx04WobyqCrtE8zv0Xpiv6Pc\n+BbRvf8InNGBtH/eqIAnqKpVFkPjuupJerixBvTCdiqTavuG0zedUQFeFqck\nM9YB\r\n=RmmI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDaUcuBaCg2RHB1NQ5DO9v7N0RqqdJscNhFWZMN0uYQTAiATNyq/zzCqb6xCA4jKT4IS5BBKUq6HX1n4F061EYyeMw=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.11.4_1622116600150_0.833888592096681"},"_hasShrinkwrap":false},"0.12.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.2","@fwl/tracing":"0.10.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.10.1","send":"0.17.1","tls":"^0.0.1"},"devDependencies":{"@fewlines/eslint-config":"3.1.2","@types/cookie":"0.4.0","@types/express":"4.17.12","@types/jest":"26.0.23","@types/memjs":"1.2.2","@types/node":"15.6.1","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.6","@types/send":"0.14.7","@typescript-eslint/eslint-plugin":"4.25.0","@typescript-eslint/parser":"4.25.0","eslint":"7.27.0","eslint-config-prettier":"8.3.0","eslint-plugin-import":"2.23.3","eslint-plugin-prettier":"3.4.0","express":"4.17.1","jest":"27.0.1","memjs":"1.3.0","mock-http":"1.1.0","next":"10.2.3","prettier":"2.3.0","supertest":"6.1.3","ts-jest":"27.0.1","ts-node":"10.0.0","typescript":"4.3.2"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.12.0","_id":"@fwl/web@0.12.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-MNqifzGDRs650nd/cbEG9OCP41uzBrNoXY4sCskk108BHIYgoWpKim6CQAoA0jnTY+nS+DhT2Na0dslZsO9ahQ==","shasum":"da08734d65b56f31ee9b8beb7fe9c3bc6667ed8d","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.12.0.tgz","fileCount":101,"unpackedSize":248723,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgzKGNCRA9TVsSAnZWagAAUXMP/1xcAujMQCZaqBTuiYKl\nGfQLc6NznFmDMPB7osQ8u/3yIgzsdpwhmIqiHXVhx7p2FbVGZl9qGKtFuxvg\n6z0Ex5y1lTMSbmXSV4pYULJsB6H6bVzuykZlP1Ge7cL0PolHqoH48v0gj/qj\n4nF6iU4ntkplLbzcWqiv5qIKcn3NsNbrxUSc0bGJljp9amXUfQRGQNbHeLXF\n4QDYll8h0ME7vkXKWpudRLkMcZeqtbYTnweKVI+Qarc9v3dIsNzi8O8u7Owp\nZrzsikwIa+HtXnZ0DVZ60teppuUjc2R9YOJd0Uz0Mu4KW+IsK1GGxogQ3AZZ\ns4HNTQoiExdhsTRSuA6WHVHCZuBK7bzNjSSaPzCAy6Z8YjfM1kNIFhtFSCf+\nEBKhmrDAUqswJoKnab3hw6db/EE1jmCc93sP7lz2ykCuXAhEcqmrNpnJaQGn\n7HDsQOugor9Z0zQPOyzfMNRnRZVMe4ED9YqCLCkrBJiwrHpiJWVMR2FxNv0d\nwehfCPBuoXPUXQd46RkCHPqoj1XnpDX13N8j/+1bFIKGoQdlciFHw1Y/Ozql\n26niP6shE3iYvCg7oIBaJWblXD05uAt2T6KNtoU+EEZ4szhVlbJhTgUxqt9+\no9wq+FQdby2D3Aga8LZRLXrQKho7Ibg7KSsLi5AwU7rfbYnwwnzQhnhZVbJT\nQayI\r\n=C+DG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDj5lwrvAhVngDbnP9+Zwiq5dxdEo8aHzDeogCbIYfspAiBH1rWzM2PR6iyBScCM2q3/C6zPIVIcTvkUnyumbJAznw=="}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.12.0_1624023437347_0.2971439298012495"},"_hasShrinkwrap":false},"0.13.0":{"author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","dependencies":{"@fwl/logging":"0.1.2","@fwl/tracing":"0.10.0","@hapi/iron":"6.0.0","cookie":"0.4.1","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.10.1","send":"0.17.1","tls":"^0.0.1"},"devDependencies":{"@fewlines/eslint-config":"3.1.2","@types/cookie":"0.4.0","@types/express":"4.17.12","@types/jest":"26.0.23","@types/memjs":"1.2.2","@types/node":"15.6.1","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.6","@types/send":"0.14.7","@typescript-eslint/eslint-plugin":"4.25.0","@typescript-eslint/parser":"4.25.0","eslint":"7.27.0","eslint-config-prettier":"8.3.0","eslint-plugin-import":"2.23.3","eslint-plugin-prettier":"3.4.0","express":"4.17.1","jest":"27.0.1","memjs":"1.3.0","mock-http":"1.1.0","next":"10.2.3","prettier":"2.3.0","supertest":"6.1.3","ts-jest":"27.0.1","ts-node":"10.0.0","typescript":"4.3.2"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"license":"MIT","main":"dist/index.js","name":"@fwl/web","scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"types":"dist/index.d.ts","version":"0.13.0","_id":"@fwl/web@0.13.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-fdikuD2had1+qfBtfmaOe3lgm/aiRAcZflEaX4iIv39Udtn27eIQf8PZnDLXZClYOJgQSLVIZq2sH/6PyNLtJQ==","shasum":"38f5775390bec4dddc5361e3ac11439cbea4eca1","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.13.0.tgz","fileCount":101,"unpackedSize":249116,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg1JESCRA9TVsSAnZWagAA5NUQAJP4Z4OjfEoyExbRL+JM\nAj+23OK8TOLc5ZDFKEvnfBaJDi86KedV48VtFUdczRVr+dILsfqnezXLO7kI\nIklVmf1QkwvkbxiMToiYPb/sguY4J2NZFu/Y/UqzW73LNS1I34moC4i+SnzB\nybX0dyd4Mis8KZkd9xurM5F5FAMba+hzTSjn9QtQcx4EY3fh4IH1IOy/FTTb\nlOK9CZiOm5isel2BTTo8IbQ5bSqcD/RKTylVkwgSlQYAicPWvWax5lrjCDMN\nf3ysNLvIs0jrsz7bVjiKG6dC3qSmglGljrUv8Zwo3ImC2Csp7EfSuncB05gm\nE7opkr6NmF3UsmRwX746E22BX6WqNnrpYd+8YKOWsrhVIMJnkJLWnGgNy3t2\niyjBGHJzRLfBG/e1uQXTn4O39m45d7TI6VOpDNyLlKdoIl67NczLHaJM5eOd\nsUcJ51OppMLRnfzdkxQXQ3GZ5ztC9e7SU+fF5bkKrKl9iedthjB+BWJ4D3x+\nNl25S4a5YRbGDYghf6PGj+uab893WKpK4uaw3G2YwoINmEiSWpxtPFTtZOYv\n8inJUsN3a3qzOq2W+uTHd4Kkk9iv4BrmFmNUjnU+PeiqM3Q6SAFvR5V/XW+e\nlYy4bKAwezt9LKMWBC8+i49nZkMr+nITD3QR06sj0n2m7anYATaadA308OJQ\nChHU\r\n=TB4I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC8Ppi2KI+c9EOd2aFFeRZ8QoNCXvkqClwOTVv+dFvLWQIhAOH9kE4+oS2v4ACTkH8a5wg8WT2j/hdcoGv3+T0EATVv"}]},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.13.0_1624543505486_0.6890686475039183"},"_hasShrinkwrap":false},"0.14.1":{"name":"@fwl/web","author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","license":"MIT","main":"dist/index.js","types":"dist/index.d.ts","version":"0.14.1","repository":{"type":"git","url":"git+https://github.com/fewlinesco/node-web-libraries.git","directory":"packages/web"},"scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"dependencies":{"@fwl/logging":"0.1.2","@fwl/tracing":"0.10.0","@hapi/iron":"6.0.0","@types/cookie":"0.4.0","@types/express":"4.17.12","@types/memjs":"1.2.2","@types/node":"15.6.1","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.6","@types/send":"0.14.7","cookie":"0.4.1","express":"4.17.1","memjs":"1.3.0","next":"10.2.3","on-finished":"2.3.0","parseurl":"1.3.3","qs":"6.10.1","send":"0.17.1","tls":"^0.0.1"},"devDependencies":{"@fewlines/eslint-config":"3.1.2","@types/jest":"26.0.23","@typescript-eslint/eslint-plugin":"4.25.0","@typescript-eslint/parser":"4.25.0","eslint-config-prettier":"8.3.0","eslint-plugin-import":"2.23.3","eslint-plugin-prettier":"3.4.0","eslint":"7.27.0","jest":"27.0.1","mock-http":"1.1.0","prettier":"2.3.0","supertest":"6.1.3","ts-jest":"27.0.1","typescript":"4.3.2","ts-node":"10.0.0"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"bugs":{"url":"https://github.com/fewlinesco/node-web-libraries/issues"},"homepage":"https://github.com/fewlinesco/node-web-libraries#readme","_id":"@fwl/web@0.14.1","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-hdGHuWv76HbV0neKw+rG20kGta8y8wcELK2NXlOhHDf7i2wXmfuErPsjbVeslD4WiLAcubX216B+RHg5ciYgGw==","shasum":"f63b095ef5423df2557a3dd531596c9127e5548d","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.14.1.tgz","fileCount":101,"unpackedSize":249250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqeBV3RwUfIKMP73VAOtZZZSHdiW836YmW5z/B8ZrqWQIgNtI7UgOr3daTMkLJZWcjrc4Lh/LNVIeUfhJTjmtyc5M="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZrRFACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrCpw/+JjMmalWHPldFZYq5gn7+CChOhEL6GYqsOSpjRrrZIi4Hz6/0\r\nTG1I3L2w6hE7cKlqDIL82Hf5GEf/Mc1gFJI/JdgZ1roKm8JvZBPqiX3Ue1tu\r\nzQG6WqqVtYvi6ZdVQQvMLJZhKrnchL4Wuk9z8xnctbhKrLy8T7JY2DYHez/t\r\nillCc5bqtoxx9GVCYpvEi1OKvMKQk7CXUPkjLlSJ2Tv+tSunvw75qC5OfLZ6\r\n8+WMY6eocvvkwIojJKvnOdc5v8auhWr7ydK/E0d6EZM0zof1qymZZudIfhOB\r\n7tB1bhV/9VGQ5DNKxjl69yAUoYLVlPik0IafhNAmt3Rcz5F1HdshvthV1A5u\r\nFIzPc6m8399YhwhbhJ5xpy2T36B0QSFW4G9Czip5i8jeDwcVvdTz30N32DJ3\r\nHh95MJYxtdrgOTG6srfi25hfn88KOCzoOKsAMbCPWpkWf/M2zPSBnqIj9qb3\r\n6ekY4VnDzaOVzmw40dC5VaYH2/EsOTBWW985eLmz9pqaOI3H06WHfRWXNCp6\r\n2N+/DkeKvWAUHoYJuxkNjo9xxw/TNC+yt6K/MdFUbmPhxban61I44wOFKzsc\r\n7LSe7GgEdwQNyrB9BWzsjTnS96U2gGNHIHAiKZebyC/UtE0GovxLlPlJ32ji\r\ny0ANFZvmvTvbcwPpMrF7SVr9RuF+FgeVYgY=\r\n=ijT3\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.14.1_1650897989599_0.6307846951120661"},"_hasShrinkwrap":false},"0.14.2":{"name":"@fwl/web","author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","license":"MIT","main":"dist/index.js","types":"dist/index.d.ts","version":"0.14.2","repository":{"type":"git","url":"git+https://github.com/fewlinesco/node-web-libraries.git","directory":"packages/web"},"scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"dependencies":{"@fwl/logging":"0.1.4","@fwl/tracing":"0.10.6","@hapi/iron":"6.0.0","@types/cookie":"0.5.1","@types/express":"4.17.13","@types/memjs":"1.2.4","@types/node":"17.0.31","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.7","@types/send":"0.17.1","cookie":"0.5.0","express":"4.18.1","memjs":"1.3.0","next":"12.1.5","on-finished":"2.4.1","parseurl":"1.3.3","qs":"6.10.3","send":"0.18.0","tls":"^0.0.1"},"devDependencies":{"@fewlines/eslint-config":"3.1.2","@types/jest":"27.4.1","@typescript-eslint/eslint-plugin":"5.21.0","@typescript-eslint/parser":"5.21.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-import":"2.26.0","eslint-plugin-prettier":"4.0.0","jest":"27.5.1","mock-http":"1.1.0","prettier":"2.6.2","supertest":"6.2.3","ts-jest":"27.1.4","ts-node":"10.7.0","typescript":"4.6.4"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"gitHead":"a907848f39909c4597dac11b9076cfa83410d1da","bugs":{"url":"https://github.com/fewlinesco/node-web-libraries/issues"},"homepage":"https://github.com/fewlinesco/node-web-libraries#readme","_id":"@fwl/web@0.14.2","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-JllssDW/ykecJ5hYh5vj3Z1HO3cSwvXEhWrWfRVtRZaHxItLXGYBSYmx4NCoO5fHdarmYuluTS6KN3dBK96Rew==","shasum":"dcd9bc561a4378178bbc8bf2b29709d392f528e8","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.14.2.tgz","fileCount":100,"unpackedSize":255385,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDEhyUR2V+GW2WC0oYg1BSzfjXx+xjw1mrEsjb0DeU0sAIgLAp+0ZF+RxGPYyseP3I+g4TPpWgbTHZDDiptYDHLidc="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJib6DRACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqOtBAAlTXAnm7RKmSHg9al4YfxwQ4Vvsjjek0YZ9LgnYSBWYwJ6mR1\r\nBGNHDKxHGsqsIIsQCRcQr2eddgdbC1emgxKVPxR+0q73Tm2X9htp4KZW3Dz2\r\n/FWu8o+D2Vv0XVZgBFu8mwc2TJ093ijMDhLaTmLunZGxbccjF0NcPwsfIyCM\r\nLfqBHvSCyBU0jqDU+MLVF9ekwnZ/iS5wipXFN9CM5kcg1ISPDa0ouJPTG24X\r\nzhJReUMI3dztsREN6BgYMaLOxAtq5EQOIJSuLYsjKUAEmh22/PdAWYoQUPdu\r\n65buy4ppR8Z6XsXzaxaNS65FKM+pnj5Vno6cc5e0o5R7stAhMS7jiER7z/XQ\r\n4iP4Aop6gGIfIXBJgjkr4s+TyhU8iUS87wZk+jthVMM1bfi9mIpW7JwW+uVO\r\nrKscHWo3NgJhbhOcLcj3XITrD7yVZFvI7JcIRDtLx35GbrLYxFltweuSigLr\r\nXYmSgoMZhY/9iRWznVIk0mJ2TinuQwjCBEytknWXs/1sHs2gJqBxZ7QVRb6X\r\n4HvWk+O4M6R68DsGSBdS95yer5LIk8HWusrwj82umnOblkdfMB9OqoxnoZBM\r\nurS7e/VJVzfk5efPvs4qSzQtDeS9psAKdRkocbbv/jwRGXXRZSIiYvyN3yDd\r\n7Lw/PGe1mWhbtz3GyOYrznt2ZVTurs0b26M=\r\n=YmuB\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.14.2_1651482833776_0.48574709357580326"},"_hasShrinkwrap":false},"0.14.3":{"name":"@fwl/web","author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","license":"MIT","main":"dist/index.js","types":"dist/index.d.ts","version":"0.14.3","repository":{"type":"git","url":"git+https://github.com/fewlinesco/node-web-libraries.git","directory":"packages/web"},"scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepare":"yarn build","prepublishOnly":"yarn lint","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"dependencies":{"@fwl/logging":"^0.1.7","@fwl/tracing":"^0.10.8","@hapi/iron":"7.0.0","@types/cookie":"0.5.1","@types/express":"4.17.13","@types/memjs":"1.2.4","@types/node":"17.0.35","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.7","@types/send":"0.17.1","cookie":"0.5.0","express":"4.18.1","memjs":"1.3.0","next":"12.1.6","on-finished":"2.4.1","parseurl":"1.3.3","qs":"6.10.3","send":"0.18.0","tls":"^0.0.1"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"gitHead":"1d46da441ca540a6b3139cbdc1259d6a0a822e89","bugs":{"url":"https://github.com/fewlinesco/node-web-libraries/issues"},"homepage":"https://github.com/fewlinesco/node-web-libraries#readme","_id":"@fwl/web@0.14.3","_nodeVersion":"16.15.0","_npmVersion":"lerna/5.0.0/node@v16.15.0+arm64 (darwin)","dist":{"integrity":"sha512-BOkXLWiuhG6VJip+TxwtnmrZtQHGXslYVF76fjNpeDH2d8slVt5ZWkXIY8XajMcFOWRApTocvY4LfkjSX6Xr8g==","shasum":"ded23150d4e97a8a54c2fbd95915dd0c467da9b7","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.14.3.tgz","fileCount":73,"unpackedSize":158190,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCWzNC9ooSJmHdD2Pp00i2HF1B5KdFhpXLMro8bH19oPAIgd2qxOyd+XsASfwxQFkENftdOLZhXuuTxi/9TMmfIK0Y="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJioKEQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo8Tg//SNpukY/EP96sRaZkVIbcRIBl7BpUsuEHO6ghRM0qm2buxZsJ\r\nXvUsF2mCQzrFV5wikqJH5i5FLWwgwNfqQWfIdnFcSwhzv32ybdRx3bRRLTvQ\r\nO0WzIjb2PuMcFRRvqxLF1n7OZxlJTmgLaTNcjIJ2s4YF53iMZERPTLEWG8xM\r\nRh6Z4IWw7xkLqlbzIgFMnOfNPa0LlB6wgUGEOxwCKg6ONlAdKWCow3jhbcaQ\r\ndp9vNutrptzsYqFjMsB8r0V+1b/Uj97YDgGNaXnb/xKT4+wocT5c/6iUL6LJ\r\nWjf8m/SxYesSvP33vSDvhm7O8LjOHbLB7bt5pSvJoYxoOwUm0qaNSnt7rEOf\r\n5wCqTlZqkMALs7aMZNXToweE/sN6Y1ThLZb0gd8vmvsjkR/EfiYn1KNstxth\r\nALNLy4rz0J/H4SEFHiwtEGqcPYYh71/x8L2tJDsmDrJciwszYL4YmQxN/M2Y\r\nojwz85XNjNKu9Pdg0Q5FtgW1ZmSsjjz1uOMtfxuYYDC1ESt7qXb1mYsfwBUZ\r\njvE3u/7SxC41IB3Q95bINkueHLnxs2R1nElP2qXAhFBgY2E0qHC00MEXHjq8\r\nbhzJF5Z3kfDQ9rKAzAKA+bLEUDMk52vSBfup3Ihcreb1+V8sJCFAbl01n8Yy\r\nDnvjTXGo1nxvaf1CjGPxTwnoJ27JznmBEIM=\r\n=nRuu\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.14.3_1654694160226_0.27713173967772153"},"_hasShrinkwrap":false},"0.14.4":{"name":"@fwl/web","author":{"name":"Fewlines"},"description":"Web part of Fewlines Web Libraries","license":"MIT","main":"dist/index.js","types":"dist/index.d.ts","version":"0.14.4","repository":{"type":"git","url":"git+https://github.com/fewlinesco/node-web-libraries.git","directory":"packages/web"},"scripts":{"build":"tsc","clean":"rm -rf dist","prelint":"prettier --trailing-comma all --write './**/*.md'","lint":"eslint --ext ts --ignore-pattern dist .","prebuild":"yarn clean","prepublishOnly":"yarn lint && yarn build","preversion":"yarn lint","experimental-publish":"yarn publish --new-version \"0.0.0-experimental-$(git rev-parse --short HEAD)\" --access public --tag experimental","test":"jest --runInBand"},"dependencies":{"@fwl/logging":"^0.1.8","@fwl/tracing":"^0.10.9","@hapi/iron":"7.0.0","@types/cookie":"0.5.1","@types/express":"4.17.13","@types/memjs":"1.2.4","@types/node":"17.0.35","@types/on-finished":"2.3.1","@types/parseurl":"1.3.1","@types/qs":"6.9.7","@types/send":"0.17.1","cookie":"0.5.0","express":"4.18.1","memjs":"1.3.0","next":"12.1.6","on-finished":"2.4.1","parseurl":"1.3.3","qs":"6.10.3","send":"0.18.0","tls":"^0.0.1"},"eslintConfig":{"extends":["@fewlines/eslint-config/node","@fewlines/eslint-config/typescript"]},"jest":{"preset":"ts-jest","testEnvironment":"node","testPathIgnorePatterns":["dist/"]},"gitHead":"1b885c96412544c76eba916f30d00aeeea2892f9","bugs":{"url":"https://github.com/fewlinesco/node-web-libraries/issues"},"homepage":"https://github.com/fewlinesco/node-web-libraries#readme","_id":"@fwl/web@0.14.4","_nodeVersion":"16.15.0","_npmVersion":"lerna/5.0.0/node@v16.15.0+arm64 (darwin)","dist":{"integrity":"sha512-m9JEGFZeErHEOldeeG1peB5iXcERq8tCPUXk57y1UxWRsMbW8Zq++9JE6pxCghkv8Z8y5v+5GQiYP00WXARjiw==","shasum":"e54d19a74cd05bcd43bf2a72c7a9369ba9a46a9a","tarball":"https://registry.npmjs.org/@fwl/web/-/web-0.14.4.tgz","fileCount":73,"unpackedSize":158175,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH3+NdyMU2drhDShGZGzZ5T2yYzVlj61zu60EFh9ZsfQAiBA5DBk5IetyOPLNgZ3iUbnsqZFkHeJtOrWh+EgHkRQMw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJioaDXACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq2Fw/+Kpc5eypKYst/0rx3JsGtl8JRmdlvxHo+MHyDVP2rFNg0uzBQ\r\nZffEPtXR+ehwcZ7k0H+06SVnazkSgyPIOG5BqP6L4hLAvpMT0MqfpVK2+fGA\r\n+YXMXwsqiKiJb3sXmf3WbdGNWhCVDD1XTB+KZ4U0ZiGZboM5kLx1b4Aa44Ra\r\nMo+hO6f9uVSVcDZ/4va5kAuhtzHMCwGkcCq9d83aMw5KfSjCgHuyqViMiNV6\r\ny81cllcjMxTvMQ22HFOY3SydG5Iw2Gn3F9hlHLsQ2A+wb1G8re5TA0M5dXsg\r\nM3XeRj0OFmnzVQc/rBBS35PplBOe+G4EZHvvD/aer3WiA1DCrbEC1do4vi4F\r\nUVcJqaZlQFQhUTEr02GXdBSAExQKWDY7pUYv1K/x32u/Tp0zHmHopFUqcP/m\r\n3wwVQ5u3WPYcwHiQkmMTZs+qjNw1UYWOASFIntEr+fqHsw4Ey5vt7k8x+O7k\r\ng0xfVYFbC+2HsgR6DsupEleAkDQVmbUFLicGWDB6UTY0spuhCBFPB7jNkiyh\r\nHEebJNEf4N8TWkWvC8Y8k8MdhzJrsIsg1rH1zpYVPm9XzMUdwYLTu2R3VBBF\r\nvg6wbLbqH+MfEV8YqGGQAD3uQGgL1EXFoDttVOSVOhr/ir1ZDSQ+NCTSeGai\r\nwFzTtbXSxm2UxAdg8dztXI1ysRqbjd8D6Sw=\r\n=HwR0\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"fewlines-tech","email":"tech@fewlines.co"},"directories":{},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web_0.14.4_1654759639269_0.19472695509611682"},"_hasShrinkwrap":false}},"time":{"created":"2020-08-04T12:48:38.247Z","0.1.0":"2020-08-04T12:48:38.640Z","modified":"2022-06-09T07:27:19.504Z","0.2.0":"2020-08-05T13:20:03.754Z","0.3.0":"2020-08-06T14:17:45.790Z","0.4.0":"2020-08-11T15:55:56.754Z","0.4.1":"2020-08-12T08:28:07.902Z","0.0.0-experimental-a36c666":"2020-12-30T09:03:30.630Z","0.0.0-experimental-cf1a8d3":"2020-12-30T12:21:20.454Z","0.0.0-experimental-e797b88":"2020-12-31T14:13:21.793Z","0.0.0-experimental-a2686b3":"2021-01-04T08:23:13.793Z","0.0.0-experimental-f703461":"2021-01-04T13:36:43.352Z","0.0.0-experimental-82a1438":"2021-01-04T13:46:04.287Z","0.5.0":"2021-01-05T14:56:05.864Z","0.0.0-experimental-a81e88d":"2021-01-05T17:08:40.702Z","0.5.1":"2021-01-05T17:42:11.999Z","0.5.2":"2021-01-06T14:28:24.728Z","0.0.0-experimental-0f46df7":"2021-01-06T15:31:43.312Z","0.0.0-experimental-e54e453":"2021-01-06T15:34:21.028Z","0.5.3":"2021-01-06T16:39:51.403Z","0.6.0":"2021-01-07T10:47:34.068Z","0.0.0-experimental-6d4cdda":"2021-01-20T16:12:31.602Z","0.0.0-experimental-523b93c":"2021-01-20T16:23:46.542Z","0.0.0-experimental-32a623d":"2021-01-21T13:03:02.537Z","0.0.0-experimental-ee33f94":"2021-01-21T14:12:22.470Z","0.6.1":"2021-01-22T10:56:51.351Z","0.6.2":"2021-01-22T14:49:14.937Z","0.0.0-experimental-83512fc":"2021-02-01T12:58:31.847Z","0.7.0":"2021-02-02T10:41:13.673Z","0.0.0-experimental-9000b8e":"2021-02-04T09:14:27.289Z","0.8.0":"2021-02-04T10:11:30.009Z","0.0.0-experimental-f87237d":"2021-02-08T08:59:23.905Z","0.0.0-experimental-b210d8a":"2021-02-08T10:09:59.777Z","0.9.0":"2021-02-08T17:02:13.928Z","0.10.0":"2021-02-09T15:22:17.962Z","0.10.1":"2021-02-16T15:42:18.418Z","0.10.2":"2021-02-17T17:26:20.835Z","0.10.3":"2021-02-18T15:40:10.476Z","0.0.0-experimental-7534e1f":"2021-02-18T17:47:48.481Z","0.10.4":"2021-02-19T13:08:39.978Z","0.0.0-experimental-2821681":"2021-02-23T14:42:54.485Z","0.0.0-experimental-8542abe":"2021-02-23T14:56:05.686Z","0.10.5":"2021-02-23T15:30:38.432Z","0.0.0-experimental-de0bb5a":"2021-03-02T17:47:17.290Z","0.0.0-experimental-8892af0":"2021-03-05T11:57:06.555Z","0.11.0":"2021-03-08T09:31:28.253Z","0.0.0-experimental-c897022":"2021-03-08T10:29:46.422Z","0.0.0-experimental-3969a3b":"2021-03-08T11:59:15.114Z","0.11.1":"2021-03-08T12:24:35.838Z","0.0.0-experimental-bf14a93":"2021-03-11T10:48:35.101Z","0.0.0-experimental-a661c4b":"2021-03-11T11:59:09.545Z","0.0.0-experimental-5fa592b":"2021-03-11T12:33:58.693Z","0.11.2":"2021-03-18T15:41:15.053Z","0.0.0-experimental-3606cd8":"2021-03-19T08:12:13.968Z","0.11.3":"2021-03-19T09:08:52.809Z","0.11.4":"2021-05-27T11:56:40.376Z","0.0.0-experimental-27fb19b":"2021-06-14T11:06:23.856Z","0.12.0":"2021-06-18T13:37:17.560Z","0.0.0-experimental-2f4af26":"2021-06-24T08:03:40.813Z","0.13.0":"2021-06-24T14:05:05.692Z","0.14.1":"2022-04-25T14:46:29.748Z","0.14.2":"2022-05-02T09:13:53.911Z","0.14.3":"2022-06-08T13:16:00.463Z","0.14.4":"2022-06-09T07:27:19.434Z"},"maintainers":[{"name":"fewlines-tech","email":"tech@fewlines.co"}],"description":"Web part of Fewlines Web Libraries","author":{"name":"Fewlines"},"license":"MIT","readme":"# FWL Web\n\n**Disclaimer**: this package is made for our internal usage and is only open source for convenience so we might not consider Pull Requests or Issues. Feel free to fork though.\n\nThis is part of the Fewlines Web Libraries packages.\n\nIt provides a framework on top of `http` to be used with Express or Next.JS.\n\n## Installation\n\n```shell\nyarn add @fwl/web\n```\n\n## Usage\n\n### Definitions\n\nFor the purpose of this package, here are the definitions we will use:\n\n- **path**: a string, the path for which a _handler_ will be invoked.\n- **handler**: a function that will be invoked for a request. It takes a request and a response as parameters and returns `any`.\n- **route**: the combination of a _path_, a _handler_ and an HTTP method (e.g: the _handler_ that will respond to `GET /resource`)\n- **router**: a collection of _routes_\n- **endpoint**: the combination of _handlers_ that will respond for a given request for all HTTP methods (a sort of _route_ for all methods).\n\n### Handler\n\nAt its simplest, a handler would look like that:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(\n  request: IncomingMessage,\n  response: ServerResponse,\n): void {\n  response.statusCode = HttpStatus.OK;\n  response.end(\"OK\");\n}\n```\n\nHowever, we recommend to use tracing, for easier debugging and it should look like this:\n\n```typescript\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (\n    request: IncomingMessage,\n    response: ServerResponse,\n  ): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.statusCode = HttpStatus.OK;\n      response.end(\"OK\");\n    });\n  };\n}\n```\n\nThis is basic dependency injection via a function.\nWe're exporting a function that takes a dependency (`tracer`) that returns a Handler.\nYou could then use it with a Router `router.get(\"/ping\", pingHandler(tracer))` or with an Endpoint `new Endpoint().get(pingHandler(tracer))`.\n\n### Router\n\nA Router is a class that takes a list of middlewares for its constructor.\nYou can then add routes to it by giving it a path and a handler.\n\n`Router` can be parametized with request and response types, allowing your handlers to use these and not have typing problems.\nThis can let us use Express' `Request` and `Response` types and use all of their methods.\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\n\nexport function start(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nIf you need to add a middleware to one route in particular, you can use `wrapMiddlewares`:\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nexport function start(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n  router.post(\n    \"/users\",\n    wrapMiddlewares([someAuthMiddleware], userHandler.createUser(tracer)),\n  );\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\nYou can also create several routers (take care to add the basic middlewares too though):\n\n```typescript\nimport express, { Application, Request, Response } from \"express\";\nimport { errorMiddleware, loggingMiddleware } from \"@fwl/web/dist/middlewares\";\nimport { Router } from \"@fwl/web\";\n\nexport function start(tracer, logger): Application {\n  const router = new Router<Request, Response>([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n  router.get(\"/users/:id\", userHandler.getUserById(tracer));\n\n  const withAuthRouter = new Router([\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n    someAuthMiddleware,\n  ]);\n  withAuthRouter.post(\"/users\", userHandler.createUser(tracer));\n\n  return createApp(express(), [withAuthRouter, router]);\n}\n```\n\n### Endpoint\n\n`Endpoint` is thought for Next.JS as its main goal is to deal with file based routing.\nCreating an Endpoint lets you set Handlers to HTTP verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().get(handler);\n```\n\nBy doing that, all requests on this endpoint that are not `GET` will not call the Handler and returns a `403 Method Not Allowed`.\n\nYou can also chain it to allow several verbs:\n\n```typescript\nimport { Endpoint } from \"@fwl/web\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nexport default new Endpoint().put(handler).patch(handler);\n```\n\nApplying middlewares can be done like so:\n\n```typescript\n// pages/api/hello.ts\n\nimport { Endpoint } from \"@fwl/web\";\nimport {\n  errorMiddleware,\n  loggingMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n  wrapMiddlewares,\n} from \"@fwl/web/dist/middlewares\";\n\nfunction handler(request, response) {\n  // ...\n}\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n  \"/api/hello\",\n);\nexport default new Endpoint().get(wrappedHandler);\n```\n\n### Dealing With Errors\n\nThis package provides one way to deal with errors by combining a `WebError` error type and a middleware.\nHere's an example:\n\n```typescript\nexport class UserNotFoundError extends WebError {}\n\nexport function getUserById(tracer: Tracer) {\n  return (request, response): Promise<void> => {\n    return tracer.span(\"get-user-by-id\", async (span) => {\n      span.setDisclosedAttribute(\"user-id\", request.params.id);\n\n      const user = users.find((user) => user.id === request.params.id);\n\n      if (!user) {\n        throw new UserNotFoundError({\n          error: {\n            code: \"TST_201229_YNXL\",\n            message: \"No user found\",\n          },\n          httpStatus: HttpStatus.NOT_FOUND,\n        });\n      }\n\n      response.statusCode = HttpStatus.OK;\n      sendJSON(response, user);\n    });\n  };\n}\n```\n\nThrowing this error will result in a `404 Not Found` with a JSON body with `code` and `message`.\nThis is done by catching the error in the `errorMiddleware` and setting the response if it's an instance of `WebError`.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\nYou can also specify the optionnal `parentError` attribute when throwing a new `WebError`. When this attribute is provided with the `error` that triggered the `WebError`, both the `errorMiddleware` and `loggingMiddleware`\nwill use it to display its name and message along with the `WebError`'s ones in traces and logs.\n\n```typescript\ntry {\n  await insertUser(user);\n} catch (error) {\n  if (error instanceof UserAlreadyExistsError) {\n    throw new WebError({\n      error: {\n        code: \"user_already_exists\",\n        message: \"User already exists\",\n      },\n      httpStatus: HttpStatus.BAD_REQUEST,\n      parentError: error,\n    });\n  }\n  throw error;\n}\n```\n\n### Middlewares\n\nThis package provides a middleware API different from Express.\nInstead of receiving the Request, Response and a function to go to the next middleware (the last middleware being the handler), we choose to have middlewares receive a handler and gives back a handler.\n\nThe main advantage is that a middleware like that can do operations before the handler, after or both.\nFor instance, that's how the logging middleware is constructed: calling the handler, setting the result aside, logging and returning the result.\n\nTo be able to use those middlewares, you can use `wrapMiddlewares` to create a handler with its associated middlewares:\n\n> ⚠️ Middlewares are set in the order of their execution, meaning that the first middleware you put in the array will be the closest to the Handler.\n> Said differently, if `middlewareX` did something that needed to be done before we could use `middlewareY`, the order should be `[middlewareX, middlewareY]`.\n\n```typescript\nimport { wrappMiddlewares } from \"@fwl/web/dist/middlewares\";\n\nconst wrappedhandler = wrapMiddlewares(\n  [middleware1, middleware2],\n  handler,\n  routePath,\n);\n```\n\nThe last parameter `routePath` is optionnal but will be used to name the tracing span on requests.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n#### Logging Middleware\n\nThis middleware will log each requests with:\n\n- the path,\n- the duration of the request,\n- the HTTP method,\n- the IP address that initiated the request,\n- the status code of the request,\n- the trace ID for the request.\n\nThe log message will either be empty in case of success or contain the error message if an error was thrown somewhere.\n\nIf you're throwing a `WebError`, the status code of this error will be logged.\n\n#### Error Middleware\n\nThis middleware will capture `WebError` thrown in middlewares or handlers and return a formatted Response, with the status code of the error and the error code and message as JSON.\n\nIf you don't want to use `WebError`, you can either don't use it or recode your own for your own errors.\n\n> ⚠️ If you use both `errorMiddleware` and `loggingMiddleware` the order must be error, then logging.\n> Otherwise, the error message will not be logged.\n\n#### Https Redirect Middleware\n\nThis middleware will redirect requests to the `https` version of the url if the host is not `localhost` or `127.0.0.1`.\n\n#### Recovery Middleware\n\nThis middleware only goal is to catch errors and return a 500 Internal Server Error.\nThis way, your server will not crash and leak errors when something unexpected happens.\n\nIt will also add the error message and stacktrace in the trace of the request for debugging purposes.\n\n#### Tracing Middleware\n\nThis middleware will create a trace per request, it should be used when we want to use tracing with Express and Next.JS.\n\n> ⚠️ If it's used, it should be the first middleware of the list since the others will try to create spans.\n\n#### Rate Limiting Middleware\n\nThis middleware can be placed right after the Tracing middleware and will block or slow down requests if there are too many hits from the same IP address.\nIt takes the `tracer` and `logger` as parameter as well as an `options` object:\n\n- `windowMs` a number of milliseconds, the window of time during which the count for one IP will be logged in. If you're blocked, you will need to wait that much time before attempting new requests.\n- `requestsUntilBlock` is the number of requests that are accepted in the `windowMs` time before blocking this IP.\n- `slowdown` is optional, if set, the middleware will first artificially makes the requests slower. It's an object with:\n  - `requestsUntilDelay` is the number of requests that are accepted before starting to slow down subsequent requests.\n  - `incrementalDelayBetweenRequestMs` is the number of milliseconds that will be added to each requests when slowing down. Each requests will be incrementally longer (for instance, if this value is `500`, the first slow down request will take 500ms longer, the following 1000ms longer, the following 1500ms etc...).\n    > The maximum delay will be 3 seconds.\n- `memcachedClient` is optional. If you want to use Memcache as a storage for this middleware, you will need to provide a `memjs.Client` object.\n\nIf this middleware is used and a request is blocked, it will act as the logging middleware.\nIf the request is slowed down, it will add a span that represent this artifical slow down.\n\n### Usage for Express\n\nThis package provides a `createApp` function that create an Express Application from an empty Express app and a list of Routers.\nYou also can parametize the Router with Express' `Request` and `Response` type to allow your handlers to use these types without any TypeScript problem.\n\nHere's a basic application creation:\n\n```typescript\n// server.ts\nimport { Logger } from \"@fwl/logging\";\nimport { Tracer } from \"@fwl/tracing\";\nimport { Router } from \"@fwl/web\";\nimport { createApp } from \"@fwl/web/dist/express\";\nimport {\n  loggingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n  tracingMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport express, { Application, Request, Response } from \"express\";\n\nexport function start(tracer: Tracer, logger: Logger): Application {\n  const router = new Router<Request, Response>([\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ]);\n\n  router.get(\"/ping\", pingHandler(tracer));\n\n  return createApp(express(), [router]);\n}\n```\n\nAnd after that, you can have a file that starts your server:\n\n```typescript\n// index.ts\nimport { createLogger, EncoderTypeEnum } from \"@fwl/logging\";\nimport { InMemoryTracer } from \"@fwl/tracing\";\n\nimport * as server from \"./server\";\n\nconst logger = createLogger({\n  service: \"fwl-sparta-api\",\n  encoder: EncoderTypeEnum.JSON,\n});\nconst tracer = new InMemoryTracer();\n\nconst applicationServer = server.start(tracer, logger);\n\napplicationServer.listen(process.env.PORT, () => {\n  logger.log(`Server started on http://localhost:${process.env.PORT}`);\n});\n```\n\nTyping the Router like so allows the Handler to look like this:\n\n```typescript\nimport { Tracer } from \"@fwl/tracing\";\nimport { HttpStatus } from \"@fwl/web\";\nimport { Request, Response } from \"express\";\n\nexport function pingHandler(tracer: Tracer) {\n  return (request: Request, response: Response): Promise<void> => {\n    return tracer.span(\"ping-handler\", async () => {\n      response.status(HttpStatus.OK).end(\"OK\");\n    });\n  };\n}\n```\n\n> Notice we couldn't have use `response.status` with a `ServerResponse` type, this function is from Express.\n\nWe also provide a conversion function for Express middlewares:\n\n```typescript\nimport { convertMiddleware } from \"@fwl/web/dist/express\";\nimport cookieParser from \"cookie-parser\";\n// get your tracer as usual\n\nconst fwlCookieParser = convertMiddleware(tracer, cookieParser());\n```\n\n### Usage for Next.Js\n\nNext.JS does not allow our tracing library to patch the underlying node packages: that means that we need to have a middleware which creates the first span.\n\nHere's an example of the `hello` API page with an added error:\n\n```typescript\nimport { Endpoint, HttpStatus } from \"@fwl/web\";\nimport { WebError } from \"@fwl/web/dist/errors\";\nimport {\n  loggingMiddleware,\n  wrapMiddlewares,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\nimport logger from \"../../logger\";\nimport getTracer from \"../../tracer\";\n\nconst tracer = getTracer();\n\nconst handler = (\n  request: NextApiRequest,\n  response: NextApiResponse,\n): Promise<void> => {\n  return tracer.span(\"hello handler\", async (span) => {\n    if (request.query.someQueryParam) {\n      span.setDisclosedAttribute(\n        \"someQueryParam\",\n        request.query.someQueryParam,\n      );\n      throw new WebError({\n        error: {\n          code: \"1\",\n          message: \"oups\",\n        },\n        httpStatus: HttpStatus.NOT_ACCEPTABLE,\n      });\n    }\n\n    response.statusCode = 200;\n    response.json({ name: \"John Doe\" });\n  });\n};\n\nconst wrappedHandler = wrapMiddlewares(\n  [\n    tracingMiddleware(tracer),\n    recoveryMiddleware(tracer),\n    errorMiddleware(tracer),\n    loggingMiddleware(tracer, logger),\n  ],\n  handler,\n);\nexport default new Endpoint().get(wrappedHandler).getHandler();\n```\n\nUsage in a React Page is a bit different because we always need to be returning something and you may need to access `context.params`.\nYou can use the `getServerSidePropsWithMiddlewares` function that takes the context, a list of middlewares and a handler.\nHere's an example with the same middlewares as the API Page:\n\n```typescript\nimport {\n  loggingMiddleware,\n  tracingMiddleware,\n  errorMiddleware,\n  recoveryMiddleware,\n} from \"@fwl/web/dist/middlewares\";\nimport { getServerSidePropsWithMiddlewares } from \"@fwl/web/dist/next\";\n\nexport const getServerSideProps: GetServerSideProps = async (context) => {\n  return getServerSidePropsWithMiddlewares(\n    context,\n    [\n      tracingMiddleware(tracer),\n      recoveryMiddleware(tracer),\n      errorMiddleware(tracer),\n      loggingMiddleware(tracer, logger),\n    ],\n    () => {\n      // do something with `context.params` here\n      return {\n        props: {},\n      };\n    },\n  );\n};\n```\n\n## Utils\n\n### Server side cookies\n\n#### setServerSideCookies\n\nUsed to set a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n- The cookie value (anything serializable)\n- The cookie options, which is an object composed of\n  - `shouldCookieBeSealed: boolean`\n  - `cookieSalt?: string`\n  - ..[and the usual cookies options](https://www.npmjs.com/package/cookie#options-1).\n\n```ts\nawait setServerSideCookies(\n  response,\n  \"cookie-name\",\n  { key: \"value\" },\n  {\n    shouldCookieBeSealed: true,\n    cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n    maxAge: 24 * 60 * 60,\n    path: \"/\",\n    httpOnly: true,\n    secure: true,\n  },\n);\n```\n\nThis function will check if a `Set-Cookie` header is already being set, and will concat them into a list, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\n\n#### getServerSideCookies\n\nUsed to get the value of a cookie on the server side. This function requires as input:\n\n- The request\n- The cookie options, which is an object composed of:\n  - `cookieName: string`\n  - `isCookieSealed: boolean`\n  - `cookieSalt?: string`\n\n```ts\nconst cookie = await getServerSideCookies<UserCookie>(request, {\n  cookieName: \"cookie-name\",\n  isCookieSealed: true,\n  cookieSalt: \"7220e882-f534-44b0-99c4-76bdcb8ed0f7\",\n});\n```\n\n#### deleteServerSideCookie\n\nUsed to delete a cookie on the server side. This function requires as input:\n\n- The response\n- The cookie name\n\n```ts\nawait deleteServerSideCookie(response, \"cookie-name\");\n```\n\n#### setAlertMessagesCookie\n\nUsed to set a cookie on the server side.\n\nAlert message data structure: `{id: string, text: string, expiresAt: number}`.\n\nThis function requires as input:\n\n- The response object from the current process.\n- A **list** of alert messages — even you only want to set a single alert message.\n\n```ts\nconst alertMessage = {\n  id: \"e308b694-7fc1-49dd-9a6e-0cf1d0fcc00c\",\n  text: \"foo\",\n  expiresAt: Date.now() + 300000,\n};\n\nsetAlertMessagesCookie(response, [alertMessage]);\n```\n\nThis function will check if a `Set-Cookie` header is already being set. It will concat them to update the list passed as argument, following the [RFC spec](https://tools.ietf.org/html/rfc2109#section-4.2.2).\nNote that you will need to un-serialized the value of the cookie, and that the returned value will be a list of alert messages, even if only one message has been set in the `alert-messages` cookie.\n\n```ts\nconst serializedAlertMessages = await getServerSideCookies(request, {\n  cookieName: \"alert-messages\",\n});\n\nJSON.parse(serializedAlertMessages); // { id: \"e308b694-7fc1-49dd-9a6e-0cf1d0fcc00c\", text: \"foo\", expiresAt: 1614939460723 }\n```\n","readmeFilename":"README.md","homepage":"https://github.com/fewlinesco/node-web-libraries#readme","repository":{"type":"git","url":"git+https://github.com/fewlinesco/node-web-libraries.git","directory":"packages/web"},"bugs":{"url":"https://github.com/fewlinesco/node-web-libraries/issues"}}