From 01abd59c24308a29c83b8aa06adedfbbb3f90c77 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sat, 25 Jan 2020 23:14:10 +0700 Subject: [PATCH 01/46] update solution structure --- docs/imgs/code-solution-structure.png | Bin 24181 -> 23653 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/imgs/code-solution-structure.png b/docs/imgs/code-solution-structure.png index 2caedc8d261bedb4fb5651c664ce9ed68e56cf24..e771df34a5a9d206596b74c237c6d16ee8dc1853 100644 GIT binary patch literal 23653 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 KO8G_Wu>S*>DPPC{ literal 24181 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 From d721139a26a30037e26e4ffea36aa14d35787db9 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 27 Jan 2020 00:08:13 +0700 Subject: [PATCH 02/46] typo issues --- docs/imgs/onion-architecture.drawio | 2 +- docs/imgs/onion-architecture.png | Bin 81224 -> 81202 bytes docs/imgs/the-clean-architecture.drawio | 2 +- docs/imgs/the-clean-architecture.png | Bin 69221 -> 69208 bytes .../OS/IDateTimeProvider.cs | 2 +- .../OS/DateTimeProvider.cs | 2 +- ...Extensions.cs => PersistenceExtensions.cs} | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename src/ClassifiedAds.Projects/{ClassifiedAds.DomainServices/Infrastructure => ClassifiedAds.CrossCuttingConcerns}/OS/IDateTimeProvider.cs (64%) rename src/ClassifiedAds.Projects/ClassifiedAds.Persistence/{PersistanceExtensions.cs => PersistenceExtensions.cs} (96%) diff --git a/docs/imgs/onion-architecture.drawio b/docs/imgs/onion-architecture.drawio index 1deb4e8ed..f5dca0f10 100644 --- a/docs/imgs/onion-architecture.drawio +++ b/docs/imgs/onion-architecture.drawio @@ -1 +1 @@ -7Vpdc6M2FP01frQHEGD86I91mpntdKdpN5tHBQRoKxAj5Njur68E4lPEdhrb69lNHhx0JUA65x7dA/YILJPdHYNZ/DsNEBlZRrAbgdXIsjzXE58ysC8DtmuUgYjhoAyZTeAB/4tUsBq2wQHKOwM5pYTjrBv0aZoin3dikDG67Q4LKeneNYMR0gIPPiR69BEHPFbLcowm/hvCUVzd2TRUTwKrwSqQxzCg21YIfBqBJaOUl0fJbomIxK7CpTxv/UpvPTGGUn7KCY/3xnL1eZ483e2+fp1++9O1Z6sxcNTk+L5aMQoEAKqZ0lT8WzC6SQMkr2OIFmU8phFNIflMaSaCpgh+R5zvFX1ww6kIxTwhqhftMP/WOn5qHa926rpFY181Us728hRjYkyr9lPRtqtmc2bR6pz6BTGcII5YHQzmMh1E0ycwz7FfBteYVJPMOaP/1CwLfhYhTblak+mq9pISygqkgFH8ibhOhWInpxvmowP4K3VwyCLED/EEyoGSnNYdFNN3iIq1sr0YwBCBHL90sxcqEUT1uCZPxIFKlbekjXvzadPNGecXzBlrdls5M/3ImdvPGe+mckbN5gWSjbqTnkOECC8gU2cbY44eMlgAsRVupJsYMM9KgxDinUywRSgoaaEcej7y/ZqfVs+z59jOQfxfEONodxAw1TtTK1B2CChzsG28hVM5iLjlK+rg2SE2PQ3Ss2nQaGlQ6emACmvhqK2gUY3sD2AeF3My3yHZQmhqocNS/P8Sm50osbMrTJ36hWIx5TrRTLubaZbXy6ByouqsXhLV03hHXs2uut036XV8w++l0uG8+ylS5F07RP0Ac/3CfWzT+GDyTUzOrlhO1+u1tVyep2j29zJPr5r2UNW0L1c1jR8BZb98tX2jfSaoewbFHHAoYAhrcDmsgYb1PMsI9oViaCo6HhB7wT7KNQbEmnkX6q63U9tEG24VggRHqWj6Aknp7RcSQXFHMlcdCQ4C8hq33b3wCqxZbo81A2isme4Aa9bFSLM10v7OBZKWcS8RDSVevy5dXpcuR9eYaV2TrXoJDVt/oZzfqqIY5aX2wcp2jlDmnmlfdLqU2aY5UIOuSZmlUXafhgwKPjY+37BblVeLO2BeiTzbA13yrB9Nnl7SVjSBWFYz9RXKz2AmrJ5vMweMmzVkJqyLmQlLr0s18h8+QvMRQ5Xpqj7Cco6YvyUd2Ooa4MzjiukJxJ3OF7P1AL5D+HeFJf7OVG16PABzwIUP8WBfjAdX4+EPhiMsnvBbz8vzkeXCROKbPudZgYVLpHSwOIhKDXH5ze1czsZaf0dhyNA+gwSxhE58moigEL94bF4bU/HBYzSmqWB6DJkvaSxK21iAy8dmcY3y+uKwvsUty/eEl/zdNHt/LlV7buU1XT2VXHBVSetv9Vc6afrbmjZ/MczkuGQXyV8dTEJCt34skmISQA6f4auF8hStD5BTa/vc5DjTnq2cDrAzQI57MXL0d0RrTPQd9sie2idIUIL9SUD9TVKAdXNEuPa0u+POBirfkFUxL2ZVgG4SH9Fz41MOeMQKfp/QTXCCZ+xvczfHDjheD2uv0mZn+nZyRLP5UU75bUjzyybw6T8= \ No newline at end of file +7Vpbc5s4GP01fnSGu/GjL3U2M93ZzqbbNI8KCFBXIEbIsb2/fiUQFyFiO+NLPW38YKNPEkjnfJcDeGQv0u09BXnyJwkhHllGuB3Zy5Fl+Z7Pv4VhVxkc06wMMUVhZeoYHtF/UBoNaV2jEBbKQEYIZihXjQHJMhgwxQYoJRt1WESwetUcxFAzPAYA69YnFLJEbss1WvsfEMVJfWXTkD0pqAdLQ5GAkGw6JvvTyF5QQlh1lG4XEAvsalyqeas3epuFUZixYyY8PRiL5edZ+ny//fZt8v1vz5kux7YrF8d29Y5hyAGQzYxk/GdOyToLoTiPwVuEsoTEJAP4MyE5N5rc+AMytpP0gTUj3JSwFMteuEXse+f4uXO83Mrzlo1d3cgY3Ykpxp0xqdvPZdupm+3MsqVM/QIpSiGDtDGGM+EOvBlgUBQoqIwrhOtFFoySfxuWOT/ziGRM7sn0ZHtBMKElUrZRfrhdp0KyU5A1DeAe/GV0MEBjyPbxZFcDBTmdK0im7yHhe6U7PoBCDBh6Vb0XyCCIm3Gtn/AD6SrvcRvv5t1G9Rn3N/QZa3pbPjP58Jnb9xn/pnxGruYV4LW8ku5DGHMtIFxnkyAGH3NQArHhakR1DFDklUCI0FY42DzilHRQjvwABkHDT6fnxXcddy/+r5AyuN0LmOydyh1IOWRLcbBptYVbK4ikoysa49khNn0N0rPFoNGJQRlPe6KwCRyZCtqoEf0hKJJyTeYJIVsGmtyoHormSSE2PTLEzh5hcuoXgviSG0czHdXTLL/nQdVC5ayeEzXLOMGvpldN9617HU74PVfa73e/hIuclCGaG5jrF+5DSeODyXcxOb1iOV2tVtZicZ6i2c9lvl41naGq6Vyuaho/A8p++erqRudMUPcEijmgUOwhrO3LYW1rWM/yHKOARwzJeMcjpK8ogIXGAN8zU6FWtZ1ME124pQlgFGe8GXAkhbafCwT5FfFMdqQoDPFb3Kq58AqsWV6PNcPWWDO9Adasi5HmaKT9U3AkLeNBIBoJvH5funyVLlePMdO6JlvNFlq2vsKC3WpEUcKq2LeXjnuAMu9MedFVKWueYys16JqUWRplD1lEAedjHbA1vdXw6nBnm1ciz/FtlTzrZ5Onl7QlSQES1Uy+QvkVxITV023mgHCzhsSEdTExYel1qUH+Q0doOmKoMl1VR1juAfG3IAOprgXOPBwxvQDxJrP5dDWA7xD+amDxz5mqTY8H2xxQ4UM8OBfjwdN4+IuiGPE7/M798mxkeSAV+GYvRV5i4WEROogfxFUMMfHmdiZWY61+wCiicJcDDGlK7gKSciMPfn7bvDIm/IslcEwyzvQY0EDQWJa2MQeXjc3yHNX5+WFziVsO3yMe8qtudrov1Tm31pqe7kqefdWQ1p/qL3XS9LckXf4SkItx6TYW/zq4izDZBAl3irsQMPAC3iyUx8T6ADlNbGsPkPnpURZ/rZ5xnekdgTvpac3JAGUDjHkXY0x/cLRCWE+7BxJtnzXOEwruQhKs0xKsM7NzOhGeM1HT8HSgHA7pF/Ni+sXWleMTfGnFyx7hWMMfYLIOjxCS/dx3c+zYh4tkI2C67EzeTw5vtv/UqV6RtH93sj/9Dw== \ No newline at end of file diff --git a/docs/imgs/onion-architecture.png b/docs/imgs/onion-architecture.png index 66be82ef5629852e86513e3c64bdf15b03acc188..503f86f5cc8676583424f7c28d2c4340b01b502c 100644 GIT binary patch literal 81202 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�fof_=s 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≥O1mP2 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 z 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 literal 81224 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 \ No newline at end of file +3Vpdc+IqGP41Xuok5KPxssbadmbPbM92dtte7WCCyjYGh2DV/fUHEvIJVduN0bO96ISXQOB5Xt7nBexZ/nJ7S+Fq8Q8JUdQDRrjtWeMeAJ7r8f/CsMsMtmtkhjnFYWYyS8Mj/o2kMX9tjUOU1F5khEQMr+rGgMQxCljNBiklm/prMxLVv7qCc6QYHgMYqdYnHLKFnJZjlPY7hOeL/MumIWuWMH9ZGpIFDMmmYrJuepZPCWHZ03Lro0hgl+OStZu8U1sMjKKYHdPg6d7wx1+uly+32x8/rp6/ufZw3JfsvMFoLScsB8t2OQK8Fw42L4w2C8zQ4woGombD6ea2BVtGvGTyR5isMgZmeIv4R0czHEU+iQhNO7JCiLxZwO0Jo+QVVWrcwEPTGa9R5ySn+YYoQ9uKSc7xFpElYnTHX5G1jpW1kP5mSfQ3JXl2TtGiQlxhhNJh5kXPJab8QcL6AYiHXULsIC+0dRB7YGq5bjsQm0YdY0/F2NJhbJ0M4zxadALyzAtQIPx4RmL2KD9g5uUsgplaEqaeY/PI0Q4JDUc3NZ4OdCyA07FgKSz4HBJKogjRRCGET5TVka8DFpMYNdCXJhjhecyLAYcPcftIwIZ50L6WFUschtF7VFOyjkNB7Ng4gsQ2qLpqUGWoVJmulqoTMVXMoWRqPLpQgihhkGEiWnGX309YSwFu2NAQoBORLukCCl3f7y+fLss8tMBa4styG4RZzpkJUyPhTcwww0gNg63p0mQyAb7fTUgDjRTA1OQApk59zJOpD7DVRcJhBYYPEw3sl7FazqE+ztnVx1WY+krxHMdQNE3ImgrArntiAG4kWML8YZ7RxcTG71oMAEymEZkPggjBOOD7TjoIyJJb1zE39adkyp+BYYrBGnyfMxHpyYQtUD9t0Yc0EPQEbE3RIHUD+TH+VvG9S3abSgwV5UokMNI/xb3+3Jdst+ZKxWa+4kpX5vA0rvRwt/R/+6vvPluMf/7rDY0EOn1NHoPecHCx6/0c6mgaw0b6aXWmjlrS1GzmCU0vn7Cusk/LawRscGa+HDWZ2XJgs3B9LzCewctdcmeQWGvYocRqKVMV9hYytIG7S2XpDOvMtM0aa2DonXedXSmkPVCUpOD+D2jrbrcHvPpq0xynnIq329U4/sXAN0xDw8PjO39nPOdJSIWcOkiEsgWZEx4vvxCyksD8QoztJDJwzUidS44O3T3L9mnhRRQGTl4cb6uV411RCq/FrQcv8uQ4eM1MEywmlL6QOUl+lQH2MZIn4u/6q0wrGKRzxPYlabJDFNbuWlSCKYq4N72h2jBap8tR6BIDy51WLpLPcLbF7Lny/FJ5LukShV2Vu+cq5S81Vg9yLAe7j2IhccGavqXemIUJSFneQRDBJMFBbpa9qNEkPV4wDG/v4XV7DmO07TBpUz5puKu8sCI4Zkml5wdhKAONe1Xf7li20XC+rMfSFYuhfd47VeWeRJwqYJBZL73mFOfpR6jB8VGeawz+DadpV4JfCQvv1xn1nDG3RHCKohEMXudpYKvehaR/6g60cJYDO9B9C1RRhOKWVw62V71I1SlFnwdM0wM1Eotbio/5juIcfRsM6joEzIHZ6IbMZgn39GbE+pib7FtKulzhgBodOGX8+I1i64cMXiMvszXZNNAIvH0qgbeOUYxL0PwynJeSYCqS8Geqnwfnw1EcnEv2945bezv5t60ZW3fFeKo1szfp0x7H50etU5oftH5ds9VahNcHvnJaZaO4rn//Or5tNlyrfuLWaQTbuxaPZOM+/nvJ6HRpaOVE5eIAxp8Skw43gnlKdcGSoCXC7ljXSyl/qdZdiK4fTeLZtvN7x310aOMpMgwYaVfzLyCw6X6m0lZg48Xyl6rZHqb8ua918x8= \ No newline at end of file diff --git a/docs/imgs/the-clean-architecture.png b/docs/imgs/the-clean-architecture.png index 3112e2cd378957e685a78f4e81881fa895ede5f0..9f7aacda32356fc995f8ee7c341e253e5e6e0716 100644 GIT binary patch delta 67444 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 delta 67468 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®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 Date: Mon, 27 Jan 2020 20:11:22 +0700 Subject: [PATCH 03/46] display messages from notification server --- .../Dockerfile | 1 - .../ClassifiedAds.GRPC/Dockerfile | 1 - .../ClassifiedAds.GraphQL/Dockerfile | 1 - .../ClassifiedAds.IdentityServer/Dockerfile | 1 - .../ConfigurationOptions/AppSettings.cs | 9 + .../ConfigurationOptions/CORS.cs | 7 + .../Dockerfile | 1 - .../Startup.cs | 28 +- .../appsettings.json | 5 +- ...lassifiedAds.NotificationTestClient.csproj | 23 - ...assifiedAds.NotificationTestClient.ruleset | 32 - .../Program.cs | 95 - .../ClassifiedAds.Ocelot/Dockerfile | 1 - .../ClassifiedAds.Projects.sln | 7 - .../ClassifiedAds.WebAPI/Dockerfile | 1 - .../ClassifiedAds.WebMVC.csproj | 2 + .../ConfigurationOptions/AppSettings.cs | 2 + .../NotificationServer.cs | 7 + .../Controllers/AppSettingsController.cs | 30 + .../ClassifiedAds.WebMVC/Dockerfile | 1 - .../ClassifiedAds.WebMVC/Startup.cs | 100 + .../Views/Shared/_Layout.cshtml | 6 +- .../ClassifiedAds.WebMVC/appsettings.json | 3 + .../ClassifiedAds.WebMVC/wwwroot/js/site.js | 13 + .../wwwroot/lib/signalr/signalr.js | 4922 +++++++++++++++++ .../wwwroot/lib/signalr/signalr.js.map | 1 + .../wwwroot/lib/signalr/signalr.min.js | 17 + .../wwwroot/lib/signalr/signalr.min.js.map | 1 + .../wwwroot/lib/toastr/toastr.min.css | 1 + .../wwwroot/lib/toastr/toastr.min.js | 7 + src/ClassifiedAds.Projects/Dockerfile | 1 - src/ClassifiedAds.Projects/docker-compose.yml | 1 + 32 files changed, 5155 insertions(+), 173 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/ConfigurationOptions/AppSettings.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/ConfigurationOptions/CORS.cs delete mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/ClassifiedAds.NotificationTestClient.csproj delete mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/ClassifiedAds.NotificationTestClient.ruleset delete mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/Program.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/NotificationServer.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/AppSettingsController.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.js create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.js.map create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.min.js create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.min.js.map create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/toastr/toastr.min.css create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/toastr/toastr.min.js diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Dockerfile b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Dockerfile index 231116a82..7148eb8a5 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Dockerfile +++ b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.GRPC/Dockerfile b/src/ClassifiedAds.Projects/ClassifiedAds.GRPC/Dockerfile index 442e82a57..6e360c086 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.GRPC/Dockerfile +++ b/src/ClassifiedAds.Projects/ClassifiedAds.GRPC/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Dockerfile b/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Dockerfile index ac8dd2769..1dd693841 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Dockerfile +++ b/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Dockerfile b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Dockerfile index 759fe1ba0..8f1353b5d 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Dockerfile +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/ConfigurationOptions/AppSettings.cs b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/ConfigurationOptions/AppSettings.cs new file mode 100644 index 000000000..5c27b2f5b --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/ConfigurationOptions/AppSettings.cs @@ -0,0 +1,9 @@ +namespace ClassifiedAds.NotificationServer.ConfigurationOptions +{ + public class AppSettings + { + public string AllowedHosts { get; set; } + + public CORS CORS { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/ConfigurationOptions/CORS.cs b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/ConfigurationOptions/CORS.cs new file mode 100644 index 000000000..b20cf0d36 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/ConfigurationOptions/CORS.cs @@ -0,0 +1,7 @@ +namespace ClassifiedAds.NotificationServer.ConfigurationOptions +{ + public class CORS + { + public string[] AllowedOrigins { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Dockerfile b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Dockerfile index 366b185df..cbd0bf04e 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Dockerfile +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Startup.cs index 58675d61a..823d826da 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Startup.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using ClassifiedAds.NotificationServer.ConfigurationOptions; using ClassifiedAds.NotificationServer.Hubs; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -13,12 +10,31 @@ namespace ClassifiedAds.NotificationServer { public class Startup { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddSignalR() .AddMessagePackProtocol(); + + var appSettings = new AppSettings(); + Configuration.Bind(appSettings); + + services.AddCors(options => + { + options.AddPolicy("AllowedOrigins", builder => builder + .WithOrigins(appSettings.CORS.AllowedOrigins) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -31,6 +47,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); + app.UseCors("AllowedOrigins"); + app.UseEndpoints(endpoints => { endpoints.MapHub("/SimulatedLongRunningTaskHub"); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/appsettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/appsettings.json index def9159a7..ec60d08ff 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/appsettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/appsettings.json @@ -4,5 +4,8 @@ "Default": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "CORS": { + "AllowedOrigins": [ "https://localhost:44364", "http://localhost:9003" ] + } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/ClassifiedAds.NotificationTestClient.csproj b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/ClassifiedAds.NotificationTestClient.csproj deleted file mode 100644 index d722da02a..000000000 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/ClassifiedAds.NotificationTestClient.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - Exe - netcoreapp3.1 - ClassifiedAds.NotificationTestClient.ruleset - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/ClassifiedAds.NotificationTestClient.ruleset b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/ClassifiedAds.NotificationTestClient.ruleset deleted file mode 100644 index db4598310..000000000 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/ClassifiedAds.NotificationTestClient.ruleset +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/Program.cs b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/Program.cs deleted file mode 100644 index 92ab42dd6..000000000 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationTestClient/Program.cs +++ /dev/null @@ -1,95 +0,0 @@ -using ClassifiedAds.DomainServices.DomainEvents; -using ClassifiedAds.Infrastructure.MessageBrokers.AzureQueue; -using ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus; -using ClassifiedAds.Infrastructure.MessageBrokers.Kafka; -using ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ; -using Microsoft.AspNetCore.SignalR.Client; -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace ClassifiedAds.NotificationTestClient -{ - internal class Program - { - private static void Main(string[] args) - { - var connection = new HubConnectionBuilder() - .WithUrl("http://localhost:62710/SimulatedLongRunningTaskHub") - .AddMessagePackProtocol() - .Build(); - - connection.On("ReceiveTaskStatus", (message) => { Console.WriteLine(message); }); - connection.StartAsync().GetAwaiter().GetResult(); - - var rabbitMQFileUploadedEventReceiver = new RabbitMQReceiver(new RabbitMQReceiverOptions - { - HostName = "localhost", - UserName = "guest", - Password = "guest", - QueueName = "classifiedadds_fileuploaded", - }); - - rabbitMQFileUploadedEventReceiver.Receive(data => - { - Console.WriteLine("RabbitMQ - File Uploaded: " + data.FileEntry.Id); - }); - - var rabbitMQFileDeletedEventReceiver = new RabbitMQReceiver(new RabbitMQReceiverOptions - { - HostName = "localhost", - UserName = "guest", - Password = "guest", - QueueName = "classifiedadds_filedeleted", - }); - - rabbitMQFileDeletedEventReceiver.Receive(data => - { - Console.WriteLine("RabbitMQ - File Deleted: " + data.FileEntry.Id); - }); - - var kafkaFileUploadedEventReceiver = new KafkaReceiver("localhost:9092", "classifiedadds_fileuploaded", "classified"); - - kafkaFileUploadedEventReceiver.Receive(data => - { - Console.WriteLine("Kafka - File Uploaded: " + data.FileEntry.Id); - }); - - var kafkaFileDeletedEventReceiver = new KafkaReceiver("localhost:9092", "classifiedadds_filedeleted", "classified"); - - kafkaFileDeletedEventReceiver.Receive(data => - { - Console.WriteLine("Kafka - File Deleted: " + data.FileEntry.Id); - }); - - var azureQueueFileUploadedEventReceiver = new AzureQueueReceiver("DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=xxx;EndpointSuffix=core.windows.net", "classifiedadds-fileuploaded"); - azureQueueFileUploadedEventReceiver.Receive(data => - { - Console.WriteLine("AzureQueue - File Uploaded:" + data.FileEntry.Id); - }); - - var azureQueueFileDeletedEventReceiver = new AzureQueueReceiver("DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=xxx;EndpointSuffix=core.windows.net", "classifiedadds-filedeleted"); - azureQueueFileDeletedEventReceiver.Receive(data => - { - Console.WriteLine("AzureQueue - File Deleted:" + data.FileEntry.Id); - }); - - var azureServiceBusFileUploadedEventReceiver = new AzureServiceBusReceiver("Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=xxx;SharedAccessKey=xxx", "classifiedadds_fileuploaded"); - azureServiceBusFileUploadedEventReceiver.Receive(data => - { - Console.WriteLine("AzureServiceBus - File Uploaded:" + data.FileEntry.Id); - }); - - var azureServiceBusFileDeletedEventReceiver = new AzureServiceBusReceiver("Endpoint=sb://xxx.servicebus.windows.net/;SharedAccessKeyName=xxx;SharedAccessKey=xxx", "classifiedadds_filedeleted"); - azureServiceBusFileDeletedEventReceiver.Receive(data => - { - Console.WriteLine("AzureServiceBus - File Deleted:" + data.FileEntry.Id); - }); - - Console.WriteLine("Listening..."); - Console.ReadLine(); - - rabbitMQFileUploadedEventReceiver.Dispose(); - rabbitMQFileDeletedEventReceiver.Dispose(); - } - } -} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Ocelot/Dockerfile b/src/ClassifiedAds.Projects/ClassifiedAds.Ocelot/Dockerfile index cd031c6ad..6cc50feb4 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Ocelot/Dockerfile +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Ocelot/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Projects.sln b/src/ClassifiedAds.Projects/ClassifiedAds.Projects.sln index 03d7fad53..b2076672a 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Projects.sln +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Projects.sln @@ -39,8 +39,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.BackgroundSer EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.NotificationServer", "ClassifiedAds.NotificationServer\ClassifiedAds.NotificationServer.csproj", "{53FE6014-FBEC-447A-90AA-F579B13C4B8E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.NotificationTestClient", "ClassifiedAds.NotificationTestClient\ClassifiedAds.NotificationTestClient.csproj", "{118A3740-319B-4A92-9AD2-974002299A0B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Migrator", "ClassifiedAds.Migrator\ClassifiedAds.Migrator.csproj", "{95943383-5C81-45B6-8876-4789C00A9255}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{60BC7A82-187C-43E2-8C37-DE610700A7FA}" @@ -123,10 +121,6 @@ Global {53FE6014-FBEC-447A-90AA-F579B13C4B8E}.Debug|Any CPU.Build.0 = Debug|Any CPU {53FE6014-FBEC-447A-90AA-F579B13C4B8E}.Release|Any CPU.ActiveCfg = Release|Any CPU {53FE6014-FBEC-447A-90AA-F579B13C4B8E}.Release|Any CPU.Build.0 = Release|Any CPU - {118A3740-319B-4A92-9AD2-974002299A0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {118A3740-319B-4A92-9AD2-974002299A0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {118A3740-319B-4A92-9AD2-974002299A0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {118A3740-319B-4A92-9AD2-974002299A0B}.Release|Any CPU.Build.0 = Release|Any CPU {95943383-5C81-45B6-8876-4789C00A9255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {95943383-5C81-45B6-8876-4789C00A9255}.Debug|Any CPU.Build.0 = Debug|Any CPU {95943383-5C81-45B6-8876-4789C00A9255}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -173,7 +167,6 @@ Global {3B4CC496-3DD8-4FAC-8CBD-7D3823836410} = {9E758648-749A-45B1-9096-0A4B05427626} {D579E4B4-FE95-41F7-A67A-2D46BB29396E} = {C6061573-0203-4860-AD42-4C6C2CF55110} {53FE6014-FBEC-447A-90AA-F579B13C4B8E} = {C6061573-0203-4860-AD42-4C6C2CF55110} - {118A3740-319B-4A92-9AD2-974002299A0B} = {9E758648-749A-45B1-9096-0A4B05427626} {95943383-5C81-45B6-8876-4789C00A9255} = {1FB7D402-B430-4487-AAD3-CB96EA00ACB1} {FDBA0506-A4E7-405B-918D-66A195F16C95} = {B7638821-9A76-4994-90DD-8F9C71478E7B} {8BA96FA5-059D-4BBC-B124-39A262BC4BE9} = {173ECE2E-FF30-4471-9484-2410BB7963C8} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Dockerfile b/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Dockerfile index 674d6048c..e6d1b60f4 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Dockerfile +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj index b8f60943d..c531d7249 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj @@ -13,6 +13,8 @@ + + diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs index 9cbab56c9..ec9a73983 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs @@ -12,6 +12,8 @@ public class AppSettings public ResourceServer ResourceServer { get; set; } + public NotificationServer NotificationServer { get; set; } + public string AllowedHosts { get; set; } public string CurrentUrl { get; set; } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/NotificationServer.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/NotificationServer.cs new file mode 100644 index 000000000..172779c79 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/NotificationServer.cs @@ -0,0 +1,7 @@ +namespace ClassifiedAds.WebMVC.ConfigurationOptions +{ + public class NotificationServer + { + public string Endpoint { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/AppSettingsController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/AppSettingsController.cs new file mode 100644 index 000000000..6cf127e33 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/AppSettingsController.cs @@ -0,0 +1,30 @@ +using ClassifiedAds.WebMVC.ConfigurationOptions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +namespace ClassifiedAds.WebMVC.Controllers +{ + public class AppSettingsController : Controller + { + private readonly AppSettings _appSettings; + + public AppSettingsController(IOptionsSnapshot appSettings) + { + _appSettings = appSettings.Value; + } + + public IActionResult Index() + { + string text = @" + const appSettings = { + NotificationServer: { + Endpoint: '" + _appSettings.NotificationServer.Endpoint + @"' + } + }"; + + var rs = Content(text); + rs.ContentType = "application/javascript"; + return rs; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Dockerfile b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Dockerfile index e148d0ccb..9df0859af 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Dockerfile +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index da996a170..a586d502c 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -23,6 +23,7 @@ using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -53,6 +54,8 @@ public Startup(IConfiguration configuration, IWebHostEnvironment env) public IConfiguration Configuration { get; } + private AppSettings AppSettings { get; set; } + // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { @@ -60,6 +63,8 @@ public void ConfigureServices(IServiceCollection services) var appSettings = new AppSettings(); Configuration.Bind(appSettings); + AppSettings = appSettings; + var validationResult = appSettings.Validate(); if (validationResult.Failed) { @@ -267,6 +272,101 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { endpoints.MapDefaultControllerRoute(); }); + + try + { + TestMessageBrokerReceivers(); + } + catch + { + } + } + + private void TestMessageBrokerReceivers() + { + IMessageReceiver fileUploadedMessageQueueReceiver = null; + IMessageReceiver fileDeletedMessageQueueReceiver = null; + + if (AppSettings.MessageBroker.UsedRabbitMQ()) + { + fileUploadedMessageQueueReceiver = new RabbitMQReceiver(new RabbitMQReceiverOptions + { + HostName = AppSettings.MessageBroker.RabbitMQ.HostName, + UserName = AppSettings.MessageBroker.RabbitMQ.UserName, + Password = AppSettings.MessageBroker.RabbitMQ.Password, + QueueName = "classifiedadds_fileuploaded", + }); + + fileDeletedMessageQueueReceiver = new RabbitMQReceiver(new RabbitMQReceiverOptions + { + HostName = AppSettings.MessageBroker.RabbitMQ.HostName, + UserName = AppSettings.MessageBroker.RabbitMQ.UserName, + Password = AppSettings.MessageBroker.RabbitMQ.Password, + QueueName = "classifiedadds_filedeleted", + }); + } + + if (AppSettings.MessageBroker.UsedKafka()) + { + fileUploadedMessageQueueReceiver = new KafkaReceiver( + AppSettings.MessageBroker.Kafka.BootstrapServers, + AppSettings.MessageBroker.Kafka.Topic_FileUploaded, + "classified"); + + fileDeletedMessageQueueReceiver = new KafkaReceiver( + AppSettings.MessageBroker.Kafka.BootstrapServers, + AppSettings.MessageBroker.Kafka.Topic_FileDeleted, + "classified"); + } + + if (AppSettings.MessageBroker.UsedAzureQueue()) + { + fileUploadedMessageQueueReceiver = new AzureQueueReceiver( + AppSettings.MessageBroker.AzureQueue.ConnectionString, + AppSettings.MessageBroker.AzureQueue.QueueName_FileUploaded); + + fileDeletedMessageQueueReceiver = new AzureQueueReceiver( + AppSettings.MessageBroker.AzureQueue.ConnectionString, + AppSettings.MessageBroker.AzureQueue.QueueName_FileDeleted); + } + + if (AppSettings.MessageBroker.UsedAzureServiceBus()) + { + fileUploadedMessageQueueReceiver = new AzureServiceBusReceiver( + AppSettings.MessageBroker.AzureServiceBus.ConnectionString, + AppSettings.MessageBroker.AzureServiceBus.QueueName_FileUploaded); + + fileDeletedMessageQueueReceiver = new AzureServiceBusReceiver( + AppSettings.MessageBroker.AzureServiceBus.ConnectionString, + AppSettings.MessageBroker.AzureServiceBus.QueueName_FileDeleted); + } + + var connection = new HubConnectionBuilder() + .WithUrl($"{AppSettings.NotificationServer.Endpoint}/SimulatedLongRunningTaskHub") + .AddMessagePackProtocol() + .Build(); + + fileUploadedMessageQueueReceiver?.Receive(data => + { + string message = data.FileEntry.Id.ToString(); + + connection.StartAsync().GetAwaiter().GetResult(); + + connection.InvokeAsync("SendTaskStatus", $"{AppSettings.MessageBroker.Provider} - File Uploaded", message); + + connection.StopAsync().GetAwaiter().GetResult(); + }); + + fileDeletedMessageQueueReceiver?.Receive(data => + { + string message = data.FileEntry.Id.ToString(); + + connection.StartAsync().GetAwaiter().GetResult(); + + connection.InvokeAsync("SendTaskStatus", $"{AppSettings.MessageBroker.Provider} - File Deleted", message); + + connection.StopAsync().GetAwaiter().GetResult(); + }); } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml index 1def57cc3..1823330e8 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml @@ -19,6 +19,7 @@ crossorigin="anonymous" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" /> + @@ -82,7 +83,7 @@
- © 2019 - ClassifiedAds.WebMVC - Privacy + © 2020 - ClassifiedAds.WebMVC - Privacy
@@ -104,6 +105,9 @@ integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o"> + + + @RenderSection("Scripts", required: false) diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json index 0fdbab80f..d82bb8628 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json @@ -10,6 +10,9 @@ "ResourceServer": { "Endpoint": "https://localhost:44312" }, + "NotificationServer": { + "Endpoint": "http://localhost:62710" + }, "Logging": { "LogLevel": { "Default": "Warning" diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/js/site.js b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/js/site.js index ac49c1864..9754aa391 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/js/site.js +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/js/site.js @@ -2,3 +2,16 @@ // for details on configuring this project to bundle and minify static web assets. // Write your JavaScript code. + +const connection = new signalR.HubConnectionBuilder() + .withUrl(appSettings.NotificationServer.Endpoint + "/SimulatedLongRunningTaskHub") + .configureLogging(signalR.LogLevel.Information) + .build(); + +connection.start().then(function () { + toastr.info("Connected to SimulatedLongRunningTaskHub") +}); + +connection.on("ReceiveTaskStatus", (message) => { + toastr.info("Received Message from Notification Server:
" + message) +}); \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.js b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.js new file mode 100644 index 000000000..6e6c95e8c --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.js @@ -0,0 +1,4922 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["signalR"] = factory(); + else + root["signalR"] = factory(); +})(window, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "VERSION", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["VERSION"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "AbortError", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["AbortError"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HttpError", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["HttpError"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TimeoutError", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["TimeoutError"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HttpClient", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["HttpClient"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HttpResponse", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["HttpResponse"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DefaultHttpClient", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["DefaultHttpClient"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HubConnection", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnection"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HubConnectionState", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnectionState"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HubConnectionBuilder", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnectionBuilder"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MessageType", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["MessageType"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "LogLevel", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["LogLevel"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HttpTransportType", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["HttpTransportType"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TransferFormat", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["TransferFormat"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "NullLogger", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["NullLogger"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "JsonHubProtocol", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["JsonHubProtocol"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Subject", function() { return _index__WEBPACK_IMPORTED_MODULE_1__["Subject"]; }); + +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// This is where we add any polyfills we'll need for the browser. It is the entry module for browser-specific builds. + +// Copy from Array.prototype into Uint8Array to polyfill on IE. It's OK because the implementations of indexOf and slice use properties +// that exist on Uint8Array with the same name, and JavaScript is magic. +// We make them 'writable' because the Buffer polyfill messes with it as well. +if (!Uint8Array.prototype.indexOf) { + Object.defineProperty(Uint8Array.prototype, "indexOf", { + value: Array.prototype.indexOf, + writable: true, + }); +} +if (!Uint8Array.prototype.slice) { + Object.defineProperty(Uint8Array.prototype, "slice", { + // wrap the slice in Uint8Array so it looks like a Uint8Array.slice call + // tslint:disable-next-line:object-literal-shorthand + value: function (start, end) { return new Uint8Array(Array.prototype.slice.call(this, start, end)); }, + writable: true, + }); +} +if (!Uint8Array.prototype.forEach) { + Object.defineProperty(Uint8Array.prototype, "forEach", { + value: Array.prototype.forEach, + writable: true, + }); +} + + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global) {var require;/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version v4.2.2+97478eb6 + */ + +(function (global, factory) { + true ? module.exports = factory() : + undefined; +}(this, (function () { 'use strict'; + +function objectOrFunction(x) { + var type = typeof x; + return x !== null && (type === 'object' || type === 'function'); +} + +function isFunction(x) { + return typeof x === 'function'; +} + + + +var _isArray = void 0; +if (Array.isArray) { + _isArray = Array.isArray; +} else { + _isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; +} + +var isArray = _isArray; + +var len = 0; +var vertxNext = void 0; +var customSchedulerFn = void 0; + +var asap = function asap(callback, arg) { + queue[len] = callback; + queue[len + 1] = arg; + len += 2; + if (len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (customSchedulerFn) { + customSchedulerFn(flush); + } else { + scheduleFlush(); + } + } +}; + +function setScheduler(scheduleFn) { + customSchedulerFn = scheduleFn; +} + +function setAsap(asapFn) { + asap = asapFn; +} + +var browserWindow = typeof window !== 'undefined' ? window : undefined; +var browserGlobal = browserWindow || {}; +var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; +var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; + +// test for web worker but not in IE10 +var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; + +// node +function useNextTick() { + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // see https://github.com/cujojs/when/issues/410 for details + return function () { + return process.nextTick(flush); + }; +} + +// vertx +function useVertxTimer() { + if (typeof vertxNext !== 'undefined') { + return function () { + vertxNext(flush); + }; + } + + return useSetTimeout(); +} + +function useMutationObserver() { + var iterations = 0; + var observer = new BrowserMutationObserver(flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function () { + node.data = iterations = ++iterations % 2; + }; +} + +// web worker +function useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = flush; + return function () { + return channel.port2.postMessage(0); + }; +} + +function useSetTimeout() { + // Store setTimeout reference so es6-promise will be unaffected by + // other code modifying setTimeout (like sinon.useFakeTimers()) + var globalSetTimeout = setTimeout; + return function () { + return globalSetTimeout(flush, 1); + }; +} + +var queue = new Array(1000); +function flush() { + for (var i = 0; i < len; i += 2) { + var callback = queue[i]; + var arg = queue[i + 1]; + + callback(arg); + + queue[i] = undefined; + queue[i + 1] = undefined; + } + + len = 0; +} + +function attemptVertx() { + try { + var r = require; + var vertx = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module 'vertx'"); e.code = 'MODULE_NOT_FOUND'; throw e; }())); + vertxNext = vertx.runOnLoop || vertx.runOnContext; + return useVertxTimer(); + } catch (e) { + return useSetTimeout(); + } +} + +var scheduleFlush = void 0; +// Decide what async method to use to triggering processing of queued callbacks: +if (isNode) { + scheduleFlush = useNextTick(); +} else if (BrowserMutationObserver) { + scheduleFlush = useMutationObserver(); +} else if (isWorker) { + scheduleFlush = useMessageChannel(); +} else if (browserWindow === undefined && "function" === 'function') { + scheduleFlush = attemptVertx(); +} else { + scheduleFlush = useSetTimeout(); +} + +function then(onFulfillment, onRejection) { + var parent = this; + + var child = new this.constructor(noop); + + if (child[PROMISE_ID] === undefined) { + makePromise(child); + } + + var _state = parent._state; + + + if (_state) { + var callback = arguments[_state - 1]; + asap(function () { + return invokeCallback(_state, child, callback, parent._result); + }); + } else { + subscribe(parent, child, onFulfillment, onRejection); + } + + return child; +} + +/** + `Promise.resolve` returns a promise that will become resolved with the + passed `value`. It is shorthand for the following: + + ```javascript + let promise = new Promise(function(resolve, reject){ + resolve(1); + }); + + promise.then(function(value){ + // value === 1 + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + let promise = Promise.resolve(1); + + promise.then(function(value){ + // value === 1 + }); + ``` + + @method resolve + @static + @param {Any} value value that the returned promise will be resolved with + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` +*/ +function resolve$1(object) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + var promise = new Constructor(noop); + resolve(promise, object); + return promise; +} + +var PROMISE_ID = Math.random().toString(36).substring(16); + +function noop() {} + +var PENDING = void 0; +var FULFILLED = 1; +var REJECTED = 2; + +var GET_THEN_ERROR = new ErrorObject(); + +function selfFulfillment() { + return new TypeError("You cannot resolve a promise with itself"); +} + +function cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); +} + +function getThen(promise) { + try { + return promise.then; + } catch (error) { + GET_THEN_ERROR.error = error; + return GET_THEN_ERROR; + } +} + +function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) { + try { + then$$1.call(value, fulfillmentHandler, rejectionHandler); + } catch (e) { + return e; + } +} + +function handleForeignThenable(promise, thenable, then$$1) { + asap(function (promise) { + var sealed = false; + var error = tryThen(then$$1, thenable, function (value) { + if (sealed) { + return; + } + sealed = true; + if (thenable !== value) { + resolve(promise, value); + } else { + fulfill(promise, value); + } + }, function (reason) { + if (sealed) { + return; + } + sealed = true; + + reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + reject(promise, error); + } + }, promise); +} + +function handleOwnThenable(promise, thenable) { + if (thenable._state === FULFILLED) { + fulfill(promise, thenable._result); + } else if (thenable._state === REJECTED) { + reject(promise, thenable._result); + } else { + subscribe(thenable, undefined, function (value) { + return resolve(promise, value); + }, function (reason) { + return reject(promise, reason); + }); + } +} + +function handleMaybeThenable(promise, maybeThenable, then$$1) { + if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) { + handleOwnThenable(promise, maybeThenable); + } else { + if (then$$1 === GET_THEN_ERROR) { + reject(promise, GET_THEN_ERROR.error); + GET_THEN_ERROR.error = null; + } else if (then$$1 === undefined) { + fulfill(promise, maybeThenable); + } else if (isFunction(then$$1)) { + handleForeignThenable(promise, maybeThenable, then$$1); + } else { + fulfill(promise, maybeThenable); + } + } +} + +function resolve(promise, value) { + if (promise === value) { + reject(promise, selfFulfillment()); + } else if (objectOrFunction(value)) { + handleMaybeThenable(promise, value, getThen(value)); + } else { + fulfill(promise, value); + } +} + +function publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + publish(promise); +} + +function fulfill(promise, value) { + if (promise._state !== PENDING) { + return; + } + + promise._result = value; + promise._state = FULFILLED; + + if (promise._subscribers.length !== 0) { + asap(publish, promise); + } +} + +function reject(promise, reason) { + if (promise._state !== PENDING) { + return; + } + promise._state = REJECTED; + promise._result = reason; + + asap(publishRejection, promise); +} + +function subscribe(parent, child, onFulfillment, onRejection) { + var _subscribers = parent._subscribers; + var length = _subscribers.length; + + + parent._onerror = null; + + _subscribers[length] = child; + _subscribers[length + FULFILLED] = onFulfillment; + _subscribers[length + REJECTED] = onRejection; + + if (length === 0 && parent._state) { + asap(publish, parent); + } +} + +function publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (subscribers.length === 0) { + return; + } + + var child = void 0, + callback = void 0, + detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } + + promise._subscribers.length = 0; +} + +function ErrorObject() { + this.error = null; +} + +var TRY_CATCH_ERROR = new ErrorObject(); + +function tryCatch(callback, detail) { + try { + return callback(detail); + } catch (e) { + TRY_CATCH_ERROR.error = e; + return TRY_CATCH_ERROR; + } +} + +function invokeCallback(settled, promise, callback, detail) { + var hasCallback = isFunction(callback), + value = void 0, + error = void 0, + succeeded = void 0, + failed = void 0; + + if (hasCallback) { + value = tryCatch(callback, detail); + + if (value === TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value.error = null; + } else { + succeeded = true; + } + + if (promise === value) { + reject(promise, cannotReturnOwn()); + return; + } + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== PENDING) { + // noop + } else if (hasCallback && succeeded) { + resolve(promise, value); + } else if (failed) { + reject(promise, error); + } else if (settled === FULFILLED) { + fulfill(promise, value); + } else if (settled === REJECTED) { + reject(promise, value); + } +} + +function initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value) { + resolve(promise, value); + }, function rejectPromise(reason) { + reject(promise, reason); + }); + } catch (e) { + reject(promise, e); + } +} + +var id = 0; +function nextId() { + return id++; +} + +function makePromise(promise) { + promise[PROMISE_ID] = id++; + promise._state = undefined; + promise._result = undefined; + promise._subscribers = []; +} + +function validationError() { + return new Error('Array Methods must be provided an Array'); +} + +function validationError() { + return new Error('Array Methods must be provided an Array'); +} + +var Enumerator = function () { + function Enumerator(Constructor, input) { + this._instanceConstructor = Constructor; + this.promise = new Constructor(noop); + + if (!this.promise[PROMISE_ID]) { + makePromise(this.promise); + } + + if (isArray(input)) { + this.length = input.length; + this._remaining = input.length; + + this._result = new Array(this.length); + + if (this.length === 0) { + fulfill(this.promise, this._result); + } else { + this.length = this.length || 0; + this._enumerate(input); + if (this._remaining === 0) { + fulfill(this.promise, this._result); + } + } + } else { + reject(this.promise, validationError()); + } + } + + Enumerator.prototype._enumerate = function _enumerate(input) { + for (var i = 0; this._state === PENDING && i < input.length; i++) { + this._eachEntry(input[i], i); + } + }; + + Enumerator.prototype._eachEntry = function _eachEntry(entry, i) { + var c = this._instanceConstructor; + var resolve$$1 = c.resolve; + + + if (resolve$$1 === resolve$1) { + var _then = getThen(entry); + + if (_then === then && entry._state !== PENDING) { + this._settledAt(entry._state, i, entry._result); + } else if (typeof _then !== 'function') { + this._remaining--; + this._result[i] = entry; + } else if (c === Promise$2) { + var promise = new c(noop); + handleMaybeThenable(promise, entry, _then); + this._willSettleAt(promise, i); + } else { + this._willSettleAt(new c(function (resolve$$1) { + return resolve$$1(entry); + }), i); + } + } else { + this._willSettleAt(resolve$$1(entry), i); + } + }; + + Enumerator.prototype._settledAt = function _settledAt(state, i, value) { + var promise = this.promise; + + + if (promise._state === PENDING) { + this._remaining--; + + if (state === REJECTED) { + reject(promise, value); + } else { + this._result[i] = value; + } + } + + if (this._remaining === 0) { + fulfill(promise, this._result); + } + }; + + Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) { + var enumerator = this; + + subscribe(promise, undefined, function (value) { + return enumerator._settledAt(FULFILLED, i, value); + }, function (reason) { + return enumerator._settledAt(REJECTED, i, reason); + }); + }; + + return Enumerator; +}(); + +/** + `Promise.all` accepts an array of promises, and returns a new promise which + is fulfilled with an array of fulfillment values for the passed promises, or + rejected with the reason of the first passed promise to be rejected. It casts all + elements of the passed iterable to promises as it runs this algorithm. + + Example: + + ```javascript + let promise1 = resolve(1); + let promise2 = resolve(2); + let promise3 = resolve(3); + let promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // The array here would be [ 1, 2, 3 ]; + }); + ``` + + If any of the `promises` given to `all` are rejected, the first promise + that is rejected will be given as an argument to the returned promises's + rejection handler. For example: + + Example: + + ```javascript + let promise1 = resolve(1); + let promise2 = reject(new Error("2")); + let promise3 = reject(new Error("3")); + let promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(error) { + // error.message === "2" + }); + ``` + + @method all + @static + @param {Array} entries array of promises + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all `promises` have been + fulfilled, or rejected if any of them become rejected. + @static +*/ +function all(entries) { + return new Enumerator(this, entries).promise; +} + +/** + `Promise.race` returns a new promise which is settled in the same way as the + first passed promise to settle. + + Example: + + ```javascript + let promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + let promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 2'); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // result === 'promise 2' because it was resolved before promise1 + // was resolved. + }); + ``` + + `Promise.race` is deterministic in that only the state of the first + settled promise matters. For example, even if other promises given to the + `promises` array argument are resolved, but the first settled promise has + become rejected before the other promises became fulfilled, the returned + promise will become rejected: + + ```javascript + let promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + let promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + reject(new Error('promise 2')); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // Code here never runs + }, function(reason){ + // reason.message === 'promise 2' because promise 2 became rejected before + // promise 1 became fulfilled + }); + ``` + + An example real-world use case is implementing timeouts: + + ```javascript + Promise.race([ajax('foo.json'), timeout(5000)]) + ``` + + @method race + @static + @param {Array} promises array of promises to observe + Useful for tooling. + @return {Promise} a promise which settles in the same way as the first passed + promise to settle. +*/ +function race(entries) { + /*jshint validthis:true */ + var Constructor = this; + + if (!isArray(entries)) { + return new Constructor(function (_, reject) { + return reject(new TypeError('You must pass an array to race.')); + }); + } else { + return new Constructor(function (resolve, reject) { + var length = entries.length; + for (var i = 0; i < length; i++) { + Constructor.resolve(entries[i]).then(resolve, reject); + } + }); + } +} + +/** + `Promise.reject` returns a promise rejected with the passed `reason`. + It is shorthand for the following: + + ```javascript + let promise = new Promise(function(resolve, reject){ + reject(new Error('WHOOPS')); + }); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + let promise = Promise.reject(new Error('WHOOPS')); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + @method reject + @static + @param {Any} reason value that the returned promise will be rejected with. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. +*/ +function reject$1(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(noop); + reject(promise, reason); + return promise; +} + +function needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); +} + +function needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); +} + +/** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise's eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + let promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + let xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } + + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class Promise + @param {Function} resolver + Useful for tooling. + @constructor +*/ + +var Promise$2 = function () { + function Promise(resolver) { + this[PROMISE_ID] = nextId(); + this._result = this._state = undefined; + this._subscribers = []; + + if (noop !== resolver) { + typeof resolver !== 'function' && needsResolver(); + this instanceof Promise ? initializePromise(this, resolver) : needsNew(); + } + } + + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + Chaining + -------- + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + Assimilation + ------------ + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + If the assimliated promise rejects, then the downstream promise will also reject. + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + Simple Example + -------------- + Synchronous Example + ```javascript + let result; + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + Errback Example + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + Promise Example; + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + Advanced Example + -------------- + Synchronous Example + ```javascript + let author, books; + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + Errback Example + ```js + function foundBooks(books) { + } + function failure(reason) { + } + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + Promise Example; + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} + */ + + + Promise.prototype.catch = function _catch(onRejection) { + return this.then(null, onRejection); + }; + + /** + `finally` will be invoked regardless of the promise's fate just as native + try/catch/finally behaves + + Synchronous example: + + ```js + findAuthor() { + if (Math.random() > 0.5) { + throw new Error(); + } + return new Author(); + } + + try { + return findAuthor(); // succeed or fail + } catch(error) { + return findOtherAuther(); + } finally { + // always runs + // doesn't affect the return value + } + ``` + + Asynchronous example: + + ```js + findAuthor().catch(function(reason){ + return findOtherAuther(); + }).finally(function(){ + // author was either found, or not + }); + ``` + + @method finally + @param {Function} callback + @return {Promise} + */ + + + Promise.prototype.finally = function _finally(callback) { + var promise = this; + var constructor = promise.constructor; + + return promise.then(function (value) { + return constructor.resolve(callback()).then(function () { + return value; + }); + }, function (reason) { + return constructor.resolve(callback()).then(function () { + throw reason; + }); + }); + }; + + return Promise; +}(); + +Promise$2.prototype.then = then; +Promise$2.all = all; +Promise$2.race = race; +Promise$2.resolve = resolve$1; +Promise$2.reject = reject$1; +Promise$2._setScheduler = setScheduler; +Promise$2._setAsap = setAsap; +Promise$2._asap = asap; + +/*global self*/ +function polyfill() { + var local = void 0; + + if (typeof global !== 'undefined') { + local = global; + } else if (typeof self !== 'undefined') { + local = self; + } else { + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } + } + + var P = local.Promise; + + if (P) { + var promiseToString = null; + try { + promiseToString = Object.prototype.toString.call(P.resolve()); + } catch (e) { + // silently ignored + } + + if (promiseToString === '[object Promise]' && !P.cast) { + return; + } + } + + local.Promise = Promise$2; +} + +// Strange compat.. +Promise$2.polyfill = polyfill; +Promise$2.Promise = Promise$2; + +Promise$2.polyfill(); + +return Promise$2; + +}))); + + + +//# sourceMappingURL=es6-promise.auto.map + +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || new Function("return this")(); +} catch (e) { + // This works if the window reference is available + if (typeof window === "object") g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; + + +/***/ }), +/* 3 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VERSION", function() { return VERSION; }); +/* harmony import */ var _Errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "AbortError", function() { return _Errors__WEBPACK_IMPORTED_MODULE_0__["AbortError"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HttpError", function() { return _Errors__WEBPACK_IMPORTED_MODULE_0__["HttpError"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TimeoutError", function() { return _Errors__WEBPACK_IMPORTED_MODULE_0__["TimeoutError"]; }); + +/* harmony import */ var _HttpClient__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HttpClient", function() { return _HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpClient"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HttpResponse", function() { return _HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpResponse"]; }); + +/* harmony import */ var _DefaultHttpClient__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "DefaultHttpClient", function() { return _DefaultHttpClient__WEBPACK_IMPORTED_MODULE_2__["DefaultHttpClient"]; }); + +/* harmony import */ var _HubConnection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(10); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HubConnection", function() { return _HubConnection__WEBPACK_IMPORTED_MODULE_3__["HubConnection"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HubConnectionState", function() { return _HubConnection__WEBPACK_IMPORTED_MODULE_3__["HubConnectionState"]; }); + +/* harmony import */ var _HubConnectionBuilder__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HubConnectionBuilder", function() { return _HubConnectionBuilder__WEBPACK_IMPORTED_MODULE_4__["HubConnectionBuilder"]; }); + +/* harmony import */ var _IHubProtocol__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(15); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MessageType", function() { return _IHubProtocol__WEBPACK_IMPORTED_MODULE_5__["MessageType"]; }); + +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(9); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "LogLevel", function() { return _ILogger__WEBPACK_IMPORTED_MODULE_6__["LogLevel"]; }); + +/* harmony import */ var _ITransport__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(20); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "HttpTransportType", function() { return _ITransport__WEBPACK_IMPORTED_MODULE_7__["HttpTransportType"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TransferFormat", function() { return _ITransport__WEBPACK_IMPORTED_MODULE_7__["TransferFormat"]; }); + +/* harmony import */ var _Loggers__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(14); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "NullLogger", function() { return _Loggers__WEBPACK_IMPORTED_MODULE_8__["NullLogger"]; }); + +/* harmony import */ var _JsonHubProtocol__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(25); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "JsonHubProtocol", function() { return _JsonHubProtocol__WEBPACK_IMPORTED_MODULE_9__["JsonHubProtocol"]; }); + +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(16); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Subject", function() { return _Subject__WEBPACK_IMPORTED_MODULE_10__["Subject"]; }); + +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Version token that will be replaced by the prepack command +/** The version of the SignalR client. */ +var VERSION = "3.1.0"; + + + + + + + + + + + + + +/***/ }), +/* 4 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HttpError", function() { return HttpError; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeoutError", function() { return TimeoutError; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AbortError", function() { return AbortError; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __extends = (undefined && undefined.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +/** Error thrown when an HTTP request fails. */ +var HttpError = /** @class */ (function (_super) { + __extends(HttpError, _super); + /** Constructs a new instance of {@link @microsoft/signalr.HttpError}. + * + * @param {string} errorMessage A descriptive error message. + * @param {number} statusCode The HTTP status code represented by this error. + */ + function HttpError(errorMessage, statusCode) { + var _newTarget = this.constructor; + var _this = this; + var trueProto = _newTarget.prototype; + _this = _super.call(this, errorMessage) || this; + _this.statusCode = statusCode; + // Workaround issue in Typescript compiler + // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200 + _this.__proto__ = trueProto; + return _this; + } + return HttpError; +}(Error)); + +/** Error thrown when a timeout elapses. */ +var TimeoutError = /** @class */ (function (_super) { + __extends(TimeoutError, _super); + /** Constructs a new instance of {@link @microsoft/signalr.TimeoutError}. + * + * @param {string} errorMessage A descriptive error message. + */ + function TimeoutError(errorMessage) { + var _newTarget = this.constructor; + if (errorMessage === void 0) { errorMessage = "A timeout occurred."; } + var _this = this; + var trueProto = _newTarget.prototype; + _this = _super.call(this, errorMessage) || this; + // Workaround issue in Typescript compiler + // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200 + _this.__proto__ = trueProto; + return _this; + } + return TimeoutError; +}(Error)); + +/** Error thrown when an action is aborted. */ +var AbortError = /** @class */ (function (_super) { + __extends(AbortError, _super); + /** Constructs a new instance of {@link AbortError}. + * + * @param {string} errorMessage A descriptive error message. + */ + function AbortError(errorMessage) { + var _newTarget = this.constructor; + if (errorMessage === void 0) { errorMessage = "An abort occurred."; } + var _this = this; + var trueProto = _newTarget.prototype; + _this = _super.call(this, errorMessage) || this; + // Workaround issue in Typescript compiler + // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200 + _this.__proto__ = trueProto; + return _this; + } + return AbortError; +}(Error)); + + + +/***/ }), +/* 5 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HttpResponse", function() { return HttpResponse; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HttpClient", function() { return HttpClient; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __assign = (undefined && undefined.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +/** Represents an HTTP response. */ +var HttpResponse = /** @class */ (function () { + function HttpResponse(statusCode, statusText, content) { + this.statusCode = statusCode; + this.statusText = statusText; + this.content = content; + } + return HttpResponse; +}()); + +/** Abstraction over an HTTP client. + * + * This class provides an abstraction over an HTTP client so that a different implementation can be provided on different platforms. + */ +var HttpClient = /** @class */ (function () { + function HttpClient() { + } + HttpClient.prototype.get = function (url, options) { + return this.send(__assign({}, options, { method: "GET", url: url })); + }; + HttpClient.prototype.post = function (url, options) { + return this.send(__assign({}, options, { method: "POST", url: url })); + }; + HttpClient.prototype.delete = function (url, options) { + return this.send(__assign({}, options, { method: "DELETE", url: url })); + }; + /** Gets all cookies that apply to the specified URL. + * + * @param url The URL that the cookies are valid for. + * @returns {string} A string containing all the key-value cookie pairs for the specified URL. + */ + // @ts-ignore + HttpClient.prototype.getCookieString = function (url) { + return ""; + }; + return HttpClient; +}()); + + + +/***/ }), +/* 6 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DefaultHttpClient", function() { return DefaultHttpClient; }); +/* harmony import */ var _Errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var _HttpClient__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* harmony import */ var _NodeHttpClient__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7); +/* harmony import */ var _XhrHttpClient__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __extends = (undefined && undefined.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); + + + + +/** Default implementation of {@link @microsoft/signalr.HttpClient}. */ +var DefaultHttpClient = /** @class */ (function (_super) { + __extends(DefaultHttpClient, _super); + /** Creates a new instance of the {@link @microsoft/signalr.DefaultHttpClient}, using the provided {@link @microsoft/signalr.ILogger} to log messages. */ + function DefaultHttpClient(logger) { + var _this = _super.call(this) || this; + if (typeof XMLHttpRequest !== "undefined") { + _this.httpClient = new _XhrHttpClient__WEBPACK_IMPORTED_MODULE_3__["XhrHttpClient"](logger); + } + else { + _this.httpClient = new _NodeHttpClient__WEBPACK_IMPORTED_MODULE_2__["NodeHttpClient"](logger); + } + return _this; + } + /** @inheritDoc */ + DefaultHttpClient.prototype.send = function (request) { + // Check that abort was not signaled before calling send + if (request.abortSignal && request.abortSignal.aborted) { + return Promise.reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["AbortError"]()); + } + if (!request.method) { + return Promise.reject(new Error("No method defined.")); + } + if (!request.url) { + return Promise.reject(new Error("No url defined.")); + } + return this.httpClient.send(request); + }; + DefaultHttpClient.prototype.getCookieString = function (url) { + return this.httpClient.getCookieString(url); + }; + return DefaultHttpClient; +}(_HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpClient"])); + + + +/***/ }), +/* 7 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NodeHttpClient", function() { return NodeHttpClient; }); +/* harmony import */ var _HttpClient__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __extends = (undefined && undefined.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +// This is an empty implementation of the NodeHttpClient that will be included in browser builds so the output file will be smaller + +/** @private */ +var NodeHttpClient = /** @class */ (function (_super) { + __extends(NodeHttpClient, _super); + // @ts-ignore: Need ILogger to compile, but unused variables generate errors + function NodeHttpClient(logger) { + return _super.call(this) || this; + } + NodeHttpClient.prototype.send = function () { + return Promise.reject(new Error("If using Node either provide an XmlHttpRequest polyfill or consume the cjs or esm script instead of the browser/signalr.js one.")); + }; + return NodeHttpClient; +}(_HttpClient__WEBPACK_IMPORTED_MODULE_0__["HttpClient"])); + + + +/***/ }), +/* 8 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "XhrHttpClient", function() { return XhrHttpClient; }); +/* harmony import */ var _Errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var _HttpClient__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __extends = (undefined && undefined.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); + + + +var XhrHttpClient = /** @class */ (function (_super) { + __extends(XhrHttpClient, _super); + function XhrHttpClient(logger) { + var _this = _super.call(this) || this; + _this.logger = logger; + return _this; + } + /** @inheritDoc */ + XhrHttpClient.prototype.send = function (request) { + var _this = this; + // Check that abort was not signaled before calling send + if (request.abortSignal && request.abortSignal.aborted) { + return Promise.reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["AbortError"]()); + } + if (!request.method) { + return Promise.reject(new Error("No method defined.")); + } + if (!request.url) { + return Promise.reject(new Error("No url defined.")); + } + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open(request.method, request.url, true); + xhr.withCredentials = true; + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + // Explicitly setting the Content-Type header for React Native on Android platform. + xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); + var headers = request.headers; + if (headers) { + Object.keys(headers) + .forEach(function (header) { + xhr.setRequestHeader(header, headers[header]); + }); + } + if (request.responseType) { + xhr.responseType = request.responseType; + } + if (request.abortSignal) { + request.abortSignal.onabort = function () { + xhr.abort(); + reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["AbortError"]()); + }; + } + if (request.timeout) { + xhr.timeout = request.timeout; + } + xhr.onload = function () { + if (request.abortSignal) { + request.abortSignal.onabort = null; + } + if (xhr.status >= 200 && xhr.status < 300) { + resolve(new _HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpResponse"](xhr.status, xhr.statusText, xhr.response || xhr.responseText)); + } + else { + reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["HttpError"](xhr.statusText, xhr.status)); + } + }; + xhr.onerror = function () { + _this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Warning, "Error from HTTP request. " + xhr.status + ": " + xhr.statusText + "."); + reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["HttpError"](xhr.statusText, xhr.status)); + }; + xhr.ontimeout = function () { + _this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Warning, "Timeout from HTTP request."); + reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["TimeoutError"]()); + }; + xhr.send(request.content || ""); + }); + }; + return XhrHttpClient; +}(_HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpClient"])); + + + +/***/ }), +/* 9 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LogLevel", function() { return LogLevel; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// These values are designed to match the ASP.NET Log Levels since that's the pattern we're emulating here. +/** Indicates the severity of a log message. + * + * Log Levels are ordered in increasing severity. So `Debug` is more severe than `Trace`, etc. + */ +var LogLevel; +(function (LogLevel) { + /** Log level for very low severity diagnostic messages. */ + LogLevel[LogLevel["Trace"] = 0] = "Trace"; + /** Log level for low severity diagnostic messages. */ + LogLevel[LogLevel["Debug"] = 1] = "Debug"; + /** Log level for informational diagnostic messages. */ + LogLevel[LogLevel["Information"] = 2] = "Information"; + /** Log level for diagnostic messages that indicate a non-fatal problem. */ + LogLevel[LogLevel["Warning"] = 3] = "Warning"; + /** Log level for diagnostic messages that indicate a failure in the current operation. */ + LogLevel[LogLevel["Error"] = 4] = "Error"; + /** Log level for diagnostic messages that indicate a failure that will terminate the entire application. */ + LogLevel[LogLevel["Critical"] = 5] = "Critical"; + /** The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted. */ + LogLevel[LogLevel["None"] = 6] = "None"; +})(LogLevel || (LogLevel = {})); + + +/***/ }), +/* 10 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HubConnectionState", function() { return HubConnectionState; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HubConnection", function() { return HubConnection; }); +/* harmony import */ var _HandshakeProtocol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(11); +/* harmony import */ var _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(15); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16); +/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(13); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (undefined && undefined.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; + + + + + +var DEFAULT_TIMEOUT_IN_MS = 30 * 1000; +var DEFAULT_PING_INTERVAL_IN_MS = 15 * 1000; +/** Describes the current state of the {@link HubConnection} to the server. */ +var HubConnectionState; +(function (HubConnectionState) { + /** The hub connection is disconnected. */ + HubConnectionState["Disconnected"] = "Disconnected"; + /** The hub connection is connecting. */ + HubConnectionState["Connecting"] = "Connecting"; + /** The hub connection is connected. */ + HubConnectionState["Connected"] = "Connected"; + /** The hub connection is disconnecting. */ + HubConnectionState["Disconnecting"] = "Disconnecting"; + /** The hub connection is reconnecting. */ + HubConnectionState["Reconnecting"] = "Reconnecting"; +})(HubConnectionState || (HubConnectionState = {})); +/** Represents a connection to a SignalR Hub. */ +var HubConnection = /** @class */ (function () { + function HubConnection(connection, logger, protocol, reconnectPolicy) { + var _this = this; + _Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isRequired(connection, "connection"); + _Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isRequired(logger, "logger"); + _Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isRequired(protocol, "protocol"); + this.serverTimeoutInMilliseconds = DEFAULT_TIMEOUT_IN_MS; + this.keepAliveIntervalInMilliseconds = DEFAULT_PING_INTERVAL_IN_MS; + this.logger = logger; + this.protocol = protocol; + this.connection = connection; + this.reconnectPolicy = reconnectPolicy; + this.handshakeProtocol = new _HandshakeProtocol__WEBPACK_IMPORTED_MODULE_0__["HandshakeProtocol"](); + this.connection.onreceive = function (data) { return _this.processIncomingData(data); }; + this.connection.onclose = function (error) { return _this.connectionClosed(error); }; + this.callbacks = {}; + this.methods = {}; + this.closedCallbacks = []; + this.reconnectingCallbacks = []; + this.reconnectedCallbacks = []; + this.invocationId = 0; + this.receivedHandshakeResponse = false; + this.connectionState = HubConnectionState.Disconnected; + this.connectionStarted = false; + this.cachedPingMessage = this.protocol.writeMessage({ type: _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Ping }); + } + /** @internal */ + // Using a public static factory method means we can have a private constructor and an _internal_ + // create method that can be used by HubConnectionBuilder. An "internal" constructor would just + // be stripped away and the '.d.ts' file would have no constructor, which is interpreted as a + // public parameter-less constructor. + HubConnection.create = function (connection, logger, protocol, reconnectPolicy) { + return new HubConnection(connection, logger, protocol, reconnectPolicy); + }; + Object.defineProperty(HubConnection.prototype, "state", { + /** Indicates the state of the {@link HubConnection} to the server. */ + get: function () { + return this.connectionState; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(HubConnection.prototype, "connectionId", { + /** Represents the connection id of the {@link HubConnection} on the server. The connection id will be null when the connection is either + * in the disconnected state or if the negotiation step was skipped. + */ + get: function () { + return this.connection ? (this.connection.connectionId || null) : null; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(HubConnection.prototype, "baseUrl", { + /** Indicates the url of the {@link HubConnection} to the server. */ + get: function () { + return this.connection.baseUrl || ""; + }, + /** + * Sets a new url for the HubConnection. Note that the url can only be changed when the connection is in either the Disconnected or + * Reconnecting states. + * @param {string} url The url to connect to. + */ + set: function (url) { + if (this.connectionState !== HubConnectionState.Disconnected && this.connectionState !== HubConnectionState.Reconnecting) { + throw new Error("The HubConnection must be in the Disconnected or Reconnecting state to change the url."); + } + if (!url) { + throw new Error("The HubConnection url must be a valid url."); + } + this.connection.baseUrl = url; + }, + enumerable: true, + configurable: true + }); + /** Starts the connection. + * + * @returns {Promise} A Promise that resolves when the connection has been successfully established, or rejects with an error. + */ + HubConnection.prototype.start = function () { + this.startPromise = this.startWithStateTransitions(); + return this.startPromise; + }; + HubConnection.prototype.startWithStateTransitions = function () { + return __awaiter(this, void 0, void 0, function () { + var e_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.connectionState !== HubConnectionState.Disconnected) { + return [2 /*return*/, Promise.reject(new Error("Cannot start a HubConnection that is not in the 'Disconnected' state."))]; + } + this.connectionState = HubConnectionState.Connecting; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Starting HubConnection."); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.startInternal()]; + case 2: + _a.sent(); + this.connectionState = HubConnectionState.Connected; + this.connectionStarted = true; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "HubConnection connected successfully."); + return [3 /*break*/, 4]; + case 3: + e_1 = _a.sent(); + this.connectionState = HubConnectionState.Disconnected; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "HubConnection failed to start successfully because of error '" + e_1 + "'."); + return [2 /*return*/, Promise.reject(e_1)]; + case 4: return [2 /*return*/]; + } + }); + }); + }; + HubConnection.prototype.startInternal = function () { + return __awaiter(this, void 0, void 0, function () { + var handshakePromise, handshakeRequest, e_2; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + this.stopDuringStartError = undefined; + this.receivedHandshakeResponse = false; + handshakePromise = new Promise(function (resolve, reject) { + _this.handshakeResolver = resolve; + _this.handshakeRejecter = reject; + }); + return [4 /*yield*/, this.connection.start(this.protocol.transferFormat)]; + case 1: + _a.sent(); + _a.label = 2; + case 2: + _a.trys.push([2, 5, , 7]); + handshakeRequest = { + protocol: this.protocol.name, + version: this.protocol.version, + }; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Sending handshake request."); + return [4 /*yield*/, this.sendMessage(this.handshakeProtocol.writeHandshakeRequest(handshakeRequest))]; + case 3: + _a.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "Using HubProtocol '" + this.protocol.name + "'."); + // defensively cleanup timeout in case we receive a message from the server before we finish start + this.cleanupTimeout(); + this.resetTimeoutPeriod(); + this.resetKeepAliveInterval(); + return [4 /*yield*/, handshakePromise]; + case 4: + _a.sent(); + // It's important to check the stopDuringStartError instead of just relying on the handshakePromise + // being rejected on close, because this continuation can run after both the handshake completed successfully + // and the connection was closed. + if (this.stopDuringStartError) { + // It's important to throw instead of returning a rejected promise, because we don't want to allow any state + // transitions to occur between now and the calling code observing the exceptions. Returning a rejected promise + // will cause the calling continuation to get scheduled to run later. + throw this.stopDuringStartError; + } + return [3 /*break*/, 7]; + case 5: + e_2 = _a.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Hub handshake failed with error '" + e_2 + "' during start(). Stopping HubConnection."); + this.cleanupTimeout(); + this.cleanupPingTimer(); + // HttpConnection.stop() should not complete until after the onclose callback is invoked. + // This will transition the HubConnection to the disconnected state before HttpConnection.stop() completes. + return [4 /*yield*/, this.connection.stop(e_2)]; + case 6: + // HttpConnection.stop() should not complete until after the onclose callback is invoked. + // This will transition the HubConnection to the disconnected state before HttpConnection.stop() completes. + _a.sent(); + throw e_2; + case 7: return [2 /*return*/]; + } + }); + }); + }; + /** Stops the connection. + * + * @returns {Promise} A Promise that resolves when the connection has been successfully terminated, or rejects with an error. + */ + HubConnection.prototype.stop = function () { + return __awaiter(this, void 0, void 0, function () { + var startPromise, e_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + startPromise = this.startPromise; + this.stopPromise = this.stopInternal(); + return [4 /*yield*/, this.stopPromise]; + case 1: + _a.sent(); + _a.label = 2; + case 2: + _a.trys.push([2, 4, , 5]); + // Awaiting undefined continues immediately + return [4 /*yield*/, startPromise]; + case 3: + // Awaiting undefined continues immediately + _a.sent(); + return [3 /*break*/, 5]; + case 4: + e_3 = _a.sent(); + return [3 /*break*/, 5]; + case 5: return [2 /*return*/]; + } + }); + }); + }; + HubConnection.prototype.stopInternal = function (error) { + if (this.connectionState === HubConnectionState.Disconnected) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Call to HubConnection.stop(" + error + ") ignored because it is already in the disconnected state."); + return Promise.resolve(); + } + if (this.connectionState === HubConnectionState.Disconnecting) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Call to HttpConnection.stop(" + error + ") ignored because the connection is already in the disconnecting state."); + return this.stopPromise; + } + this.connectionState = HubConnectionState.Disconnecting; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Stopping HubConnection."); + if (this.reconnectDelayHandle) { + // We're in a reconnect delay which means the underlying connection is currently already stopped. + // Just clear the handle to stop the reconnect loop (which no one is waiting on thankfully) and + // fire the onclose callbacks. + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Connection stopped during reconnect delay. Done reconnecting."); + clearTimeout(this.reconnectDelayHandle); + this.reconnectDelayHandle = undefined; + this.completeClose(); + return Promise.resolve(); + } + this.cleanupTimeout(); + this.cleanupPingTimer(); + this.stopDuringStartError = error || new Error("The connection was stopped before the hub handshake could complete."); + // HttpConnection.stop() should not complete until after either HttpConnection.start() fails + // or the onclose callback is invoked. The onclose callback will transition the HubConnection + // to the disconnected state if need be before HttpConnection.stop() completes. + return this.connection.stop(error); + }; + /** Invokes a streaming hub method on the server using the specified name and arguments. + * + * @typeparam T The type of the items returned by the server. + * @param {string} methodName The name of the server method to invoke. + * @param {any[]} args The arguments used to invoke the server method. + * @returns {IStreamResult} An object that yields results from the server as they are received. + */ + HubConnection.prototype.stream = function (methodName) { + var _this = this; + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + var _a = this.replaceStreamingParams(args), streams = _a[0], streamIds = _a[1]; + var invocationDescriptor = this.createStreamInvocation(methodName, args, streamIds); + var promiseQueue; + var subject = new _Subject__WEBPACK_IMPORTED_MODULE_3__["Subject"](); + subject.cancelCallback = function () { + var cancelInvocation = _this.createCancelInvocation(invocationDescriptor.invocationId); + delete _this.callbacks[invocationDescriptor.invocationId]; + return promiseQueue.then(function () { + return _this.sendWithProtocol(cancelInvocation); + }); + }; + this.callbacks[invocationDescriptor.invocationId] = function (invocationEvent, error) { + if (error) { + subject.error(error); + return; + } + else if (invocationEvent) { + // invocationEvent will not be null when an error is not passed to the callback + if (invocationEvent.type === _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Completion) { + if (invocationEvent.error) { + subject.error(new Error(invocationEvent.error)); + } + else { + subject.complete(); + } + } + else { + subject.next((invocationEvent.item)); + } + } + }; + promiseQueue = this.sendWithProtocol(invocationDescriptor) + .catch(function (e) { + subject.error(e); + delete _this.callbacks[invocationDescriptor.invocationId]; + }); + this.launchStreams(streams, promiseQueue); + return subject; + }; + HubConnection.prototype.sendMessage = function (message) { + this.resetKeepAliveInterval(); + return this.connection.send(message); + }; + /** + * Sends a js object to the server. + * @param message The js object to serialize and send. + */ + HubConnection.prototype.sendWithProtocol = function (message) { + return this.sendMessage(this.protocol.writeMessage(message)); + }; + /** Invokes a hub method on the server using the specified name and arguments. Does not wait for a response from the receiver. + * + * The Promise returned by this method resolves when the client has sent the invocation to the server. The server may still + * be processing the invocation. + * + * @param {string} methodName The name of the server method to invoke. + * @param {any[]} args The arguments used to invoke the server method. + * @returns {Promise} A Promise that resolves when the invocation has been successfully sent, or rejects with an error. + */ + HubConnection.prototype.send = function (methodName) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + var _a = this.replaceStreamingParams(args), streams = _a[0], streamIds = _a[1]; + var sendPromise = this.sendWithProtocol(this.createInvocation(methodName, args, true, streamIds)); + this.launchStreams(streams, sendPromise); + return sendPromise; + }; + /** Invokes a hub method on the server using the specified name and arguments. + * + * The Promise returned by this method resolves when the server indicates it has finished invoking the method. When the promise + * resolves, the server has finished invoking the method. If the server method returns a result, it is produced as the result of + * resolving the Promise. + * + * @typeparam T The expected return type. + * @param {string} methodName The name of the server method to invoke. + * @param {any[]} args The arguments used to invoke the server method. + * @returns {Promise} A Promise that resolves with the result of the server method (if any), or rejects with an error. + */ + HubConnection.prototype.invoke = function (methodName) { + var _this = this; + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + var _a = this.replaceStreamingParams(args), streams = _a[0], streamIds = _a[1]; + var invocationDescriptor = this.createInvocation(methodName, args, false, streamIds); + var p = new Promise(function (resolve, reject) { + // invocationId will always have a value for a non-blocking invocation + _this.callbacks[invocationDescriptor.invocationId] = function (invocationEvent, error) { + if (error) { + reject(error); + return; + } + else if (invocationEvent) { + // invocationEvent will not be null when an error is not passed to the callback + if (invocationEvent.type === _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Completion) { + if (invocationEvent.error) { + reject(new Error(invocationEvent.error)); + } + else { + resolve(invocationEvent.result); + } + } + else { + reject(new Error("Unexpected message type: " + invocationEvent.type)); + } + } + }; + var promiseQueue = _this.sendWithProtocol(invocationDescriptor) + .catch(function (e) { + reject(e); + // invocationId will always have a value for a non-blocking invocation + delete _this.callbacks[invocationDescriptor.invocationId]; + }); + _this.launchStreams(streams, promiseQueue); + }); + return p; + }; + /** Registers a handler that will be invoked when the hub method with the specified method name is invoked. + * + * @param {string} methodName The name of the hub method to define. + * @param {Function} newMethod The handler that will be raised when the hub method is invoked. + */ + HubConnection.prototype.on = function (methodName, newMethod) { + if (!methodName || !newMethod) { + return; + } + methodName = methodName.toLowerCase(); + if (!this.methods[methodName]) { + this.methods[methodName] = []; + } + // Preventing adding the same handler multiple times. + if (this.methods[methodName].indexOf(newMethod) !== -1) { + return; + } + this.methods[methodName].push(newMethod); + }; + HubConnection.prototype.off = function (methodName, method) { + if (!methodName) { + return; + } + methodName = methodName.toLowerCase(); + var handlers = this.methods[methodName]; + if (!handlers) { + return; + } + if (method) { + var removeIdx = handlers.indexOf(method); + if (removeIdx !== -1) { + handlers.splice(removeIdx, 1); + if (handlers.length === 0) { + delete this.methods[methodName]; + } + } + } + else { + delete this.methods[methodName]; + } + }; + /** Registers a handler that will be invoked when the connection is closed. + * + * @param {Function} callback The handler that will be invoked when the connection is closed. Optionally receives a single argument containing the error that caused the connection to close (if any). + */ + HubConnection.prototype.onclose = function (callback) { + if (callback) { + this.closedCallbacks.push(callback); + } + }; + /** Registers a handler that will be invoked when the connection starts reconnecting. + * + * @param {Function} callback The handler that will be invoked when the connection starts reconnecting. Optionally receives a single argument containing the error that caused the connection to start reconnecting (if any). + */ + HubConnection.prototype.onreconnecting = function (callback) { + if (callback) { + this.reconnectingCallbacks.push(callback); + } + }; + /** Registers a handler that will be invoked when the connection successfully reconnects. + * + * @param {Function} callback The handler that will be invoked when the connection successfully reconnects. + */ + HubConnection.prototype.onreconnected = function (callback) { + if (callback) { + this.reconnectedCallbacks.push(callback); + } + }; + HubConnection.prototype.processIncomingData = function (data) { + this.cleanupTimeout(); + if (!this.receivedHandshakeResponse) { + data = this.processHandshakeResponse(data); + this.receivedHandshakeResponse = true; + } + // Data may have all been read when processing handshake response + if (data) { + // Parse the messages + var messages = this.protocol.parseMessages(data, this.logger); + for (var _i = 0, messages_1 = messages; _i < messages_1.length; _i++) { + var message = messages_1[_i]; + switch (message.type) { + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Invocation: + this.invokeClientMethod(message); + break; + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].StreamItem: + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Completion: + var callback = this.callbacks[message.invocationId]; + if (callback) { + if (message.type === _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Completion) { + delete this.callbacks[message.invocationId]; + } + callback(message); + } + break; + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Ping: + // Don't care about pings + break; + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Close: + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "Close message received from server."); + var error = message.error ? new Error("Server returned an error on close: " + message.error) : undefined; + if (message.allowReconnect === true) { + // It feels wrong not to await connection.stop() here, but processIncomingData is called as part of an onreceive callback which is not async, + // this is already the behavior for serverTimeout(), and HttpConnection.Stop() should catch and log all possible exceptions. + // tslint:disable-next-line:no-floating-promises + this.connection.stop(error); + } + else { + // We cannot await stopInternal() here, but subsequent calls to stop() will await this if stopInternal() is still ongoing. + this.stopPromise = this.stopInternal(error); + } + break; + default: + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Warning, "Invalid message type: " + message.type + "."); + break; + } + } + } + this.resetTimeoutPeriod(); + }; + HubConnection.prototype.processHandshakeResponse = function (data) { + var _a; + var responseMessage; + var remainingData; + try { + _a = this.handshakeProtocol.parseHandshakeResponse(data), remainingData = _a[0], responseMessage = _a[1]; + } + catch (e) { + var message = "Error parsing handshake response: " + e; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, message); + var error = new Error(message); + this.handshakeRejecter(error); + throw error; + } + if (responseMessage.error) { + var message = "Server returned handshake error: " + responseMessage.error; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, message); + var error = new Error(message); + this.handshakeRejecter(error); + throw error; + } + else { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Server handshake complete."); + } + this.handshakeResolver(); + return remainingData; + }; + HubConnection.prototype.resetKeepAliveInterval = function () { + var _this = this; + this.cleanupPingTimer(); + this.pingServerHandle = setTimeout(function () { return __awaiter(_this, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!(this.connectionState === HubConnectionState.Connected)) return [3 /*break*/, 4]; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.sendMessage(this.cachedPingMessage)]; + case 2: + _b.sent(); + return [3 /*break*/, 4]; + case 3: + _a = _b.sent(); + // We don't care about the error. It should be seen elsewhere in the client. + // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering + this.cleanupPingTimer(); + return [3 /*break*/, 4]; + case 4: return [2 /*return*/]; + } + }); + }); }, this.keepAliveIntervalInMilliseconds); + }; + HubConnection.prototype.resetTimeoutPeriod = function () { + var _this = this; + if (!this.connection.features || !this.connection.features.inherentKeepAlive) { + // Set the timeout timer + this.timeoutHandle = setTimeout(function () { return _this.serverTimeout(); }, this.serverTimeoutInMilliseconds); + } + }; + HubConnection.prototype.serverTimeout = function () { + // The server hasn't talked to us in a while. It doesn't like us anymore ... :( + // Terminate the connection, but we don't need to wait on the promise. This could trigger reconnecting. + // tslint:disable-next-line:no-floating-promises + this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server.")); + }; + HubConnection.prototype.invokeClientMethod = function (invocationMessage) { + var _this = this; + var methods = this.methods[invocationMessage.target.toLowerCase()]; + if (methods) { + try { + methods.forEach(function (m) { return m.apply(_this, invocationMessage.arguments); }); + } + catch (e) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, "A callback for the method " + invocationMessage.target.toLowerCase() + " threw error '" + e + "'."); + } + if (invocationMessage.invocationId) { + // This is not supported in v1. So we return an error to avoid blocking the server waiting for the response. + var message = "Server requested a response, which is not supported in this version of the client."; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, message); + // We don't want to wait on the stop itself. + this.stopPromise = this.stopInternal(new Error(message)); + } + } + else { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Warning, "No client method with the name '" + invocationMessage.target + "' found."); + } + }; + HubConnection.prototype.connectionClosed = function (error) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "HubConnection.connectionClosed(" + error + ") called while in state " + this.connectionState + "."); + // Triggering this.handshakeRejecter is insufficient because it could already be resolved without the continuation having run yet. + this.stopDuringStartError = this.stopDuringStartError || error || new Error("The underlying connection was closed before the hub handshake could complete."); + // If the handshake is in progress, start will be waiting for the handshake promise, so we complete it. + // If it has already completed, this should just noop. + if (this.handshakeResolver) { + this.handshakeResolver(); + } + this.cancelCallbacksWithError(error || new Error("Invocation canceled due to the underlying connection being closed.")); + this.cleanupTimeout(); + this.cleanupPingTimer(); + if (this.connectionState === HubConnectionState.Disconnecting) { + this.completeClose(error); + } + else if (this.connectionState === HubConnectionState.Connected && this.reconnectPolicy) { + // tslint:disable-next-line:no-floating-promises + this.reconnect(error); + } + else if (this.connectionState === HubConnectionState.Connected) { + this.completeClose(error); + } + // If none of the above if conditions were true were called the HubConnection must be in either: + // 1. The Connecting state in which case the handshakeResolver will complete it and stopDuringStartError will fail it. + // 2. The Reconnecting state in which case the handshakeResolver will complete it and stopDuringStartError will fail the current reconnect attempt + // and potentially continue the reconnect() loop. + // 3. The Disconnected state in which case we're already done. + }; + HubConnection.prototype.completeClose = function (error) { + var _this = this; + if (this.connectionStarted) { + this.connectionState = HubConnectionState.Disconnected; + this.connectionStarted = false; + try { + this.closedCallbacks.forEach(function (c) { return c.apply(_this, [error]); }); + } + catch (e) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, "An onclose callback called with error '" + error + "' threw error '" + e + "'."); + } + } + }; + HubConnection.prototype.reconnect = function (error) { + return __awaiter(this, void 0, void 0, function () { + var reconnectStartTime, previousReconnectAttempts, retryError, nextRetryDelay, e_4; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + reconnectStartTime = Date.now(); + previousReconnectAttempts = 0; + retryError = error !== undefined ? error : new Error("Attempting to reconnect due to a unknown error."); + nextRetryDelay = this.getNextRetryDelay(previousReconnectAttempts++, 0, retryError); + if (nextRetryDelay === null) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Connection not reconnecting because the IRetryPolicy returned null on the first reconnect attempt."); + this.completeClose(error); + return [2 /*return*/]; + } + this.connectionState = HubConnectionState.Reconnecting; + if (error) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "Connection reconnecting because of error '" + error + "'."); + } + else { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "Connection reconnecting."); + } + if (this.onreconnecting) { + try { + this.reconnectingCallbacks.forEach(function (c) { return c.apply(_this, [error]); }); + } + catch (e) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, "An onreconnecting callback called with error '" + error + "' threw error '" + e + "'."); + } + // Exit early if an onreconnecting callback called connection.stop(). + if (this.connectionState !== HubConnectionState.Reconnecting) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Connection left the reconnecting state in onreconnecting callback. Done reconnecting."); + return [2 /*return*/]; + } + } + _a.label = 1; + case 1: + if (!(nextRetryDelay !== null)) return [3 /*break*/, 7]; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "Reconnect attempt number " + previousReconnectAttempts + " will start in " + nextRetryDelay + " ms."); + return [4 /*yield*/, new Promise(function (resolve) { + _this.reconnectDelayHandle = setTimeout(resolve, nextRetryDelay); + })]; + case 2: + _a.sent(); + this.reconnectDelayHandle = undefined; + if (this.connectionState !== HubConnectionState.Reconnecting) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Connection left the reconnecting state during reconnect delay. Done reconnecting."); + return [2 /*return*/]; + } + _a.label = 3; + case 3: + _a.trys.push([3, 5, , 6]); + return [4 /*yield*/, this.startInternal()]; + case 4: + _a.sent(); + this.connectionState = HubConnectionState.Connected; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "HubConnection reconnected successfully."); + if (this.onreconnected) { + try { + this.reconnectedCallbacks.forEach(function (c) { return c.apply(_this, [_this.connection.connectionId]); }); + } + catch (e) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, "An onreconnected callback called with connectionId '" + this.connection.connectionId + "; threw error '" + e + "'."); + } + } + return [2 /*return*/]; + case 5: + e_4 = _a.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "Reconnect attempt failed because of error '" + e_4 + "'."); + if (this.connectionState !== HubConnectionState.Reconnecting) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "Connection left the reconnecting state during reconnect attempt. Done reconnecting."); + return [2 /*return*/]; + } + retryError = e_4 instanceof Error ? e_4 : new Error(e_4.toString()); + nextRetryDelay = this.getNextRetryDelay(previousReconnectAttempts++, Date.now() - reconnectStartTime, retryError); + return [3 /*break*/, 6]; + case 6: return [3 /*break*/, 1]; + case 7: + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "Reconnect retries have been exhausted after " + (Date.now() - reconnectStartTime) + " ms and " + previousReconnectAttempts + " failed attempts. Connection disconnecting."); + this.completeClose(); + return [2 /*return*/]; + } + }); + }); + }; + HubConnection.prototype.getNextRetryDelay = function (previousRetryCount, elapsedMilliseconds, retryReason) { + try { + return this.reconnectPolicy.nextRetryDelayInMilliseconds({ + elapsedMilliseconds: elapsedMilliseconds, + previousRetryCount: previousRetryCount, + retryReason: retryReason, + }); + } + catch (e) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, "IRetryPolicy.nextRetryDelayInMilliseconds(" + previousRetryCount + ", " + elapsedMilliseconds + ") threw error '" + e + "'."); + return null; + } + }; + HubConnection.prototype.cancelCallbacksWithError = function (error) { + var callbacks = this.callbacks; + this.callbacks = {}; + Object.keys(callbacks) + .forEach(function (key) { + var callback = callbacks[key]; + callback(null, error); + }); + }; + HubConnection.prototype.cleanupPingTimer = function () { + if (this.pingServerHandle) { + clearTimeout(this.pingServerHandle); + } + }; + HubConnection.prototype.cleanupTimeout = function () { + if (this.timeoutHandle) { + clearTimeout(this.timeoutHandle); + } + }; + HubConnection.prototype.createInvocation = function (methodName, args, nonblocking, streamIds) { + if (nonblocking) { + return { + arguments: args, + streamIds: streamIds, + target: methodName, + type: _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Invocation, + }; + } + else { + var invocationId = this.invocationId; + this.invocationId++; + return { + arguments: args, + invocationId: invocationId.toString(), + streamIds: streamIds, + target: methodName, + type: _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Invocation, + }; + } + }; + HubConnection.prototype.launchStreams = function (streams, promiseQueue) { + var _this = this; + if (streams.length === 0) { + return; + } + // Synchronize stream data so they arrive in-order on the server + if (!promiseQueue) { + promiseQueue = Promise.resolve(); + } + var _loop_1 = function (streamId) { + streams[streamId].subscribe({ + complete: function () { + promiseQueue = promiseQueue.then(function () { return _this.sendWithProtocol(_this.createCompletionMessage(streamId)); }); + }, + error: function (err) { + var message; + if (err instanceof Error) { + message = err.message; + } + else if (err && err.toString) { + message = err.toString(); + } + else { + message = "Unknown error"; + } + promiseQueue = promiseQueue.then(function () { return _this.sendWithProtocol(_this.createCompletionMessage(streamId, message)); }); + }, + next: function (item) { + promiseQueue = promiseQueue.then(function () { return _this.sendWithProtocol(_this.createStreamItemMessage(streamId, item)); }); + }, + }); + }; + // We want to iterate over the keys, since the keys are the stream ids + // tslint:disable-next-line:forin + for (var streamId in streams) { + _loop_1(streamId); + } + }; + HubConnection.prototype.replaceStreamingParams = function (args) { + var streams = []; + var streamIds = []; + for (var i = 0; i < args.length; i++) { + var argument = args[i]; + if (this.isObservable(argument)) { + var streamId = this.invocationId; + this.invocationId++; + // Store the stream for later use + streams[streamId] = argument; + streamIds.push(streamId.toString()); + // remove stream from args + args.splice(i, 1); + } + } + return [streams, streamIds]; + }; + HubConnection.prototype.isObservable = function (arg) { + // This allows other stream implementations to just work (like rxjs) + return arg && arg.subscribe && typeof arg.subscribe === "function"; + }; + HubConnection.prototype.createStreamInvocation = function (methodName, args, streamIds) { + var invocationId = this.invocationId; + this.invocationId++; + return { + arguments: args, + invocationId: invocationId.toString(), + streamIds: streamIds, + target: methodName, + type: _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].StreamInvocation, + }; + }; + HubConnection.prototype.createCancelInvocation = function (id) { + return { + invocationId: id, + type: _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].CancelInvocation, + }; + }; + HubConnection.prototype.createStreamItemMessage = function (id, item) { + return { + invocationId: id, + item: item, + type: _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].StreamItem, + }; + }; + HubConnection.prototype.createCompletionMessage = function (id, error, result) { + if (error) { + return { + error: error, + invocationId: id, + type: _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Completion, + }; + } + return { + invocationId: id, + result: result, + type: _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__["MessageType"].Completion, + }; + }; + return HubConnection; +}()); + + + +/***/ }), +/* 11 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HandshakeProtocol", function() { return HandshakeProtocol; }); +/* harmony import */ var _TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + +/** @private */ +var HandshakeProtocol = /** @class */ (function () { + function HandshakeProtocol() { + } + // Handshake request is always JSON + HandshakeProtocol.prototype.writeHandshakeRequest = function (handshakeRequest) { + return _TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__["TextMessageFormat"].write(JSON.stringify(handshakeRequest)); + }; + HandshakeProtocol.prototype.parseHandshakeResponse = function (data) { + var responseMessage; + var messageData; + var remainingData; + if (Object(_Utils__WEBPACK_IMPORTED_MODULE_1__["isArrayBuffer"])(data) || (typeof Buffer !== "undefined" && data instanceof Buffer)) { + // Format is binary but still need to read JSON text from handshake response + var binaryData = new Uint8Array(data); + var separatorIndex = binaryData.indexOf(_TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__["TextMessageFormat"].RecordSeparatorCode); + if (separatorIndex === -1) { + throw new Error("Message is incomplete."); + } + // content before separator is handshake response + // optional content after is additional messages + var responseLength = separatorIndex + 1; + messageData = String.fromCharCode.apply(null, binaryData.slice(0, responseLength)); + remainingData = (binaryData.byteLength > responseLength) ? binaryData.slice(responseLength).buffer : null; + } + else { + var textData = data; + var separatorIndex = textData.indexOf(_TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__["TextMessageFormat"].RecordSeparator); + if (separatorIndex === -1) { + throw new Error("Message is incomplete."); + } + // content before separator is handshake response + // optional content after is additional messages + var responseLength = separatorIndex + 1; + messageData = textData.substring(0, responseLength); + remainingData = (textData.length > responseLength) ? textData.substring(responseLength) : null; + } + // At this point we should have just the single handshake message + var messages = _TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__["TextMessageFormat"].parse(messageData); + var response = JSON.parse(messages[0]); + if (response.type) { + throw new Error("Expected a handshake response from the server."); + } + responseMessage = response; + // multiple messages could have arrived with handshake + // return additional data to be parsed as usual, or null if all parsed + return [remainingData, responseMessage]; + }; + return HandshakeProtocol; +}()); + + + +/***/ }), +/* 12 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextMessageFormat", function() { return TextMessageFormat; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Not exported from index +/** @private */ +var TextMessageFormat = /** @class */ (function () { + function TextMessageFormat() { + } + TextMessageFormat.write = function (output) { + return "" + output + TextMessageFormat.RecordSeparator; + }; + TextMessageFormat.parse = function (input) { + if (input[input.length - 1] !== TextMessageFormat.RecordSeparator) { + throw new Error("Message is incomplete."); + } + var messages = input.split(TextMessageFormat.RecordSeparator); + messages.pop(); + return messages; + }; + TextMessageFormat.RecordSeparatorCode = 0x1e; + TextMessageFormat.RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode); + return TextMessageFormat; +}()); + + + +/***/ }), +/* 13 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Arg", function() { return Arg; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Platform", function() { return Platform; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getDataDetail", function() { return getDataDetail; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "formatArrayBuffer", function() { return formatArrayBuffer; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isArrayBuffer", function() { return isArrayBuffer; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sendMessage", function() { return sendMessage; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createLogger", function() { return createLogger; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SubjectSubscription", function() { return SubjectSubscription; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ConsoleLogger", function() { return ConsoleLogger; }); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9); +/* harmony import */ var _Loggers__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (undefined && undefined.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; + + +/** @private */ +var Arg = /** @class */ (function () { + function Arg() { + } + Arg.isRequired = function (val, name) { + if (val === null || val === undefined) { + throw new Error("The '" + name + "' argument is required."); + } + }; + Arg.isIn = function (val, values, name) { + // TypeScript enums have keys for **both** the name and the value of each enum member on the type itself. + if (!(val in values)) { + throw new Error("Unknown " + name + " value: " + val + "."); + } + }; + return Arg; +}()); + +/** @private */ +var Platform = /** @class */ (function () { + function Platform() { + } + Object.defineProperty(Platform, "isBrowser", { + get: function () { + return typeof window === "object"; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Platform, "isWebWorker", { + get: function () { + return typeof self === "object" && "importScripts" in self; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Platform, "isNode", { + get: function () { + return !this.isBrowser && !this.isWebWorker; + }, + enumerable: true, + configurable: true + }); + return Platform; +}()); + +/** @private */ +function getDataDetail(data, includeContent) { + var detail = ""; + if (isArrayBuffer(data)) { + detail = "Binary data of length " + data.byteLength; + if (includeContent) { + detail += ". Content: '" + formatArrayBuffer(data) + "'"; + } + } + else if (typeof data === "string") { + detail = "String data of length " + data.length; + if (includeContent) { + detail += ". Content: '" + data + "'"; + } + } + return detail; +} +/** @private */ +function formatArrayBuffer(data) { + var view = new Uint8Array(data); + // Uint8Array.map only supports returning another Uint8Array? + var str = ""; + view.forEach(function (num) { + var pad = num < 16 ? "0" : ""; + str += "0x" + pad + num.toString(16) + " "; + }); + // Trim of trailing space. + return str.substr(0, str.length - 1); +} +// Also in signalr-protocol-msgpack/Utils.ts +/** @private */ +function isArrayBuffer(val) { + return val && typeof ArrayBuffer !== "undefined" && + (val instanceof ArrayBuffer || + // Sometimes we get an ArrayBuffer that doesn't satisfy instanceof + (val.constructor && val.constructor.name === "ArrayBuffer")); +} +/** @private */ +function sendMessage(logger, transportName, httpClient, url, accessTokenFactory, content, logMessageContent) { + return __awaiter(this, void 0, void 0, function () { + var _a, headers, token, responseType, response; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!accessTokenFactory) return [3 /*break*/, 2]; + return [4 /*yield*/, accessTokenFactory()]; + case 1: + token = _b.sent(); + if (token) { + headers = (_a = {}, + _a["Authorization"] = "Bearer " + token, + _a); + } + _b.label = 2; + case 2: + logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Trace, "(" + transportName + " transport) sending data. " + getDataDetail(content, logMessageContent) + "."); + responseType = isArrayBuffer(content) ? "arraybuffer" : "text"; + return [4 /*yield*/, httpClient.post(url, { + content: content, + headers: headers, + responseType: responseType, + })]; + case 3: + response = _b.sent(); + logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Trace, "(" + transportName + " transport) request complete. Response status: " + response.statusCode + "."); + return [2 /*return*/]; + } + }); + }); +} +/** @private */ +function createLogger(logger) { + if (logger === undefined) { + return new ConsoleLogger(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Information); + } + if (logger === null) { + return _Loggers__WEBPACK_IMPORTED_MODULE_1__["NullLogger"].instance; + } + if (logger.log) { + return logger; + } + return new ConsoleLogger(logger); +} +/** @private */ +var SubjectSubscription = /** @class */ (function () { + function SubjectSubscription(subject, observer) { + this.subject = subject; + this.observer = observer; + } + SubjectSubscription.prototype.dispose = function () { + var index = this.subject.observers.indexOf(this.observer); + if (index > -1) { + this.subject.observers.splice(index, 1); + } + if (this.subject.observers.length === 0 && this.subject.cancelCallback) { + this.subject.cancelCallback().catch(function (_) { }); + } + }; + return SubjectSubscription; +}()); + +/** @private */ +var ConsoleLogger = /** @class */ (function () { + function ConsoleLogger(minimumLogLevel) { + this.minimumLogLevel = minimumLogLevel; + this.outputConsole = console; + } + ConsoleLogger.prototype.log = function (logLevel, message) { + if (logLevel >= this.minimumLogLevel) { + switch (logLevel) { + case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Critical: + case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Error: + this.outputConsole.error("[" + new Date().toISOString() + "] " + _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel] + ": " + message); + break; + case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Warning: + this.outputConsole.warn("[" + new Date().toISOString() + "] " + _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel] + ": " + message); + break; + case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Information: + this.outputConsole.info("[" + new Date().toISOString() + "] " + _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel] + ": " + message); + break; + default: + // console.debug only goes to attached debuggers in Node, so we use console.log for Trace and Debug + this.outputConsole.log("[" + new Date().toISOString() + "] " + _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel] + ": " + message); + break; + } + } + }; + return ConsoleLogger; +}()); + + + +/***/ }), +/* 14 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NullLogger", function() { return NullLogger; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +/** A logger that does nothing when log messages are sent to it. */ +var NullLogger = /** @class */ (function () { + function NullLogger() { + } + /** @inheritDoc */ + // tslint:disable-next-line + NullLogger.prototype.log = function (_logLevel, _message) { + }; + /** The singleton instance of the {@link @microsoft/signalr.NullLogger}. */ + NullLogger.instance = new NullLogger(); + return NullLogger; +}()); + + + +/***/ }), +/* 15 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MessageType", function() { return MessageType; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +/** Defines the type of a Hub Message. */ +var MessageType; +(function (MessageType) { + /** Indicates the message is an Invocation message and implements the {@link @microsoft/signalr.InvocationMessage} interface. */ + MessageType[MessageType["Invocation"] = 1] = "Invocation"; + /** Indicates the message is a StreamItem message and implements the {@link @microsoft/signalr.StreamItemMessage} interface. */ + MessageType[MessageType["StreamItem"] = 2] = "StreamItem"; + /** Indicates the message is a Completion message and implements the {@link @microsoft/signalr.CompletionMessage} interface. */ + MessageType[MessageType["Completion"] = 3] = "Completion"; + /** Indicates the message is a Stream Invocation message and implements the {@link @microsoft/signalr.StreamInvocationMessage} interface. */ + MessageType[MessageType["StreamInvocation"] = 4] = "StreamInvocation"; + /** Indicates the message is a Cancel Invocation message and implements the {@link @microsoft/signalr.CancelInvocationMessage} interface. */ + MessageType[MessageType["CancelInvocation"] = 5] = "CancelInvocation"; + /** Indicates the message is a Ping message and implements the {@link @microsoft/signalr.PingMessage} interface. */ + MessageType[MessageType["Ping"] = 6] = "Ping"; + /** Indicates the message is a Close message and implements the {@link @microsoft/signalr.CloseMessage} interface. */ + MessageType[MessageType["Close"] = 7] = "Close"; +})(MessageType || (MessageType = {})); + + +/***/ }), +/* 16 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Subject", function() { return Subject; }); +/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +/** Stream implementation to stream items to the server. */ +var Subject = /** @class */ (function () { + function Subject() { + this.observers = []; + } + Subject.prototype.next = function (item) { + for (var _i = 0, _a = this.observers; _i < _a.length; _i++) { + var observer = _a[_i]; + observer.next(item); + } + }; + Subject.prototype.error = function (err) { + for (var _i = 0, _a = this.observers; _i < _a.length; _i++) { + var observer = _a[_i]; + if (observer.error) { + observer.error(err); + } + } + }; + Subject.prototype.complete = function () { + for (var _i = 0, _a = this.observers; _i < _a.length; _i++) { + var observer = _a[_i]; + if (observer.complete) { + observer.complete(); + } + } + }; + Subject.prototype.subscribe = function (observer) { + this.observers.push(observer); + return new _Utils__WEBPACK_IMPORTED_MODULE_0__["SubjectSubscription"](this, observer); + }; + return Subject; +}()); + + + +/***/ }), +/* 17 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HubConnectionBuilder", function() { return HubConnectionBuilder; }); +/* harmony import */ var _DefaultReconnectPolicy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(18); +/* harmony import */ var _HttpConnection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(19); +/* harmony import */ var _HubConnection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9); +/* harmony import */ var _JsonHubProtocol__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(25); +/* harmony import */ var _Loggers__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(14); +/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(13); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __assign = (undefined && undefined.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; + + + + + + + +// tslint:disable:object-literal-sort-keys +var LogLevelNameMapping = { + trace: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].Trace, + debug: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].Debug, + info: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].Information, + information: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].Information, + warn: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].Warning, + warning: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].Warning, + error: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].Error, + critical: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].Critical, + none: _ILogger__WEBPACK_IMPORTED_MODULE_3__["LogLevel"].None, +}; +function parseLogLevel(name) { + // Case-insensitive matching via lower-casing + // Yes, I know case-folding is a complicated problem in Unicode, but we only support + // the ASCII strings defined in LogLevelNameMapping anyway, so it's fine -anurse. + var mapping = LogLevelNameMapping[name.toLowerCase()]; + if (typeof mapping !== "undefined") { + return mapping; + } + else { + throw new Error("Unknown log level: " + name); + } +} +/** A builder for configuring {@link @microsoft/signalr.HubConnection} instances. */ +var HubConnectionBuilder = /** @class */ (function () { + function HubConnectionBuilder() { + } + HubConnectionBuilder.prototype.configureLogging = function (logging) { + _Utils__WEBPACK_IMPORTED_MODULE_6__["Arg"].isRequired(logging, "logging"); + if (isLogger(logging)) { + this.logger = logging; + } + else if (typeof logging === "string") { + var logLevel = parseLogLevel(logging); + this.logger = new _Utils__WEBPACK_IMPORTED_MODULE_6__["ConsoleLogger"](logLevel); + } + else { + this.logger = new _Utils__WEBPACK_IMPORTED_MODULE_6__["ConsoleLogger"](logging); + } + return this; + }; + HubConnectionBuilder.prototype.withUrl = function (url, transportTypeOrOptions) { + _Utils__WEBPACK_IMPORTED_MODULE_6__["Arg"].isRequired(url, "url"); + this.url = url; + // Flow-typing knows where it's at. Since HttpTransportType is a number and IHttpConnectionOptions is guaranteed + // to be an object, we know (as does TypeScript) this comparison is all we need to figure out which overload was called. + if (typeof transportTypeOrOptions === "object") { + this.httpConnectionOptions = __assign({}, this.httpConnectionOptions, transportTypeOrOptions); + } + else { + this.httpConnectionOptions = __assign({}, this.httpConnectionOptions, { transport: transportTypeOrOptions }); + } + return this; + }; + /** Configures the {@link @microsoft/signalr.HubConnection} to use the specified Hub Protocol. + * + * @param {IHubProtocol} protocol The {@link @microsoft/signalr.IHubProtocol} implementation to use. + */ + HubConnectionBuilder.prototype.withHubProtocol = function (protocol) { + _Utils__WEBPACK_IMPORTED_MODULE_6__["Arg"].isRequired(protocol, "protocol"); + this.protocol = protocol; + return this; + }; + HubConnectionBuilder.prototype.withAutomaticReconnect = function (retryDelaysOrReconnectPolicy) { + if (this.reconnectPolicy) { + throw new Error("A reconnectPolicy has already been set."); + } + if (!retryDelaysOrReconnectPolicy) { + this.reconnectPolicy = new _DefaultReconnectPolicy__WEBPACK_IMPORTED_MODULE_0__["DefaultReconnectPolicy"](); + } + else if (Array.isArray(retryDelaysOrReconnectPolicy)) { + this.reconnectPolicy = new _DefaultReconnectPolicy__WEBPACK_IMPORTED_MODULE_0__["DefaultReconnectPolicy"](retryDelaysOrReconnectPolicy); + } + else { + this.reconnectPolicy = retryDelaysOrReconnectPolicy; + } + return this; + }; + /** Creates a {@link @microsoft/signalr.HubConnection} from the configuration options specified in this builder. + * + * @returns {HubConnection} The configured {@link @microsoft/signalr.HubConnection}. + */ + HubConnectionBuilder.prototype.build = function () { + // If httpConnectionOptions has a logger, use it. Otherwise, override it with the one + // provided to configureLogger + var httpConnectionOptions = this.httpConnectionOptions || {}; + // If it's 'null', the user **explicitly** asked for null, don't mess with it. + if (httpConnectionOptions.logger === undefined) { + // If our logger is undefined or null, that's OK, the HttpConnection constructor will handle it. + httpConnectionOptions.logger = this.logger; + } + // Now create the connection + if (!this.url) { + throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection."); + } + var connection = new _HttpConnection__WEBPACK_IMPORTED_MODULE_1__["HttpConnection"](this.url, httpConnectionOptions); + return _HubConnection__WEBPACK_IMPORTED_MODULE_2__["HubConnection"].create(connection, this.logger || _Loggers__WEBPACK_IMPORTED_MODULE_5__["NullLogger"].instance, this.protocol || new _JsonHubProtocol__WEBPACK_IMPORTED_MODULE_4__["JsonHubProtocol"](), this.reconnectPolicy); + }; + return HubConnectionBuilder; +}()); + +function isLogger(logger) { + return logger.log !== undefined; +} + + +/***/ }), +/* 18 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DefaultReconnectPolicy", function() { return DefaultReconnectPolicy; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// 0, 2, 10, 30 second delays before reconnect attempts. +var DEFAULT_RETRY_DELAYS_IN_MILLISECONDS = [0, 2000, 10000, 30000, null]; +/** @private */ +var DefaultReconnectPolicy = /** @class */ (function () { + function DefaultReconnectPolicy(retryDelays) { + this.retryDelays = retryDelays !== undefined ? retryDelays.concat([null]) : DEFAULT_RETRY_DELAYS_IN_MILLISECONDS; + } + DefaultReconnectPolicy.prototype.nextRetryDelayInMilliseconds = function (retryContext) { + return this.retryDelays[retryContext.previousRetryCount]; + }; + return DefaultReconnectPolicy; +}()); + + + +/***/ }), +/* 19 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HttpConnection", function() { return HttpConnection; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TransportSendQueue", function() { return TransportSendQueue; }); +/* harmony import */ var _DefaultHttpClient__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); +/* harmony import */ var _ITransport__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(20); +/* harmony import */ var _LongPollingTransport__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(21); +/* harmony import */ var _ServerSentEventsTransport__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(23); +/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(13); +/* harmony import */ var _WebSocketTransport__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(24); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (undefined && undefined.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; + + + + + + + +var MAX_REDIRECTS = 100; +var WebSocketModule = null; +var EventSourceModule = null; +if (_Utils__WEBPACK_IMPORTED_MODULE_5__["Platform"].isNode && "function" !== "undefined") { + // In order to ignore the dynamic require in webpack builds we need to do this magic + // @ts-ignore: TS doesn't know about these names + var requireFunc = true ? require : undefined; + WebSocketModule = requireFunc("ws"); + EventSourceModule = requireFunc("eventsource"); +} +/** @private */ +var HttpConnection = /** @class */ (function () { + function HttpConnection(url, options) { + if (options === void 0) { options = {}; } + this.features = {}; + this.negotiateVersion = 1; + _Utils__WEBPACK_IMPORTED_MODULE_5__["Arg"].isRequired(url, "url"); + this.logger = Object(_Utils__WEBPACK_IMPORTED_MODULE_5__["createLogger"])(options.logger); + this.baseUrl = this.resolveUrl(url); + options = options || {}; + options.logMessageContent = options.logMessageContent || false; + if (!_Utils__WEBPACK_IMPORTED_MODULE_5__["Platform"].isNode && typeof WebSocket !== "undefined" && !options.WebSocket) { + options.WebSocket = WebSocket; + } + else if (_Utils__WEBPACK_IMPORTED_MODULE_5__["Platform"].isNode && !options.WebSocket) { + if (WebSocketModule) { + options.WebSocket = WebSocketModule; + } + } + if (!_Utils__WEBPACK_IMPORTED_MODULE_5__["Platform"].isNode && typeof EventSource !== "undefined" && !options.EventSource) { + options.EventSource = EventSource; + } + else if (_Utils__WEBPACK_IMPORTED_MODULE_5__["Platform"].isNode && !options.EventSource) { + if (typeof EventSourceModule !== "undefined") { + options.EventSource = EventSourceModule; + } + } + this.httpClient = options.httpClient || new _DefaultHttpClient__WEBPACK_IMPORTED_MODULE_0__["DefaultHttpClient"](this.logger); + this.connectionState = "Disconnected" /* Disconnected */; + this.connectionStarted = false; + this.options = options; + this.onreceive = null; + this.onclose = null; + } + HttpConnection.prototype.start = function (transferFormat) { + return __awaiter(this, void 0, void 0, function () { + var message, message; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + transferFormat = transferFormat || _ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"].Binary; + _Utils__WEBPACK_IMPORTED_MODULE_5__["Arg"].isIn(transferFormat, _ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"], "transferFormat"); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Starting connection with transfer format '" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"][transferFormat] + "'."); + if (this.connectionState !== "Disconnected" /* Disconnected */) { + return [2 /*return*/, Promise.reject(new Error("Cannot start an HttpConnection that is not in the 'Disconnected' state."))]; + } + this.connectionState = "Connecting " /* Connecting */; + this.startInternalPromise = this.startInternal(transferFormat); + return [4 /*yield*/, this.startInternalPromise]; + case 1: + _a.sent(); + if (!(this.connectionState === "Disconnecting" /* Disconnecting */)) return [3 /*break*/, 3]; + message = "Failed to start the HttpConnection before stop() was called."; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, message); + // We cannot await stopPromise inside startInternal since stopInternal awaits the startInternalPromise. + return [4 /*yield*/, this.stopPromise]; + case 2: + // We cannot await stopPromise inside startInternal since stopInternal awaits the startInternalPromise. + _a.sent(); + return [2 /*return*/, Promise.reject(new Error(message))]; + case 3: + if (this.connectionState !== "Connected" /* Connected */) { + message = "HttpConnection.startInternal completed gracefully but didn't enter the connection into the connected state!"; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, message); + return [2 /*return*/, Promise.reject(new Error(message))]; + } + _a.label = 4; + case 4: + this.connectionStarted = true; + return [2 /*return*/]; + } + }); + }); + }; + HttpConnection.prototype.send = function (data) { + if (this.connectionState !== "Connected" /* Connected */) { + return Promise.reject(new Error("Cannot send data if the connection is not in the 'Connected' State.")); + } + if (!this.sendQueue) { + this.sendQueue = new TransportSendQueue(this.transport); + } + // Transport will not be null if state is connected + return this.sendQueue.send(data); + }; + HttpConnection.prototype.stop = function (error) { + return __awaiter(this, void 0, void 0, function () { + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (this.connectionState === "Disconnected" /* Disconnected */) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Call to HttpConnection.stop(" + error + ") ignored because the connection is already in the disconnected state."); + return [2 /*return*/, Promise.resolve()]; + } + if (this.connectionState === "Disconnecting" /* Disconnecting */) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Call to HttpConnection.stop(" + error + ") ignored because the connection is already in the disconnecting state."); + return [2 /*return*/, this.stopPromise]; + } + this.connectionState = "Disconnecting" /* Disconnecting */; + this.stopPromise = new Promise(function (resolve) { + // Don't complete stop() until stopConnection() completes. + _this.stopPromiseResolver = resolve; + }); + // stopInternal should never throw so just observe it. + return [4 /*yield*/, this.stopInternal(error)]; + case 1: + // stopInternal should never throw so just observe it. + _a.sent(); + return [4 /*yield*/, this.stopPromise]; + case 2: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + HttpConnection.prototype.stopInternal = function (error) { + return __awaiter(this, void 0, void 0, function () { + var e_1, e_2, e_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + // Set error as soon as possible otherwise there is a race between + // the transport closing and providing an error and the error from a close message + // We would prefer the close message error. + this.stopError = error; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.startInternalPromise]; + case 2: + _a.sent(); + return [3 /*break*/, 4]; + case 3: + e_1 = _a.sent(); + return [3 /*break*/, 4]; + case 4: + if (!this.sendQueue) return [3 /*break*/, 9]; + _a.label = 5; + case 5: + _a.trys.push([5, 7, , 8]); + return [4 /*yield*/, this.sendQueue.stop()]; + case 6: + _a.sent(); + return [3 /*break*/, 8]; + case 7: + e_2 = _a.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, "TransportSendQueue.stop() threw error '" + e_2 + "'."); + return [3 /*break*/, 8]; + case 8: + this.sendQueue = undefined; + _a.label = 9; + case 9: + if (!this.transport) return [3 /*break*/, 14]; + _a.label = 10; + case 10: + _a.trys.push([10, 12, , 13]); + return [4 /*yield*/, this.transport.stop()]; + case 11: + _a.sent(); + return [3 /*break*/, 13]; + case 12: + e_3 = _a.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, "HttpConnection.transport.stop() threw error '" + e_3 + "'."); + this.stopConnection(); + return [3 /*break*/, 13]; + case 13: + this.transport = undefined; + return [3 /*break*/, 15]; + case 14: + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "HttpConnection.transport is undefined in HttpConnection.stop() because start() failed."); + this.stopConnection(); + _a.label = 15; + case 15: return [2 /*return*/]; + } + }); + }); + }; + HttpConnection.prototype.startInternal = function (transferFormat) { + return __awaiter(this, void 0, void 0, function () { + var url, negotiateResponse, redirects, _loop_1, this_1, e_4; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + url = this.baseUrl; + this.accessTokenFactory = this.options.accessTokenFactory; + _a.label = 1; + case 1: + _a.trys.push([1, 12, , 13]); + if (!this.options.skipNegotiation) return [3 /*break*/, 5]; + if (!(this.options.transport === _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].WebSockets)) return [3 /*break*/, 3]; + // No need to add a connection ID in this case + this.transport = this.constructTransport(_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].WebSockets); + // We should just call connect directly in this case. + // No fallback or negotiate in this case. + return [4 /*yield*/, this.startTransport(url, transferFormat)]; + case 2: + // We should just call connect directly in this case. + // No fallback or negotiate in this case. + _a.sent(); + return [3 /*break*/, 4]; + case 3: throw new Error("Negotiation can only be skipped when using the WebSocket transport directly."); + case 4: return [3 /*break*/, 11]; + case 5: + negotiateResponse = null; + redirects = 0; + _loop_1 = function () { + var accessToken_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this_1.getNegotiationResponse(url)]; + case 1: + negotiateResponse = _a.sent(); + // the user tries to stop the connection when it is being started + if (this_1.connectionState === "Disconnecting" /* Disconnecting */ || this_1.connectionState === "Disconnected" /* Disconnected */) { + throw new Error("The connection was stopped during negotiation."); + } + if (negotiateResponse.error) { + throw new Error(negotiateResponse.error); + } + if (negotiateResponse.ProtocolVersion) { + throw new Error("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details."); + } + if (negotiateResponse.url) { + url = negotiateResponse.url; + } + if (negotiateResponse.accessToken) { + accessToken_1 = negotiateResponse.accessToken; + this_1.accessTokenFactory = function () { return accessToken_1; }; + } + redirects++; + return [2 /*return*/]; + } + }); + }; + this_1 = this; + _a.label = 6; + case 6: return [5 /*yield**/, _loop_1()]; + case 7: + _a.sent(); + _a.label = 8; + case 8: + if (negotiateResponse.url && redirects < MAX_REDIRECTS) return [3 /*break*/, 6]; + _a.label = 9; + case 9: + if (redirects === MAX_REDIRECTS && negotiateResponse.url) { + throw new Error("Negotiate redirection limit exceeded."); + } + return [4 /*yield*/, this.createTransport(url, this.options.transport, negotiateResponse, transferFormat)]; + case 10: + _a.sent(); + _a.label = 11; + case 11: + if (this.transport instanceof _LongPollingTransport__WEBPACK_IMPORTED_MODULE_3__["LongPollingTransport"]) { + this.features.inherentKeepAlive = true; + } + if (this.connectionState === "Connecting " /* Connecting */) { + // Ensure the connection transitions to the connected state prior to completing this.startInternalPromise. + // start() will handle the case when stop was called and startInternal exits still in the disconnecting state. + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "The HttpConnection connected successfully."); + this.connectionState = "Connected" /* Connected */; + } + return [3 /*break*/, 13]; + case 12: + e_4 = _a.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, "Failed to start the connection: " + e_4); + this.connectionState = "Disconnected" /* Disconnected */; + this.transport = undefined; + return [2 /*return*/, Promise.reject(e_4)]; + case 13: return [2 /*return*/]; + } + }); + }); + }; + HttpConnection.prototype.getNegotiationResponse = function (url) { + return __awaiter(this, void 0, void 0, function () { + var _a, headers, token, negotiateUrl, response, negotiateResponse, e_5; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!this.accessTokenFactory) return [3 /*break*/, 2]; + return [4 /*yield*/, this.accessTokenFactory()]; + case 1: + token = _b.sent(); + if (token) { + headers = (_a = {}, + _a["Authorization"] = "Bearer " + token, + _a); + } + _b.label = 2; + case 2: + negotiateUrl = this.resolveNegotiateUrl(url); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Sending negotiation request: " + negotiateUrl + "."); + _b.label = 3; + case 3: + _b.trys.push([3, 5, , 6]); + return [4 /*yield*/, this.httpClient.post(negotiateUrl, { + content: "", + headers: headers, + })]; + case 4: + response = _b.sent(); + if (response.statusCode !== 200) { + return [2 /*return*/, Promise.reject(new Error("Unexpected status code returned from negotiate " + response.statusCode))]; + } + negotiateResponse = JSON.parse(response.content); + if (!negotiateResponse.negotiateVersion || negotiateResponse.negotiateVersion < 1) { + // Negotiate version 0 doesn't use connectionToken + // So we set it equal to connectionId so all our logic can use connectionToken without being aware of the negotiate version + negotiateResponse.connectionToken = negotiateResponse.connectionId; + } + return [2 /*return*/, negotiateResponse]; + case 5: + e_5 = _b.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, "Failed to complete negotiation with the server: " + e_5); + return [2 /*return*/, Promise.reject(e_5)]; + case 6: return [2 /*return*/]; + } + }); + }); + }; + HttpConnection.prototype.createConnectUrl = function (url, connectionToken) { + if (!connectionToken) { + return url; + } + return url + (url.indexOf("?") === -1 ? "?" : "&") + ("id=" + connectionToken); + }; + HttpConnection.prototype.createTransport = function (url, requestedTransport, negotiateResponse, requestedTransferFormat) { + return __awaiter(this, void 0, void 0, function () { + var connectUrl, transportExceptions, transports, negotiate, _i, transports_1, endpoint, transportOrError, ex_1, ex_2, message; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + connectUrl = this.createConnectUrl(url, negotiateResponse.connectionToken); + if (!this.isITransport(requestedTransport)) return [3 /*break*/, 2]; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Connection was provided an instance of ITransport, using that directly."); + this.transport = requestedTransport; + return [4 /*yield*/, this.startTransport(connectUrl, requestedTransferFormat)]; + case 1: + _a.sent(); + this.connectionId = negotiateResponse.connectionId; + return [2 /*return*/]; + case 2: + transportExceptions = []; + transports = negotiateResponse.availableTransports || []; + negotiate = negotiateResponse; + _i = 0, transports_1 = transports; + _a.label = 3; + case 3: + if (!(_i < transports_1.length)) return [3 /*break*/, 13]; + endpoint = transports_1[_i]; + transportOrError = this.resolveTransportOrError(endpoint, requestedTransport, requestedTransferFormat); + if (!(transportOrError instanceof Error)) return [3 /*break*/, 4]; + // Store the error and continue, we don't want to cause a re-negotiate in these cases + transportExceptions.push(endpoint.transport + " failed: " + transportOrError); + return [3 /*break*/, 12]; + case 4: + if (!this.isITransport(transportOrError)) return [3 /*break*/, 12]; + this.transport = transportOrError; + if (!!negotiate) return [3 /*break*/, 9]; + _a.label = 5; + case 5: + _a.trys.push([5, 7, , 8]); + return [4 /*yield*/, this.getNegotiationResponse(url)]; + case 6: + negotiate = _a.sent(); + return [3 /*break*/, 8]; + case 7: + ex_1 = _a.sent(); + return [2 /*return*/, Promise.reject(ex_1)]; + case 8: + connectUrl = this.createConnectUrl(url, negotiate.connectionToken); + _a.label = 9; + case 9: + _a.trys.push([9, 11, , 12]); + return [4 /*yield*/, this.startTransport(connectUrl, requestedTransferFormat)]; + case 10: + _a.sent(); + this.connectionId = negotiate.connectionId; + return [2 /*return*/]; + case 11: + ex_2 = _a.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, "Failed to start the transport '" + endpoint.transport + "': " + ex_2); + negotiate = undefined; + transportExceptions.push(endpoint.transport + " failed: " + ex_2); + if (this.connectionState !== "Connecting " /* Connecting */) { + message = "Failed to select transport before stop() was called."; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, message); + return [2 /*return*/, Promise.reject(new Error(message))]; + } + return [3 /*break*/, 12]; + case 12: + _i++; + return [3 /*break*/, 3]; + case 13: + if (transportExceptions.length > 0) { + return [2 /*return*/, Promise.reject(new Error("Unable to connect to the server with any of the available transports. " + transportExceptions.join(" ")))]; + } + return [2 /*return*/, Promise.reject(new Error("None of the transports supported by the client are supported by the server."))]; + } + }); + }); + }; + HttpConnection.prototype.constructTransport = function (transport) { + switch (transport) { + case _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].WebSockets: + if (!this.options.WebSocket) { + throw new Error("'WebSocket' is not supported in your environment."); + } + return new _WebSocketTransport__WEBPACK_IMPORTED_MODULE_6__["WebSocketTransport"](this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.WebSocket); + case _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].ServerSentEvents: + if (!this.options.EventSource) { + throw new Error("'EventSource' is not supported in your environment."); + } + return new _ServerSentEventsTransport__WEBPACK_IMPORTED_MODULE_4__["ServerSentEventsTransport"](this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.EventSource); + case _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].LongPolling: + return new _LongPollingTransport__WEBPACK_IMPORTED_MODULE_3__["LongPollingTransport"](this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false); + default: + throw new Error("Unknown transport: " + transport + "."); + } + }; + HttpConnection.prototype.startTransport = function (url, transferFormat) { + var _this = this; + this.transport.onreceive = this.onreceive; + this.transport.onclose = function (e) { return _this.stopConnection(e); }; + return this.transport.connect(url, transferFormat); + }; + HttpConnection.prototype.resolveTransportOrError = function (endpoint, requestedTransport, requestedTransferFormat) { + var transport = _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][endpoint.transport]; + if (transport === null || transport === undefined) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Skipping transport '" + endpoint.transport + "' because it is not supported by this client."); + return new Error("Skipping transport '" + endpoint.transport + "' because it is not supported by this client."); + } + else { + if (transportMatches(requestedTransport, transport)) { + var transferFormats = endpoint.transferFormats.map(function (s) { return _ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"][s]; }); + if (transferFormats.indexOf(requestedTransferFormat) >= 0) { + if ((transport === _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].WebSockets && !this.options.WebSocket) || + (transport === _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].ServerSentEvents && !this.options.EventSource)) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Skipping transport '" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport] + "' because it is not supported in your environment.'"); + return new Error("'" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport] + "' is not supported in your environment."); + } + else { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Selecting transport '" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport] + "'."); + try { + return this.constructTransport(transport); + } + catch (ex) { + return ex; + } + } + } + else { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Skipping transport '" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport] + "' because it does not support the requested transfer format '" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"][requestedTransferFormat] + "'."); + return new Error("'" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport] + "' does not support " + _ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"][requestedTransferFormat] + "."); + } + } + else { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Skipping transport '" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport] + "' because it was disabled by the client."); + return new Error("'" + _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport] + "' is disabled by the client."); + } + } + }; + HttpConnection.prototype.isITransport = function (transport) { + return transport && typeof (transport) === "object" && "connect" in transport; + }; + HttpConnection.prototype.stopConnection = function (error) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "HttpConnection.stopConnection(" + error + ") called while in state " + this.connectionState + "."); + this.transport = undefined; + // If we have a stopError, it takes precedence over the error from the transport + error = this.stopError || error; + this.stopError = undefined; + if (this.connectionState === "Disconnected" /* Disconnected */) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug, "Call to HttpConnection.stopConnection(" + error + ") was ignored because the connection is already in the disconnected state."); + return; + } + if (this.connectionState === "Connecting " /* Connecting */) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Warning, "Call to HttpConnection.stopConnection(" + error + ") was ignored because the connection hasn't yet left the in the connecting state."); + return; + } + if (this.connectionState === "Disconnecting" /* Disconnecting */) { + // A call to stop() induced this call to stopConnection and needs to be completed. + // Any stop() awaiters will be scheduled to continue after the onclose callback fires. + this.stopPromiseResolver(); + } + if (error) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, "Connection disconnected with error '" + error + "'."); + } + else { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Information, "Connection disconnected."); + } + this.connectionId = undefined; + this.connectionState = "Disconnected" /* Disconnected */; + if (this.onclose && this.connectionStarted) { + this.connectionStarted = false; + try { + this.onclose(error); + } + catch (e) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error, "HttpConnection.onclose(" + error + ") threw error '" + e + "'."); + } + } + }; + HttpConnection.prototype.resolveUrl = function (url) { + // startsWith is not supported in IE + if (url.lastIndexOf("https://", 0) === 0 || url.lastIndexOf("http://", 0) === 0) { + return url; + } + if (!_Utils__WEBPACK_IMPORTED_MODULE_5__["Platform"].isBrowser || !window.document) { + throw new Error("Cannot resolve '" + url + "'."); + } + // Setting the url to the href propery of an anchor tag handles normalization + // for us. There are 3 main cases. + // 1. Relative path normalization e.g "b" -> "http://localhost:5000/a/b" + // 2. Absolute path normalization e.g "/a/b" -> "http://localhost:5000/a/b" + // 3. Networkpath reference normalization e.g "//localhost:5000/a/b" -> "http://localhost:5000/a/b" + var aTag = window.document.createElement("a"); + aTag.href = url; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Information, "Normalizing '" + url + "' to '" + aTag.href + "'."); + return aTag.href; + }; + HttpConnection.prototype.resolveNegotiateUrl = function (url) { + var index = url.indexOf("?"); + var negotiateUrl = url.substring(0, index === -1 ? url.length : index); + if (negotiateUrl[negotiateUrl.length - 1] !== "/") { + negotiateUrl += "/"; + } + negotiateUrl += "negotiate"; + negotiateUrl += index === -1 ? "" : url.substring(index); + if (negotiateUrl.indexOf("negotiateVersion") === -1) { + negotiateUrl += index === -1 ? "?" : "&"; + negotiateUrl += "negotiateVersion=" + this.negotiateVersion; + } + return negotiateUrl; + }; + return HttpConnection; +}()); + +function transportMatches(requestedTransport, actualTransport) { + return !requestedTransport || ((actualTransport & requestedTransport) !== 0); +} +/** @private */ +var TransportSendQueue = /** @class */ (function () { + function TransportSendQueue(transport) { + this.transport = transport; + this.buffer = []; + this.executing = true; + this.sendBufferedData = new PromiseSource(); + this.transportResult = new PromiseSource(); + this.sendLoopPromise = this.sendLoop(); + } + TransportSendQueue.prototype.send = function (data) { + this.bufferData(data); + if (!this.transportResult) { + this.transportResult = new PromiseSource(); + } + return this.transportResult.promise; + }; + TransportSendQueue.prototype.stop = function () { + this.executing = false; + this.sendBufferedData.resolve(); + return this.sendLoopPromise; + }; + TransportSendQueue.prototype.bufferData = function (data) { + if (this.buffer.length && typeof (this.buffer[0]) !== typeof (data)) { + throw new Error("Expected data to be of type " + typeof (this.buffer) + " but was of type " + typeof (data)); + } + this.buffer.push(data); + this.sendBufferedData.resolve(); + }; + TransportSendQueue.prototype.sendLoop = function () { + return __awaiter(this, void 0, void 0, function () { + var transportResult, data, error_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (false) {} + return [4 /*yield*/, this.sendBufferedData.promise]; + case 1: + _a.sent(); + if (!this.executing) { + if (this.transportResult) { + this.transportResult.reject("Connection stopped."); + } + return [3 /*break*/, 6]; + } + this.sendBufferedData = new PromiseSource(); + transportResult = this.transportResult; + this.transportResult = undefined; + data = typeof (this.buffer[0]) === "string" ? + this.buffer.join("") : + TransportSendQueue.concatBuffers(this.buffer); + this.buffer.length = 0; + _a.label = 2; + case 2: + _a.trys.push([2, 4, , 5]); + return [4 /*yield*/, this.transport.send(data)]; + case 3: + _a.sent(); + transportResult.resolve(); + return [3 /*break*/, 5]; + case 4: + error_1 = _a.sent(); + transportResult.reject(error_1); + return [3 /*break*/, 5]; + case 5: return [3 /*break*/, 0]; + case 6: return [2 /*return*/]; + } + }); + }); + }; + TransportSendQueue.concatBuffers = function (arrayBuffers) { + var totalLength = arrayBuffers.map(function (b) { return b.byteLength; }).reduce(function (a, b) { return a + b; }); + var result = new Uint8Array(totalLength); + var offset = 0; + for (var _i = 0, arrayBuffers_1 = arrayBuffers; _i < arrayBuffers_1.length; _i++) { + var item = arrayBuffers_1[_i]; + result.set(new Uint8Array(item), offset); + offset += item.byteLength; + } + return result; + }; + return TransportSendQueue; +}()); + +var PromiseSource = /** @class */ (function () { + function PromiseSource() { + var _this = this; + this.promise = new Promise(function (resolve, reject) { + var _a; + return _a = [resolve, reject], _this.resolver = _a[0], _this.rejecter = _a[1], _a; + }); + } + PromiseSource.prototype.resolve = function () { + this.resolver(); + }; + PromiseSource.prototype.reject = function (reason) { + this.rejecter(reason); + }; + return PromiseSource; +}()); + + +/***/ }), +/* 20 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HttpTransportType", function() { return HttpTransportType; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TransferFormat", function() { return TransferFormat; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// This will be treated as a bit flag in the future, so we keep it using power-of-two values. +/** Specifies a specific HTTP transport type. */ +var HttpTransportType; +(function (HttpTransportType) { + /** Specifies no transport preference. */ + HttpTransportType[HttpTransportType["None"] = 0] = "None"; + /** Specifies the WebSockets transport. */ + HttpTransportType[HttpTransportType["WebSockets"] = 1] = "WebSockets"; + /** Specifies the Server-Sent Events transport. */ + HttpTransportType[HttpTransportType["ServerSentEvents"] = 2] = "ServerSentEvents"; + /** Specifies the Long Polling transport. */ + HttpTransportType[HttpTransportType["LongPolling"] = 4] = "LongPolling"; +})(HttpTransportType || (HttpTransportType = {})); +/** Specifies the transfer format for a connection. */ +var TransferFormat; +(function (TransferFormat) { + /** Specifies that only text data will be transmitted over the connection. */ + TransferFormat[TransferFormat["Text"] = 1] = "Text"; + /** Specifies that binary data will be transmitted over the connection. */ + TransferFormat[TransferFormat["Binary"] = 2] = "Binary"; +})(TransferFormat || (TransferFormat = {})); + + +/***/ }), +/* 21 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LongPollingTransport", function() { return LongPollingTransport; }); +/* harmony import */ var _AbortController__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(22); +/* harmony import */ var _Errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9); +/* harmony import */ var _ITransport__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(20); +/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(13); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (undefined && undefined.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; + + + + + +// Not exported from 'index', this type is internal. +/** @private */ +var LongPollingTransport = /** @class */ (function () { + function LongPollingTransport(httpClient, accessTokenFactory, logger, logMessageContent) { + this.httpClient = httpClient; + this.accessTokenFactory = accessTokenFactory; + this.logger = logger; + this.pollAbort = new _AbortController__WEBPACK_IMPORTED_MODULE_0__["AbortController"](); + this.logMessageContent = logMessageContent; + this.running = false; + this.onreceive = null; + this.onclose = null; + } + Object.defineProperty(LongPollingTransport.prototype, "pollAborted", { + // This is an internal type, not exported from 'index' so this is really just internal. + get: function () { + return this.pollAbort.aborted; + }, + enumerable: true, + configurable: true + }); + LongPollingTransport.prototype.connect = function (url, transferFormat) { + return __awaiter(this, void 0, void 0, function () { + var pollOptions, token, pollUrl, response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isRequired(url, "url"); + _Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isRequired(transferFormat, "transferFormat"); + _Utils__WEBPACK_IMPORTED_MODULE_4__["Arg"].isIn(transferFormat, _ITransport__WEBPACK_IMPORTED_MODULE_3__["TransferFormat"], "transferFormat"); + this.url = url; + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) Connecting."); + // Allow binary format on Node and Browsers that support binary content (indicated by the presence of responseType property) + if (transferFormat === _ITransport__WEBPACK_IMPORTED_MODULE_3__["TransferFormat"].Binary && + (typeof XMLHttpRequest !== "undefined" && typeof new XMLHttpRequest().responseType !== "string")) { + throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported."); + } + pollOptions = { + abortSignal: this.pollAbort.signal, + headers: {}, + timeout: 100000, + }; + if (transferFormat === _ITransport__WEBPACK_IMPORTED_MODULE_3__["TransferFormat"].Binary) { + pollOptions.responseType = "arraybuffer"; + } + return [4 /*yield*/, this.getAccessToken()]; + case 1: + token = _a.sent(); + this.updateHeaderToken(pollOptions, token); + pollUrl = url + "&_=" + Date.now(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) polling: " + pollUrl + "."); + return [4 /*yield*/, this.httpClient.get(pollUrl, pollOptions)]; + case 2: + response = _a.sent(); + if (response.statusCode !== 200) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, "(LongPolling transport) Unexpected response code: " + response.statusCode + "."); + // Mark running as false so that the poll immediately ends and runs the close logic + this.closeError = new _Errors__WEBPACK_IMPORTED_MODULE_1__["HttpError"](response.statusText || "", response.statusCode); + this.running = false; + } + else { + this.running = true; + } + this.receiving = this.poll(this.url, pollOptions); + return [2 /*return*/]; + } + }); + }); + }; + LongPollingTransport.prototype.getAccessToken = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!this.accessTokenFactory) return [3 /*break*/, 2]; + return [4 /*yield*/, this.accessTokenFactory()]; + case 1: return [2 /*return*/, _a.sent()]; + case 2: return [2 /*return*/, null]; + } + }); + }); + }; + LongPollingTransport.prototype.updateHeaderToken = function (request, token) { + if (!request.headers) { + request.headers = {}; + } + if (token) { + // tslint:disable-next-line:no-string-literal + request.headers["Authorization"] = "Bearer " + token; + return; + } + // tslint:disable-next-line:no-string-literal + if (request.headers["Authorization"]) { + // tslint:disable-next-line:no-string-literal + delete request.headers["Authorization"]; + } + }; + LongPollingTransport.prototype.poll = function (url, pollOptions) { + return __awaiter(this, void 0, void 0, function () { + var token, pollUrl, response, e_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, , 8, 9]); + _a.label = 1; + case 1: + if (!this.running) return [3 /*break*/, 7]; + return [4 /*yield*/, this.getAccessToken()]; + case 2: + token = _a.sent(); + this.updateHeaderToken(pollOptions, token); + _a.label = 3; + case 3: + _a.trys.push([3, 5, , 6]); + pollUrl = url + "&_=" + Date.now(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) polling: " + pollUrl + "."); + return [4 /*yield*/, this.httpClient.get(pollUrl, pollOptions)]; + case 4: + response = _a.sent(); + if (response.statusCode === 204) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Information, "(LongPolling transport) Poll terminated by server."); + this.running = false; + } + else if (response.statusCode !== 200) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Error, "(LongPolling transport) Unexpected response code: " + response.statusCode + "."); + // Unexpected status code + this.closeError = new _Errors__WEBPACK_IMPORTED_MODULE_1__["HttpError"](response.statusText || "", response.statusCode); + this.running = false; + } + else { + // Process the response + if (response.content) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) data received. " + Object(_Utils__WEBPACK_IMPORTED_MODULE_4__["getDataDetail"])(response.content, this.logMessageContent) + "."); + if (this.onreceive) { + this.onreceive(response.content); + } + } + else { + // This is another way timeout manifest. + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) Poll timed out, reissuing."); + } + } + return [3 /*break*/, 6]; + case 5: + e_1 = _a.sent(); + if (!this.running) { + // Log but disregard errors that occur after stopping + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) Poll errored after shutdown: " + e_1.message); + } + else { + if (e_1 instanceof _Errors__WEBPACK_IMPORTED_MODULE_1__["TimeoutError"]) { + // Ignore timeouts and reissue the poll. + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) Poll timed out, reissuing."); + } + else { + // Close the connection with the error as the result. + this.closeError = e_1; + this.running = false; + } + } + return [3 /*break*/, 6]; + case 6: return [3 /*break*/, 1]; + case 7: return [3 /*break*/, 9]; + case 8: + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) Polling complete."); + // We will reach here with pollAborted==false when the server returned a response causing the transport to stop. + // If pollAborted==true then client initiated the stop and the stop method will raise the close event after DELETE is sent. + if (!this.pollAborted) { + this.raiseOnClose(); + } + return [7 /*endfinally*/]; + case 9: return [2 /*return*/]; + } + }); + }); + }; + LongPollingTransport.prototype.send = function (data) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + if (!this.running) { + return [2 /*return*/, Promise.reject(new Error("Cannot send until the transport is connected"))]; + } + return [2 /*return*/, Object(_Utils__WEBPACK_IMPORTED_MODULE_4__["sendMessage"])(this.logger, "LongPolling", this.httpClient, this.url, this.accessTokenFactory, data, this.logMessageContent)]; + }); + }); + }; + LongPollingTransport.prototype.stop = function () { + return __awaiter(this, void 0, void 0, function () { + var deleteOptions, token; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) Stopping polling."); + // Tell receiving loop to stop, abort any current request, and then wait for it to finish + this.running = false; + this.pollAbort.abort(); + _a.label = 1; + case 1: + _a.trys.push([1, , 5, 6]); + return [4 /*yield*/, this.receiving]; + case 2: + _a.sent(); + // Send DELETE to clean up long polling on the server + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) sending DELETE request to " + this.url + "."); + deleteOptions = { + headers: {}, + }; + return [4 /*yield*/, this.getAccessToken()]; + case 3: + token = _a.sent(); + this.updateHeaderToken(deleteOptions, token); + return [4 /*yield*/, this.httpClient.delete(this.url, deleteOptions)]; + case 4: + _a.sent(); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) DELETE request sent."); + return [3 /*break*/, 6]; + case 5: + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, "(LongPolling transport) Stop finished."); + // Raise close event here instead of in polling + // It needs to happen after the DELETE request is sent + this.raiseOnClose(); + return [7 /*endfinally*/]; + case 6: return [2 /*return*/]; + } + }); + }); + }; + LongPollingTransport.prototype.raiseOnClose = function () { + if (this.onclose) { + var logMessage = "(LongPolling transport) Firing onclose event."; + if (this.closeError) { + logMessage += " Error: " + this.closeError; + } + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Trace, logMessage); + this.onclose(this.closeError); + } + }; + return LongPollingTransport; +}()); + + + +/***/ }), +/* 22 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AbortController", function() { return AbortController; }); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Rough polyfill of https://developer.mozilla.org/en-US/docs/Web/API/AbortController +// We don't actually ever use the API being polyfilled, we always use the polyfill because +// it's a very new API right now. +// Not exported from index. +/** @private */ +var AbortController = /** @class */ (function () { + function AbortController() { + this.isAborted = false; + this.onabort = null; + } + AbortController.prototype.abort = function () { + if (!this.isAborted) { + this.isAborted = true; + if (this.onabort) { + this.onabort(); + } + } + }; + Object.defineProperty(AbortController.prototype, "signal", { + get: function () { + return this; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(AbortController.prototype, "aborted", { + get: function () { + return this.isAborted; + }, + enumerable: true, + configurable: true + }); + return AbortController; +}()); + + + +/***/ }), +/* 23 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ServerSentEventsTransport", function() { return ServerSentEventsTransport; }); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9); +/* harmony import */ var _ITransport__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(20); +/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (undefined && undefined.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; + + + +/** @private */ +var ServerSentEventsTransport = /** @class */ (function () { + function ServerSentEventsTransport(httpClient, accessTokenFactory, logger, logMessageContent, eventSourceConstructor) { + this.httpClient = httpClient; + this.accessTokenFactory = accessTokenFactory; + this.logger = logger; + this.logMessageContent = logMessageContent; + this.eventSourceConstructor = eventSourceConstructor; + this.onreceive = null; + this.onclose = null; + } + ServerSentEventsTransport.prototype.connect = function (url, transferFormat) { + return __awaiter(this, void 0, void 0, function () { + var token; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _Utils__WEBPACK_IMPORTED_MODULE_2__["Arg"].isRequired(url, "url"); + _Utils__WEBPACK_IMPORTED_MODULE_2__["Arg"].isRequired(transferFormat, "transferFormat"); + _Utils__WEBPACK_IMPORTED_MODULE_2__["Arg"].isIn(transferFormat, _ITransport__WEBPACK_IMPORTED_MODULE_1__["TransferFormat"], "transferFormat"); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Trace, "(SSE transport) Connecting."); + // set url before accessTokenFactory because this.url is only for send and we set the auth header instead of the query string for send + this.url = url; + if (!this.accessTokenFactory) return [3 /*break*/, 2]; + return [4 /*yield*/, this.accessTokenFactory()]; + case 1: + token = _a.sent(); + if (token) { + url += (url.indexOf("?") < 0 ? "?" : "&") + ("access_token=" + encodeURIComponent(token)); + } + _a.label = 2; + case 2: return [2 /*return*/, new Promise(function (resolve, reject) { + var opened = false; + if (transferFormat !== _ITransport__WEBPACK_IMPORTED_MODULE_1__["TransferFormat"].Text) { + reject(new Error("The Server-Sent Events transport only supports the 'Text' transfer format")); + return; + } + var eventSource; + if (_Utils__WEBPACK_IMPORTED_MODULE_2__["Platform"].isBrowser || _Utils__WEBPACK_IMPORTED_MODULE_2__["Platform"].isWebWorker) { + eventSource = new _this.eventSourceConstructor(url, { withCredentials: true }); + } + else { + // Non-browser passes cookies via the dictionary + var cookies = _this.httpClient.getCookieString(url); + eventSource = new _this.eventSourceConstructor(url, { withCredentials: true, headers: { Cookie: cookies } }); + } + try { + eventSource.onmessage = function (e) { + if (_this.onreceive) { + try { + _this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Trace, "(SSE transport) data received. " + Object(_Utils__WEBPACK_IMPORTED_MODULE_2__["getDataDetail"])(e.data, _this.logMessageContent) + "."); + _this.onreceive(e.data); + } + catch (error) { + _this.close(error); + return; + } + } + }; + eventSource.onerror = function (e) { + var error = new Error(e.data || "Error occurred"); + if (opened) { + _this.close(error); + } + else { + reject(error); + } + }; + eventSource.onopen = function () { + _this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Information, "SSE connected to " + _this.url); + _this.eventSource = eventSource; + opened = true; + resolve(); + }; + } + catch (e) { + reject(e); + return; + } + })]; + } + }); + }); + }; + ServerSentEventsTransport.prototype.send = function (data) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + if (!this.eventSource) { + return [2 /*return*/, Promise.reject(new Error("Cannot send until the transport is connected"))]; + } + return [2 /*return*/, Object(_Utils__WEBPACK_IMPORTED_MODULE_2__["sendMessage"])(this.logger, "SSE", this.httpClient, this.url, this.accessTokenFactory, data, this.logMessageContent)]; + }); + }); + }; + ServerSentEventsTransport.prototype.stop = function () { + this.close(); + return Promise.resolve(); + }; + ServerSentEventsTransport.prototype.close = function (e) { + if (this.eventSource) { + this.eventSource.close(); + this.eventSource = undefined; + if (this.onclose) { + this.onclose(e); + } + } + }; + return ServerSentEventsTransport; +}()); + + + +/***/ }), +/* 24 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WebSocketTransport", function() { return WebSocketTransport; }); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9); +/* harmony import */ var _ITransport__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(20); +/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (undefined && undefined.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; + + + +/** @private */ +var WebSocketTransport = /** @class */ (function () { + function WebSocketTransport(httpClient, accessTokenFactory, logger, logMessageContent, webSocketConstructor) { + this.logger = logger; + this.accessTokenFactory = accessTokenFactory; + this.logMessageContent = logMessageContent; + this.webSocketConstructor = webSocketConstructor; + this.httpClient = httpClient; + this.onreceive = null; + this.onclose = null; + } + WebSocketTransport.prototype.connect = function (url, transferFormat) { + return __awaiter(this, void 0, void 0, function () { + var token; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _Utils__WEBPACK_IMPORTED_MODULE_2__["Arg"].isRequired(url, "url"); + _Utils__WEBPACK_IMPORTED_MODULE_2__["Arg"].isRequired(transferFormat, "transferFormat"); + _Utils__WEBPACK_IMPORTED_MODULE_2__["Arg"].isIn(transferFormat, _ITransport__WEBPACK_IMPORTED_MODULE_1__["TransferFormat"], "transferFormat"); + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Trace, "(WebSockets transport) Connecting."); + if (!this.accessTokenFactory) return [3 /*break*/, 2]; + return [4 /*yield*/, this.accessTokenFactory()]; + case 1: + token = _a.sent(); + if (token) { + url += (url.indexOf("?") < 0 ? "?" : "&") + ("access_token=" + encodeURIComponent(token)); + } + _a.label = 2; + case 2: return [2 /*return*/, new Promise(function (resolve, reject) { + url = url.replace(/^http/, "ws"); + var webSocket; + var cookies = _this.httpClient.getCookieString(url); + var opened = false; + if (_Utils__WEBPACK_IMPORTED_MODULE_2__["Platform"].isNode && cookies) { + // Only pass cookies when in non-browser environments + webSocket = new _this.webSocketConstructor(url, undefined, { + headers: { + Cookie: "" + cookies, + }, + }); + } + if (!webSocket) { + // Chrome is not happy with passing 'undefined' as protocol + webSocket = new _this.webSocketConstructor(url); + } + if (transferFormat === _ITransport__WEBPACK_IMPORTED_MODULE_1__["TransferFormat"].Binary) { + webSocket.binaryType = "arraybuffer"; + } + // tslint:disable-next-line:variable-name + webSocket.onopen = function (_event) { + _this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Information, "WebSocket connected to " + url + "."); + _this.webSocket = webSocket; + opened = true; + resolve(); + }; + webSocket.onerror = function (event) { + var error = null; + // ErrorEvent is a browser only type we need to check if the type exists before using it + if (typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent) { + error = event.error; + } + else { + error = new Error("There was an error with the transport."); + } + reject(error); + }; + webSocket.onmessage = function (message) { + _this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Trace, "(WebSockets transport) data received. " + Object(_Utils__WEBPACK_IMPORTED_MODULE_2__["getDataDetail"])(message.data, _this.logMessageContent) + "."); + if (_this.onreceive) { + _this.onreceive(message.data); + } + }; + webSocket.onclose = function (event) { + // Don't call close handler if connection was never established + // We'll reject the connect call instead + if (opened) { + _this.close(event); + } + else { + var error = null; + // ErrorEvent is a browser only type we need to check if the type exists before using it + if (typeof ErrorEvent !== "undefined" && event instanceof ErrorEvent) { + error = event.error; + } + else { + error = new Error("There was an error with the transport."); + } + reject(error); + } + }; + })]; + } + }); + }); + }; + WebSocketTransport.prototype.send = function (data) { + if (this.webSocket && this.webSocket.readyState === this.webSocketConstructor.OPEN) { + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Trace, "(WebSockets transport) sending data. " + Object(_Utils__WEBPACK_IMPORTED_MODULE_2__["getDataDetail"])(data, this.logMessageContent) + "."); + this.webSocket.send(data); + return Promise.resolve(); + } + return Promise.reject("WebSocket is not in the OPEN state"); + }; + WebSocketTransport.prototype.stop = function () { + if (this.webSocket) { + // Clear websocket handlers because we are considering the socket closed now + this.webSocket.onclose = function () { }; + this.webSocket.onmessage = function () { }; + this.webSocket.onerror = function () { }; + this.webSocket.close(); + this.webSocket = undefined; + // Manually invoke onclose callback inline so we know the HttpConnection was closed properly before returning + // This also solves an issue where websocket.onclose could take 18+ seconds to trigger during network disconnects + this.close(undefined); + } + return Promise.resolve(); + }; + WebSocketTransport.prototype.close = function (event) { + // webSocket will be null if the transport did not start successfully + this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Trace, "(WebSockets transport) socket closed."); + if (this.onclose) { + if (event && (event.wasClean === false || event.code !== 1000)) { + this.onclose(new Error("WebSocket closed with status code: " + event.code + " (" + event.reason + ").")); + } + else { + this.onclose(); + } + } + }; + return WebSocketTransport; +}()); + + + +/***/ }), +/* 25 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "JsonHubProtocol", function() { return JsonHubProtocol; }); +/* harmony import */ var _IHubProtocol__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(15); +/* harmony import */ var _ILogger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); +/* harmony import */ var _ITransport__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(20); +/* harmony import */ var _Loggers__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(14); +/* harmony import */ var _TextMessageFormat__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(12); +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + + + + +var JSON_HUB_PROTOCOL_NAME = "json"; +/** Implements the JSON Hub Protocol. */ +var JsonHubProtocol = /** @class */ (function () { + function JsonHubProtocol() { + /** @inheritDoc */ + this.name = JSON_HUB_PROTOCOL_NAME; + /** @inheritDoc */ + this.version = 1; + /** @inheritDoc */ + this.transferFormat = _ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"].Text; + } + /** Creates an array of {@link @microsoft/signalr.HubMessage} objects from the specified serialized representation. + * + * @param {string} input A string containing the serialized representation. + * @param {ILogger} logger A logger that will be used to log messages that occur during parsing. + */ + JsonHubProtocol.prototype.parseMessages = function (input, logger) { + // The interface does allow "ArrayBuffer" to be passed in, but this implementation does not. So let's throw a useful error. + if (typeof input !== "string") { + throw new Error("Invalid input for JSON hub protocol. Expected a string."); + } + if (!input) { + return []; + } + if (logger === null) { + logger = _Loggers__WEBPACK_IMPORTED_MODULE_3__["NullLogger"].instance; + } + // Parse the messages + var messages = _TextMessageFormat__WEBPACK_IMPORTED_MODULE_4__["TextMessageFormat"].parse(input); + var hubMessages = []; + for (var _i = 0, messages_1 = messages; _i < messages_1.length; _i++) { + var message = messages_1[_i]; + var parsedMessage = JSON.parse(message); + if (typeof parsedMessage.type !== "number") { + throw new Error("Invalid payload."); + } + switch (parsedMessage.type) { + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_0__["MessageType"].Invocation: + this.isInvocationMessage(parsedMessage); + break; + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_0__["MessageType"].StreamItem: + this.isStreamItemMessage(parsedMessage); + break; + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_0__["MessageType"].Completion: + this.isCompletionMessage(parsedMessage); + break; + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_0__["MessageType"].Ping: + // Single value, no need to validate + break; + case _IHubProtocol__WEBPACK_IMPORTED_MODULE_0__["MessageType"].Close: + // All optional values, no need to validate + break; + default: + // Future protocol changes can add message types, old clients can ignore them + logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Information, "Unknown message type '" + parsedMessage.type + "' ignored."); + continue; + } + hubMessages.push(parsedMessage); + } + return hubMessages; + }; + /** Writes the specified {@link @microsoft/signalr.HubMessage} to a string and returns it. + * + * @param {HubMessage} message The message to write. + * @returns {string} A string containing the serialized representation of the message. + */ + JsonHubProtocol.prototype.writeMessage = function (message) { + return _TextMessageFormat__WEBPACK_IMPORTED_MODULE_4__["TextMessageFormat"].write(JSON.stringify(message)); + }; + JsonHubProtocol.prototype.isInvocationMessage = function (message) { + this.assertNotEmptyString(message.target, "Invalid payload for Invocation message."); + if (message.invocationId !== undefined) { + this.assertNotEmptyString(message.invocationId, "Invalid payload for Invocation message."); + } + }; + JsonHubProtocol.prototype.isStreamItemMessage = function (message) { + this.assertNotEmptyString(message.invocationId, "Invalid payload for StreamItem message."); + if (message.item === undefined) { + throw new Error("Invalid payload for StreamItem message."); + } + }; + JsonHubProtocol.prototype.isCompletionMessage = function (message) { + if (message.result && message.error) { + throw new Error("Invalid payload for Completion message."); + } + if (!message.result && message.error) { + this.assertNotEmptyString(message.error, "Invalid payload for Completion message."); + } + this.assertNotEmptyString(message.invocationId, "Invalid payload for Completion message."); + }; + JsonHubProtocol.prototype.assertNotEmptyString = function (value, errorMessage) { + if (typeof value !== "string" || value === "") { + throw new Error(errorMessage); + } + }; + return JsonHubProtocol; +}()); + + + +/***/ }) +/******/ ]); +}); +//# sourceMappingURL=signalr.js.map \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.js.map b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.js.map new file mode 100644 index 000000000..4cb870776 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack://signalR/webpack/universalModuleDefinition","webpack://signalR/webpack/bootstrap","webpack://signalR/src/browser-index.ts","webpack://signalR/node_modules/es6-promise/dist/es6-promise.auto.js","webpack://signalR/common/node_modules/webpack/buildin/global.js","webpack://signalR/src/index.ts","webpack://signalR/src/Errors.ts","webpack://signalR/src/HttpClient.ts","webpack://signalR/src/DefaultHttpClient.ts","webpack://signalR/src/EmptyNodeHttpClient.ts","webpack://signalR/src/XhrHttpClient.ts","webpack://signalR/src/ILogger.ts","webpack://signalR/src/HubConnection.ts","webpack://signalR/src/HandshakeProtocol.ts","webpack://signalR/src/TextMessageFormat.ts","webpack://signalR/src/Utils.ts","webpack://signalR/src/Loggers.ts","webpack://signalR/src/IHubProtocol.ts","webpack://signalR/src/Subject.ts","webpack://signalR/src/HubConnectionBuilder.ts","webpack://signalR/src/DefaultReconnectPolicy.ts","webpack://signalR/src/HttpConnection.ts","webpack://signalR/src/ITransport.ts","webpack://signalR/src/LongPollingTransport.ts","webpack://signalR/src/AbortController.ts","webpack://signalR/src/ServerSentEventsTransport.ts","webpack://signalR/src/WebSocketTransport.ts","webpack://signalR/src/JsonHubProtocol.ts"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD,O;ACVA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA;;;;;;;;AClFA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAE/G,qHAAqH;AAEvE;AAE9C,uIAAuI;AACvI,wEAAwE;AACxE,8EAA8E;AAC9E,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,EAAE;IAC/B,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE;QACnD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO;QAC9B,QAAQ,EAAE,IAAI;KACjB,CAAC,CAAC;CACN;AACD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE;IAC7B,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE;QACjD,wEAAwE;QACxE,oDAAoD;QACpD,KAAK,EAAE,UAAS,KAAc,EAAE,GAAY,IAAI,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtH,QAAQ,EAAE,IAAI;KACjB,CAAC,CAAC;CACN;AACD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,EAAE;IAC/B,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE;QACnD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO;QAC9B,QAAQ,EAAE,IAAI;KACjB,CAAC,CAAC;CACN;AAEuB;;;;;;;AC/BxB;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,CAAC,KAA4D;AAC7D,CAAC,SAC+B;AAChC,CAAC,qBAAqB;;AAEtB;AACA;AACA;AACA;;AAEA;AACA;AACA;;;;AAIA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,gFAAgF;;AAEhF;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,0BAA0B,sBAAsB;;AAEhD;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,iBAAiB,SAAS;AAC1B;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,gBAAgB,mBAAC,CAAC,+HAAO;AACzB;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA,CAAC;AACD;AACA,CAAC,yCAAyC,UAAc;AACxD;AACA,CAAC;AACD;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;;AAGA;AACA;AACA;AACA;AACA,KAAK;AACL,GAAG;AACH;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA,GAAG;AACH;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA,UAAU,IAAI;AACd;AACA,WAAW,QAAQ;AACnB;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;;AAEA;AACA,KAAK;;AAEL;AACA;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA,iBAAiB,wBAAwB;AACzC;AACA;;AAEA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA,mBAAmB,6CAA6C;AAChE;AACA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;AACA,OAAO;AACP;AACA;AACA,OAAO;AACP;AACA;AACA;AACA,OAAO;AACP;AACA;AACA,SAAS;AACT;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;;;AAGA;AACA;;AAEA;AACA;AACA,OAAO;AACP;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL;;AAEA;AACA,CAAC;;AAED;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;;AAEA;AACA;AACA,UAAU,MAAM;AAChB,UAAU,OAAO;AACjB;AACA,WAAW,QAAQ;AACnB;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL,GAAG;;AAEH;AACA;AACA;AACA,KAAK;AACL,GAAG;;AAEH;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL,GAAG;;AAEH;AACA;AACA;AACA,KAAK;AACL,GAAG;;AAEH;AACA;AACA,GAAG;AACH;AACA;AACA,GAAG;AACH;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,UAAU,MAAM;AAChB;AACA,WAAW,QAAQ;AACnB;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,KAAK;AACL,GAAG;AACH;AACA;AACA,qBAAqB,YAAY;AACjC;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;;AAEA;AACA;AACA,UAAU,IAAI;AACd;AACA,WAAW,QAAQ;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA,GAAG;;AAEH;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA;AACA;AACA,KAAK;AACL;;AAEA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,GAAG;AACH;;AAEA;AACA,UAAU,SAAS;AACnB;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA,GAAG;AACH;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA,aAAa;AACb;AACA;AACA;AACA,SAAS;AACT,OAAO;AACP;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA,UAAU,SAAS;AACnB,UAAU,SAAS;AACnB;AACA,WAAW;AACX;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA,UAAU,SAAS;AACnB;AACA,WAAW;AACX;;;AAGA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,0BAA0B;AAC1B,KAAK;AACL;AACA,KAAK;AACL;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL;;AAEA;AACA,YAAY,SAAS;AACrB,aAAa;AACb;;;AAGA;AACA;AACA;;AAEA;AACA;AACA;AACA,OAAO;AACP,KAAK;AACL;AACA;AACA,OAAO;AACP,KAAK;AACL;;AAEA;AACA,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,KAAK;AACL;AACA,KAAK;AACL;AACA;AACA,SAAS;AACT;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;;AAEA,CAAC;;;;AAID;;;;;;;;ACvqCA;;AAEA;AACA;AACA;AACA,CAAC;;AAED;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;;AAEA;AACA;AACA,4CAA4C;;AAE5C;;;;;;;;ACnBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAE/G,6DAA6D;AAC7D,yCAAyC;AAClC,IAAM,OAAO,GAAW,iBAAiB,CAAC;AAIc;AACM;AACb;AAEY;AACN;AAE+B;AAC/C;AAC+B;AAEtC;AACa;AAChB;;;;;;;;ACtBpC;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;AAE/G,+CAA+C;AAC/C;IAA+B,6BAAK;IAQhC;;;;OAIG;IACH,mBAAY,YAAoB,EAAE,UAAkB;;QAApD,iBAQC;QAPG,IAAM,SAAS,GAAG,WAAW,SAAS,CAAC;QACvC,0BAAM,YAAY,CAAC,SAAC;QACpB,KAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAE7B,0CAA0C;QAC1C,8EAA8E;QAC9E,KAAI,CAAC,SAAS,GAAG,SAAS,CAAC;;IAC/B,CAAC;IACL,gBAAC;AAAD,CAAC,CAtB8B,KAAK,GAsBnC;;AAED,2CAA2C;AAC3C;IAAkC,gCAAK;IAKnC;;;OAGG;IACH,sBAAY,YAA4C;;QAA5C,mEAA4C;QAAxD,iBAOC;QANG,IAAM,SAAS,GAAG,WAAW,SAAS,CAAC;QACvC,0BAAM,YAAY,CAAC,SAAC;QAEpB,0CAA0C;QAC1C,8EAA8E;QAC9E,KAAI,CAAC,SAAS,GAAG,SAAS,CAAC;;IAC/B,CAAC;IACL,mBAAC;AAAD,CAAC,CAjBiC,KAAK,GAiBtC;;AAED,8CAA8C;AAC9C;IAAgC,8BAAK;IAKjC;;;OAGG;IACH,oBAAY,YAA2C;;QAA3C,kEAA2C;QAAvD,iBAOC;QANG,IAAM,SAAS,GAAG,WAAW,SAAS,CAAC;QACvC,0BAAM,YAAY,CAAC,SAAC;QAEpB,0CAA0C;QAC1C,8EAA8E;QAC9E,KAAI,CAAC,SAAS,GAAG,SAAS,CAAC;;IAC/B,CAAC;IACL,iBAAC;AAAD,CAAC,CAjB+B,KAAK,GAiBpC;;;;;;;;;AClED;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;AA4B/G,mCAAmC;AACnC;IA6BI,sBACoB,UAAkB,EAClB,UAAmB,EACnB,OAA8B;QAF9B,eAAU,GAAV,UAAU,CAAQ;QAClB,eAAU,GAAV,UAAU,CAAS;QACnB,YAAO,GAAP,OAAO,CAAuB;IAClD,CAAC;IACL,mBAAC;AAAD,CAAC;;AAED;;;GAGG;AACH;IAAA;IAmFA,CAAC;IApEU,wBAAG,GAAV,UAAW,GAAW,EAAE,OAAqB;QACzC,OAAO,IAAI,CAAC,IAAI,cACT,OAAO,IACV,MAAM,EAAE,KAAK,EACb,GAAG,SACL,CAAC;IACP,CAAC;IAgBM,yBAAI,GAAX,UAAY,GAAW,EAAE,OAAqB;QAC1C,OAAO,IAAI,CAAC,IAAI,cACT,OAAO,IACV,MAAM,EAAE,MAAM,EACd,GAAG,SACL,CAAC;IACP,CAAC;IAgBM,2BAAM,GAAb,UAAc,GAAW,EAAE,OAAqB;QAC5C,OAAO,IAAI,CAAC,IAAI,cACT,OAAO,IACV,MAAM,EAAE,QAAQ,EAChB,GAAG,SACL,CAAC;IACP,CAAC;IASD;;;;OAIG;IACH,aAAa;IACN,oCAAe,GAAtB,UAAuB,GAAW;QAC9B,OAAO,EAAE,CAAC;IACd,CAAC;IACL,iBAAC;AAAD,CAAC;;;;;;;;;ACzJD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;AAEzE;AAC+B;AAEnB;AACF;AAEhD,uEAAuE;AACvE;IAAuC,qCAAU;IAG7C,yJAAyJ;IACzJ,2BAAmB,MAAe;QAAlC,YACI,iBAAO,SAOV;QALG,IAAI,OAAO,cAAc,KAAK,WAAW,EAAE;YACvC,KAAI,CAAC,UAAU,GAAG,IAAI,4DAAa,CAAC,MAAM,CAAC,CAAC;SAC/C;aAAM;YACH,KAAI,CAAC,UAAU,GAAG,IAAI,8DAAc,CAAC,MAAM,CAAC,CAAC;SAChD;;IACL,CAAC;IAED,kBAAkB;IACX,gCAAI,GAAX,UAAY,OAAoB;QAC5B,wDAAwD;QACxD,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE;YACpD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,kDAAU,EAAE,CAAC,CAAC;SAC3C;QAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YACjB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;SAC1D;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YACd,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;SACvD;QAED,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAEM,2CAAe,GAAtB,UAAuB,GAAW;QAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAChD,CAAC;IACL,wBAAC;AAAD,CAAC,CAlCsC,sDAAU,GAkChD;;;;;;;;;AC5CD;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;AAE/G,mIAAmI;AAE3E;AAGxD,eAAe;AACf;IAAoC,kCAAU;IAC1C,4EAA4E;IAC5E,wBAAmB,MAAe;eAC9B,iBAAO;IACX,CAAC;IAEM,6BAAI,GAAX;QACI,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iIAAiI,CAAC,CAAC,CAAC;IACxK,CAAC;IACL,qBAAC;AAAD,CAAC,CATmC,sDAAU,GAS7C;;;;;;;;;AClBD;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;AAEhD;AACM;AACvB;AAE9C;IAAmC,iCAAU;IAGzC,uBAAmB,MAAe;QAAlC,YACI,iBAAO,SAEV;QADG,KAAI,CAAC,MAAM,GAAG,MAAM,CAAC;;IACzB,CAAC;IAED,kBAAkB;IACX,4BAAI,GAAX,UAAY,OAAoB;QAAhC,iBAqEC;QApEG,wDAAwD;QACxD,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE;YACpD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,kDAAU,EAAE,CAAC,CAAC;SAC3C;QAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YACjB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;SAC1D;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YACd,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;SACvD;QAED,OAAO,IAAI,OAAO,CAAe,UAAC,OAAO,EAAE,MAAM;YAC7C,IAAM,GAAG,GAAG,IAAI,cAAc,EAAE,CAAC;YAEjC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAO,EAAE,OAAO,CAAC,GAAI,EAAE,IAAI,CAAC,CAAC;YAC9C,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC;YAC3B,GAAG,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;YAC3D,mFAAmF;YACnF,GAAG,CAAC,gBAAgB,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;YAEjE,IAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;YAChC,IAAI,OAAO,EAAE;gBACT,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;qBACf,OAAO,CAAC,UAAC,MAAM;oBACZ,GAAG,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBAClD,CAAC,CAAC,CAAC;aACV;YAED,IAAI,OAAO,CAAC,YAAY,EAAE;gBACtB,GAAG,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;aAC3C;YAED,IAAI,OAAO,CAAC,WAAW,EAAE;gBACrB,OAAO,CAAC,WAAW,CAAC,OAAO,GAAG;oBAC1B,GAAG,CAAC,KAAK,EAAE,CAAC;oBACZ,MAAM,CAAC,IAAI,kDAAU,EAAE,CAAC,CAAC;gBAC7B,CAAC,CAAC;aACL;YAED,IAAI,OAAO,CAAC,OAAO,EAAE;gBACjB,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;aACjC;YAED,GAAG,CAAC,MAAM,GAAG;gBACT,IAAI,OAAO,CAAC,WAAW,EAAE;oBACrB,OAAO,CAAC,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;iBACtC;gBAED,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;oBACvC,OAAO,CAAC,IAAI,wDAAY,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;iBAC3F;qBAAM;oBACH,MAAM,CAAC,IAAI,iDAAS,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;iBACrD;YACL,CAAC,CAAC;YAEF,GAAG,CAAC,OAAO,GAAG;gBACV,KAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,OAAO,EAAE,8BAA4B,GAAG,CAAC,MAAM,UAAK,GAAG,CAAC,UAAU,MAAG,CAAC,CAAC;gBAChG,MAAM,CAAC,IAAI,iDAAS,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;YACtD,CAAC,CAAC;YAEF,GAAG,CAAC,SAAS,GAAG;gBACZ,KAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAC;gBAChE,MAAM,CAAC,IAAI,oDAAY,EAAE,CAAC,CAAC;YAC/B,CAAC,CAAC;YAEF,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACP,CAAC;IACL,oBAAC;AAAD,CAAC,CA/EkC,sDAAU,GA+E5C;;;;;;;;;ACtFD;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAE/G,2GAA2G;AAC3G;;;GAGG;AACH,IAAY,QAeX;AAfD,WAAY,QAAQ;IAChB,2DAA2D;IAC3D,yCAAS;IACT,sDAAsD;IACtD,yCAAS;IACT,uDAAuD;IACvD,qDAAe;IACf,2EAA2E;IAC3E,6CAAW;IACX,0FAA0F;IAC1F,yCAAS;IACT,4GAA4G;IAC5G,+CAAY;IACZ,wHAAwH;IACxH,uCAAQ;AACZ,CAAC,EAfW,QAAQ,KAAR,QAAQ,QAenB;;;;;;;;ACvBD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEJ;AAE2D;AACxH;AAGV;AACN;AAE9B,IAAM,qBAAqB,GAAW,EAAE,GAAG,IAAI,CAAC;AAChD,IAAM,2BAA2B,GAAW,EAAE,GAAG,IAAI,CAAC;AAEtD,8EAA8E;AAC9E,IAAY,kBAWX;AAXD,WAAY,kBAAkB;IAC1B,0CAA0C;IAC1C,mDAA6B;IAC7B,wCAAwC;IACxC,+CAAyB;IACzB,uCAAuC;IACvC,6CAAuB;IACvB,2CAA2C;IAC3C,qDAA+B;IAC/B,0CAA0C;IAC1C,mDAA6B;AACjC,CAAC,EAXW,kBAAkB,KAAlB,kBAAkB,QAW7B;AAED,gDAAgD;AAChD;IAyDI,uBAAoB,UAAuB,EAAE,MAAe,EAAE,QAAsB,EAAE,eAA8B;QAApH,iBA4BC;QA3BG,0CAAG,CAAC,UAAU,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACzC,0CAAG,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACjC,0CAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAErC,IAAI,CAAC,2BAA2B,GAAG,qBAAqB,CAAC;QACzD,IAAI,CAAC,+BAA+B,GAAG,2BAA2B,CAAC;QAEnE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,iBAAiB,GAAG,IAAI,oEAAiB,EAAE,CAAC;QAEjD,IAAI,CAAC,UAAU,CAAC,SAAS,GAAG,UAAC,IAAS,IAAK,YAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAA9B,CAA8B,CAAC;QAC1E,IAAI,CAAC,UAAU,CAAC,OAAO,GAAG,UAAC,KAAa,IAAK,YAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAA5B,CAA4B,CAAC;QAE1E,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,qBAAqB,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,yBAAyB,GAAG,KAAK,CAAC;QACvC,IAAI,CAAC,eAAe,GAAG,kBAAkB,CAAC,YAAY,CAAC;QACvD,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;QAE/B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,yDAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IACpF,CAAC;IArCD,gBAAgB;IAChB,iGAAiG;IACjG,+FAA+F;IAC/F,6FAA6F;IAC7F,qCAAqC;IACvB,oBAAM,GAApB,UAAqB,UAAuB,EAAE,MAAe,EAAE,QAAsB,EAAE,eAA8B;QACjH,OAAO,IAAI,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;IAC5E,CAAC;IAiCD,sBAAI,gCAAK;QADT,sEAAsE;aACtE;YACI,OAAO,IAAI,CAAC,eAAe,CAAC;QAChC,CAAC;;;OAAA;IAKD,sBAAI,uCAAY;QAHhB;;WAEG;aACH;YACI,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3E,CAAC;;;OAAA;IAGD,sBAAI,kCAAO;QADX,oEAAoE;aACpE;YACI,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,EAAE,CAAC;QACzC,CAAC;QAED;;;;WAIG;aACH,UAAY,GAAW;YACnB,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,YAAY,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,YAAY,EAAE;gBACtH,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;aAC7G;YAED,IAAI,CAAC,GAAG,EAAE;gBACN,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;aACjE;YAED,IAAI,CAAC,UAAU,CAAC,OAAO,GAAG,GAAG,CAAC;QAClC,CAAC;;;OAjBA;IAmBD;;;OAGG;IACI,6BAAK,GAAZ;QACI,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAEa,iDAAyB,GAAvC;;;;;;wBACI,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,YAAY,EAAE;4BAC1D,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC,EAAC;yBAC7G;wBAED,IAAI,CAAC,eAAe,GAAG,kBAAkB,CAAC,UAAU,CAAC;wBACrD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAC,CAAC;;;;wBAGvD,qBAAM,IAAI,CAAC,aAAa,EAAE;;wBAA1B,SAA0B,CAAC;wBAE3B,IAAI,CAAC,eAAe,GAAG,kBAAkB,CAAC,SAAS,CAAC;wBACpD,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;wBAC9B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,uCAAuC,CAAC,CAAC;;;;wBAEzE,IAAI,CAAC,eAAe,GAAG,kBAAkB,CAAC,YAAY,CAAC;wBACvD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,kEAAgE,GAAC,OAAI,CAAC,CAAC;wBACvG,sBAAO,OAAO,CAAC,MAAM,CAAC,GAAC,CAAC,EAAC;;;;;KAEhC;IAEa,qCAAa,GAA3B;;;;;;;wBACI,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;wBACtC,IAAI,CAAC,yBAAyB,GAAG,KAAK,CAAC;wBAEjC,gBAAgB,GAAG,IAAI,OAAO,CAAC,UAAC,OAAO,EAAE,MAAM;4BACjD,KAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC;4BACjC,KAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC;wBACpC,CAAC,CAAC,CAAC;wBAEH,qBAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;;wBAAzD,SAAyD,CAAC;;;;wBAGhD,gBAAgB,GAA4B;4BAC9C,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;4BAC5B,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO;yBACjC,CAAC;wBAEF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC;wBAE9D,qBAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,iBAAiB,CAAC,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;;wBAAtF,SAAsF,CAAC;wBAEvF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,wBAAsB,IAAI,CAAC,QAAQ,CAAC,IAAI,OAAI,CAAC,CAAC;wBAEpF,kGAAkG;wBAClG,IAAI,CAAC,cAAc,EAAE,CAAC;wBACtB,IAAI,CAAC,kBAAkB,EAAE,CAAC;wBAC1B,IAAI,CAAC,sBAAsB,EAAE,CAAC;wBAE9B,qBAAM,gBAAgB;;wBAAtB,SAAsB,CAAC;wBAEvB,mGAAmG;wBACnG,6GAA6G;wBAC7G,iCAAiC;wBACjC,IAAI,IAAI,CAAC,oBAAoB,EAAE;4BAC3B,4GAA4G;4BAC5G,+GAA+G;4BAC/G,qEAAqE;4BACrE,MAAM,IAAI,CAAC,oBAAoB,CAAC;yBACnC;;;;wBAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,sCAAoC,GAAC,8CAA2C,CAAC,CAAC;wBAElH,IAAI,CAAC,cAAc,EAAE,CAAC;wBACtB,IAAI,CAAC,gBAAgB,EAAE,CAAC;wBAExB,yFAAyF;wBACzF,2GAA2G;wBAC3G,qBAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAC,CAAC;;wBAF7B,yFAAyF;wBACzF,2GAA2G;wBAC3G,SAA6B,CAAC;wBAC9B,MAAM,GAAC,CAAC;;;;;KAEf;IAED;;;OAGG;IACU,4BAAI,GAAjB;;;;;;wBAEU,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;wBAEvC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;wBACvC,qBAAM,IAAI,CAAC,WAAW;;wBAAtB,SAAsB,CAAC;;;;wBAGnB,2CAA2C;wBAC3C,qBAAM,YAAY;;wBADlB,2CAA2C;wBAC3C,SAAkB,CAAC;;;;;;;;;KAI1B;IAEO,oCAAY,GAApB,UAAqB,KAAa;QAC9B,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,YAAY,EAAE;YAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,gCAA8B,KAAK,+DAA4D,CAAC,CAAC;YACjI,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;SAC5B;QAED,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,aAAa,EAAE;YAC3D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,iCAA+B,KAAK,4EAAyE,CAAC,CAAC;YAC/I,OAAO,IAAI,CAAC,WAAY,CAAC;SAC5B;QAED,IAAI,CAAC,eAAe,GAAG,kBAAkB,CAAC,aAAa,CAAC;QAExD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAC,CAAC;QAE3D,IAAI,IAAI,CAAC,oBAAoB,EAAE;YAC3B,iGAAiG;YACjG,+FAA+F;YAC/F,8BAA8B;YAC9B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,+DAA+D,CAAC,CAAC;YAEjG,YAAY,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACxC,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;YAEtC,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;SAC5B;QAED,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,oBAAoB,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;QAEtH,4FAA4F;QAC5F,6FAA6F;QAC7F,+EAA+E;QAC/E,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACI,8BAAM,GAAb,UAAuB,UAAkB;QAAzC,iBA2CC;QA3C0C,cAAc;aAAd,UAAc,EAAd,qBAAc,EAAd,IAAc;YAAd,6BAAc;;QAC/C,0CAAwD,EAAvD,eAAO,EAAE,iBAAS,CAAsC;QAC/D,IAAM,oBAAoB,GAAG,IAAI,CAAC,sBAAsB,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAEtF,IAAI,YAA2B,CAAC;QAChC,IAAM,OAAO,GAAG,IAAI,gDAAO,EAAK,CAAC;QACjC,OAAO,CAAC,cAAc,GAAG;YACrB,IAAM,gBAAgB,GAA4B,KAAI,CAAC,sBAAsB,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAC;YAEjH,OAAO,KAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAC;YAEzD,OAAO,YAAY,CAAC,IAAI,CAAC;gBACrB,OAAO,KAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;YACnD,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,YAAY,CAAC,GAAG,UAAC,eAA6D,EAAE,KAAa;YAC7H,IAAI,KAAK,EAAE;gBACP,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACrB,OAAO;aACV;iBAAM,IAAI,eAAe,EAAE;gBACxB,+EAA+E;gBAC/E,IAAI,eAAe,CAAC,IAAI,KAAK,yDAAW,CAAC,UAAU,EAAE;oBACjD,IAAI,eAAe,CAAC,KAAK,EAAE;wBACvB,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;qBACnD;yBAAM;wBACH,OAAO,CAAC,QAAQ,EAAE,CAAC;qBACtB;iBACJ;qBAAM;oBACH,OAAO,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,IAAI,CAAM,CAAC,CAAC;iBAC7C;aACJ;QACL,CAAC,CAAC;QAEF,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,oBAAoB,CAAC;aACrD,KAAK,CAAC,UAAC,CAAC;YACL,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjB,OAAO,KAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEP,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAE1C,OAAO,OAAO,CAAC;IACnB,CAAC;IAEO,mCAAW,GAAnB,UAAoB,OAAY;QAC5B,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACK,wCAAgB,GAAxB,UAAyB,OAAY;QACjC,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACjE,CAAC;IAED;;;;;;;;OAQG;IACI,4BAAI,GAAX,UAAY,UAAkB;QAAE,cAAc;aAAd,UAAc,EAAd,qBAAc,EAAd,IAAc;YAAd,6BAAc;;QACpC,0CAAwD,EAAvD,eAAO,EAAE,iBAAS,CAAsC;QAC/D,IAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;QAEpG,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAEzC,OAAO,WAAW,CAAC;IACvB,CAAC;IAED;;;;;;;;;;OAUG;IACI,8BAAM,GAAb,UAAuB,UAAkB;QAAzC,iBAmCC;QAnC0C,cAAc;aAAd,UAAc,EAAd,qBAAc,EAAd,IAAc;YAAd,6BAAc;;QAC/C,0CAAwD,EAAvD,eAAO,EAAE,iBAAS,CAAsC;QAC/D,IAAM,oBAAoB,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAEvF,IAAM,CAAC,GAAG,IAAI,OAAO,CAAM,UAAC,OAAO,EAAE,MAAM;YACvC,sEAAsE;YACtE,KAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,YAAa,CAAC,GAAG,UAAC,eAA6D,EAAE,KAAa;gBAC9H,IAAI,KAAK,EAAE;oBACP,MAAM,CAAC,KAAK,CAAC,CAAC;oBACd,OAAO;iBACV;qBAAM,IAAI,eAAe,EAAE;oBACxB,+EAA+E;oBAC/E,IAAI,eAAe,CAAC,IAAI,KAAK,yDAAW,CAAC,UAAU,EAAE;wBACjD,IAAI,eAAe,CAAC,KAAK,EAAE;4BACvB,MAAM,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;yBAC5C;6BAAM;4BACH,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;yBACnC;qBACJ;yBAAM;wBACH,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA4B,eAAe,CAAC,IAAM,CAAC,CAAC,CAAC;qBACzE;iBACJ;YACL,CAAC,CAAC;YAEF,IAAM,YAAY,GAAG,KAAI,CAAC,gBAAgB,CAAC,oBAAoB,CAAC;iBAC3D,KAAK,CAAC,UAAC,CAAC;gBACL,MAAM,CAAC,CAAC,CAAC,CAAC;gBACV,sEAAsE;gBACtE,OAAO,KAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,YAAa,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;YAEP,KAAI,CAAC,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,CAAC;IACb,CAAC;IAED;;;;OAIG;IACI,0BAAE,GAAT,UAAU,UAAkB,EAAE,SAAmC;QAC7D,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,EAAE;YAC3B,OAAO;SACV;QAED,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;YAC3B,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;SACjC;QAED,qDAAqD;QACrD,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE;YACpD,OAAO;SACV;QAED,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;IAiBM,2BAAG,GAAV,UAAW,UAAkB,EAAE,MAAiC;QAC5D,IAAI,CAAC,UAAU,EAAE;YACb,OAAO;SACV;QAED,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;QACtC,IAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,EAAE;YACX,OAAO;SACV;QACD,IAAI,MAAM,EAAE;YACR,IAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC3C,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE;gBAClB,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;gBAC9B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;oBACvB,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;iBACnC;aACJ;SACJ;aAAM;YACH,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;SACnC;IAEL,CAAC;IAED;;;OAGG;IACI,+BAAO,GAAd,UAAe,QAAiC;QAC5C,IAAI,QAAQ,EAAE;YACV,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;SACvC;IACL,CAAC;IAED;;;OAGG;IACI,sCAAc,GAArB,UAAsB,QAAiC;QACnD,IAAI,QAAQ,EAAE;YACV,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;SAC7C;IACL,CAAC;IAED;;;OAGG;IACI,qCAAa,GAApB,UAAqB,QAAyC;QAC1D,IAAI,QAAQ,EAAE;YACV,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;SAC5C;IACL,CAAC;IAEO,2CAAmB,GAA3B,UAA4B,IAAS;QACjC,IAAI,CAAC,cAAc,EAAE,CAAC;QAEtB,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE;YACjC,IAAI,GAAG,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC;SACzC;QAED,iEAAiE;QACjE,IAAI,IAAI,EAAE;YACN,qBAAqB;YACrB,IAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAEhE,KAAsB,UAAQ,EAAR,qBAAQ,EAAR,sBAAQ,EAAR,IAAQ,EAAE;gBAA3B,IAAM,OAAO;gBACd,QAAQ,OAAO,CAAC,IAAI,EAAE;oBAClB,KAAK,yDAAW,CAAC,UAAU;wBACvB,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;wBACjC,MAAM;oBACV,KAAK,yDAAW,CAAC,UAAU,CAAC;oBAC5B,KAAK,yDAAW,CAAC,UAAU;wBACvB,IAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;wBACtD,IAAI,QAAQ,EAAE;4BACV,IAAI,OAAO,CAAC,IAAI,KAAK,yDAAW,CAAC,UAAU,EAAE;gCACzC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;6BAC/C;4BACD,QAAQ,CAAC,OAAO,CAAC,CAAC;yBACrB;wBACD,MAAM;oBACV,KAAK,yDAAW,CAAC,IAAI;wBACjB,yBAAyB;wBACzB,MAAM;oBACV,KAAK,yDAAW,CAAC,KAAK;wBAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,qCAAqC,CAAC,CAAC;wBAE7E,IAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,qCAAqC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;wBAE3G,IAAI,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE;4BACjC,6IAA6I;4BAC7I,4HAA4H;4BAE5H,gDAAgD;4BAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;yBAC/B;6BAAM;4BACH,0HAA0H;4BAC1H,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;yBAC/C;wBAED,MAAM;oBACV;wBACI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,OAAO,EAAE,2BAAyB,OAAO,CAAC,IAAI,MAAG,CAAC,CAAC;wBAC5E,MAAM;iBACb;aACJ;SACJ;QAED,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAEO,gDAAwB,GAAhC,UAAiC,IAAS;;QACtC,IAAI,eAAyC,CAAC;QAC9C,IAAI,aAAkB,CAAC;QAEvB,IAAI;YACA,wDAAsF,EAArF,qBAAa,EAAE,uBAAe,CAAwD;SAC1F;QAAC,OAAO,CAAC,EAAE;YACR,IAAM,OAAO,GAAG,oCAAoC,GAAG,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAEzC,IAAM,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;YACjC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,KAAK,CAAC;SACf;QACD,IAAI,eAAe,CAAC,KAAK,EAAE;YACvB,IAAM,OAAO,GAAG,mCAAmC,GAAG,eAAe,CAAC,KAAK,CAAC;YAC5E,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAEzC,IAAM,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;YACjC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,KAAK,CAAC;SACf;aAAM;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC;SACjE;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,OAAO,aAAa,CAAC;IACzB,CAAC;IAEO,8CAAsB,GAA9B;QAAA,iBAaC;QAZG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC;;;;;6BAC3B,KAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,SAAS,GAArD,wBAAqD;;;;wBAEjD,qBAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,iBAAiB,CAAC;;wBAA9C,SAA8C,CAAC;;;;wBAE/C,4EAA4E;wBAC5E,oGAAoG;wBACpG,IAAI,CAAC,gBAAgB,EAAE,CAAC;;;;;aAGnC,EAAE,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAC7C,CAAC;IAEO,0CAAkB,GAA1B;QAAA,iBAKC;QAJG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,iBAAiB,EAAE;YAC1E,wBAAwB;YACxB,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,cAAM,YAAI,CAAC,aAAa,EAAE,EAApB,CAAoB,EAAE,IAAI,CAAC,2BAA2B,CAAC,CAAC;SACjG;IACL,CAAC;IAEO,qCAAa,GAArB;QACI,+EAA+E;QAC/E,uGAAuG;QACvG,gDAAgD;QAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC,CAAC;IAC3G,CAAC;IAEO,0CAAkB,GAA1B,UAA2B,iBAAoC;QAA/D,iBAoBC;QAnBG,IAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QACrE,IAAI,OAAO,EAAE;YACT,IAAI;gBACA,OAAO,CAAC,OAAO,CAAC,UAAC,CAAC,IAAK,QAAC,CAAC,KAAK,CAAC,KAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC,EAA1C,CAA0C,CAAC,CAAC;aACtE;YAAC,OAAO,CAAC,EAAE;gBACR,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,+BAA6B,iBAAiB,CAAC,MAAM,CAAC,WAAW,EAAE,sBAAiB,CAAC,OAAI,CAAC,CAAC;aAC9H;YAED,IAAI,iBAAiB,CAAC,YAAY,EAAE;gBAChC,4GAA4G;gBAC5G,IAAM,OAAO,GAAG,oFAAoF,CAAC;gBACrG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBAEzC,4CAA4C;gBAC5C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;aAC5D;SACJ;aAAM;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,OAAO,EAAE,qCAAmC,iBAAiB,CAAC,MAAM,aAAU,CAAC,CAAC;SAC5G;IACL,CAAC;IAEO,wCAAgB,GAAxB,UAAyB,KAAa;QAClC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,oCAAkC,KAAK,gCAA2B,IAAI,CAAC,eAAe,MAAG,CAAC,CAAC;QAE3H,kIAAkI;QAClI,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;QAE7J,uGAAuG;QACvG,sDAAsD;QACtD,IAAI,IAAI,CAAC,iBAAiB,EAAE;YACxB,IAAI,CAAC,iBAAiB,EAAE,CAAC;SAC5B;QAED,IAAI,CAAC,wBAAwB,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC,CAAC;QAExH,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,aAAa,EAAE;YAC3D,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;SAC7B;aAAM,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,SAAS,IAAI,IAAI,CAAC,eAAe,EAAE;YACtF,gDAAgD;YAChD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;SACzB;aAAM,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,SAAS,EAAE;YAC9D,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;SAC7B;QAED,gGAAgG;QAChG,sHAAsH;QACtH,kJAAkJ;QAClJ,oDAAoD;QACpD,8DAA8D;IAClE,CAAC;IAEO,qCAAa,GAArB,UAAsB,KAAa;QAAnC,iBAWC;QAVG,IAAI,IAAI,CAAC,iBAAiB,EAAE;YACxB,IAAI,CAAC,eAAe,GAAG,kBAAkB,CAAC,YAAY,CAAC;YACvD,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;YAE/B,IAAI;gBACA,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,UAAC,CAAC,IAAK,QAAC,CAAC,KAAK,CAAC,KAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAtB,CAAsB,CAAC,CAAC;aAC/D;YAAC,OAAO,CAAC,EAAE;gBACR,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,4CAA0C,KAAK,uBAAkB,CAAC,OAAI,CAAC,CAAC;aAC3G;SACJ;IACL,CAAC;IAEa,iCAAS,GAAvB,UAAwB,KAAa;;;;;;;wBAC3B,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;wBAClC,yBAAyB,GAAG,CAAC,CAAC;wBAC9B,UAAU,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;wBAExG,cAAc,GAAG,IAAI,CAAC,iBAAiB,CAAC,yBAAyB,EAAE,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;wBAExF,IAAI,cAAc,KAAK,IAAI,EAAE;4BACzB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,oGAAoG,CAAC,CAAC;4BACtI,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;4BAC1B,sBAAO;yBACV;wBAED,IAAI,CAAC,eAAe,GAAG,kBAAkB,CAAC,YAAY,CAAC;wBAEvD,IAAI,KAAK,EAAE;4BACP,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,+CAA6C,KAAK,OAAI,CAAC,CAAC;yBACjG;6BAAM;4BACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC,CAAC;yBACrE;wBAED,IAAI,IAAI,CAAC,cAAc,EAAE;4BACrB,IAAI;gCACA,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,UAAC,CAAC,IAAK,QAAC,CAAC,KAAK,CAAC,KAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAtB,CAAsB,CAAC,CAAC;6BACrE;4BAAC,OAAO,CAAC,EAAE;gCACR,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,mDAAiD,KAAK,uBAAkB,CAAC,OAAI,CAAC,CAAC;6BAClH;4BAED,qEAAqE;4BACrE,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,YAAY,EAAE;gCAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,uFAAuF,CAAC,CAAC;gCACzH,sBAAO;6BACV;yBACJ;;;6BAEM,eAAc,KAAK,IAAI;wBAC1B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,8BAA4B,yBAAyB,uBAAkB,cAAc,SAAM,CAAC,CAAC;wBAEnI,qBAAM,IAAI,OAAO,CAAC,UAAC,OAAO;gCACtB,KAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC,OAAO,EAAE,cAAe,CAAC,CAAC;4BACrE,CAAC,CAAC;;wBAFF,SAEE,CAAC;wBACH,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;wBAEtC,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,YAAY,EAAE;4BAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,mFAAmF,CAAC,CAAC;4BACrH,sBAAO;yBACV;;;;wBAGG,qBAAM,IAAI,CAAC,aAAa,EAAE;;wBAA1B,SAA0B,CAAC;wBAE3B,IAAI,CAAC,eAAe,GAAG,kBAAkB,CAAC,SAAS,CAAC;wBACpD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,yCAAyC,CAAC,CAAC;wBAEjF,IAAI,IAAI,CAAC,aAAa,EAAE;4BACpB,IAAI;gCACA,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,UAAC,CAAC,IAAK,QAAC,CAAC,KAAK,CAAC,KAAI,EAAE,CAAC,KAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,EAA7C,CAA6C,CAAC,CAAC;6BAC3F;4BAAC,OAAO,CAAC,EAAE;gCACR,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yDAAuD,IAAI,CAAC,UAAU,CAAC,YAAY,uBAAkB,CAAC,OAAI,CAAC,CAAC;6BAC/I;yBACJ;wBAED,sBAAO;;;wBAEP,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,gDAA8C,GAAC,OAAI,CAAC,CAAC;wBAE3F,IAAI,IAAI,CAAC,eAAe,KAAK,kBAAkB,CAAC,YAAY,EAAE;4BAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,qFAAqF,CAAC,CAAC;4BACvH,sBAAO;yBACV;wBAED,UAAU,GAAG,GAAC,YAAY,KAAK,CAAC,CAAC,CAAC,GAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,GAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;wBAC9D,cAAc,GAAG,IAAI,CAAC,iBAAiB,CAAC,yBAAyB,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,kBAAkB,EAAE,UAAU,CAAC,CAAC;;;;wBAI1H,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,kDAA+C,IAAI,CAAC,GAAG,EAAE,GAAG,kBAAkB,iBAAW,yBAAyB,gDAA6C,CAAC,CAAC;wBAEvM,IAAI,CAAC,aAAa,EAAE,CAAC;;;;;KACxB;IAEO,yCAAiB,GAAzB,UAA0B,kBAA0B,EAAE,mBAA2B,EAAE,WAAkB;QACjG,IAAI;YACA,OAAO,IAAI,CAAC,eAAgB,CAAC,4BAA4B,CAAC;gBACtD,mBAAmB;gBACnB,kBAAkB;gBAClB,WAAW;aACd,CAAC,CAAC;SACN;QAAC,OAAO,CAAC,EAAE;YACR,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,+CAA6C,kBAAkB,UAAK,mBAAmB,uBAAkB,CAAC,OAAI,CAAC,CAAC;YAChJ,OAAO,IAAI,CAAC;SACf;IACL,CAAC;IAEO,gDAAwB,GAAhC,UAAiC,KAAY;QACzC,IAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QAEpB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;aACjB,OAAO,CAAC,UAAC,GAAG;YACT,IAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;YAChC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACX,CAAC;IAEO,wCAAgB,GAAxB;QACI,IAAI,IAAI,CAAC,gBAAgB,EAAE;YACvB,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;SACvC;IACL,CAAC;IAEO,sCAAc,GAAtB;QACI,IAAI,IAAI,CAAC,aAAa,EAAE;YACpB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;SACpC;IACL,CAAC;IAEO,wCAAgB,GAAxB,UAAyB,UAAkB,EAAE,IAAW,EAAE,WAAoB,EAAE,SAAmB;QAC/F,IAAI,WAAW,EAAE;YACb,OAAO;gBACH,SAAS,EAAE,IAAI;gBACf,SAAS;gBACT,MAAM,EAAE,UAAU;gBAClB,IAAI,EAAE,yDAAW,CAAC,UAAU;aAC/B,CAAC;SACL;aAAM;YACH,IAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;YACvC,IAAI,CAAC,YAAY,EAAE,CAAC;YAEpB,OAAO;gBACH,SAAS,EAAE,IAAI;gBACf,YAAY,EAAE,YAAY,CAAC,QAAQ,EAAE;gBACrC,SAAS;gBACT,MAAM,EAAE,UAAU;gBAClB,IAAI,EAAE,yDAAW,CAAC,UAAU;aAC/B,CAAC;SACL;IACL,CAAC;IAEO,qCAAa,GAArB,UAAsB,OAAkC,EAAE,YAA2B;QAArF,iBAkCC;QAjCG,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;YACtB,OAAO;SACV;QAED,gEAAgE;QAChE,IAAI,CAAC,YAAY,EAAE;YACf,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;SACpC;gCAIU,QAAQ;YACf,OAAO,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC;gBACxB,QAAQ,EAAE;oBACN,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,cAAM,YAAI,CAAC,gBAAgB,CAAC,KAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,EAA7D,CAA6D,CAAC,CAAC;gBAC1G,CAAC;gBACD,KAAK,EAAE,UAAC,GAAG;oBACP,IAAI,OAAe,CAAC;oBACpB,IAAI,GAAG,YAAY,KAAK,EAAE;wBACtB,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;qBACzB;yBAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE;wBAC5B,OAAO,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;qBAC5B;yBAAM;wBACH,OAAO,GAAG,eAAe,CAAC;qBAC7B;oBAED,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,cAAM,YAAI,CAAC,gBAAgB,CAAC,KAAI,CAAC,uBAAuB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAtE,CAAsE,CAAC,CAAC;gBACnH,CAAC;gBACD,IAAI,EAAE,UAAC,IAAI;oBACP,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,cAAM,YAAI,CAAC,gBAAgB,CAAC,KAAI,CAAC,uBAAuB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAnE,CAAmE,CAAC,CAAC;gBAChH,CAAC;aACJ,CAAC,CAAC;QACP,CAAC;QAvBD,sEAAsE;QACtE,iCAAiC;QACjC,KAAK,IAAM,QAAQ,IAAI,OAAO;oBAAnB,QAAQ;SAqBlB;IACL,CAAC;IAEO,8CAAsB,GAA9B,UAA+B,IAAW;QACtC,IAAM,OAAO,GAA8B,EAAE,CAAC;QAC9C,IAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAClC,IAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE;gBAC7B,IAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC;gBACnC,IAAI,CAAC,YAAY,EAAE,CAAC;gBACpB,iCAAiC;gBACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;gBAC7B,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAEpC,0BAA0B;gBAC1B,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;aACrB;SACJ;QAED,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAChC,CAAC;IAEO,oCAAY,GAApB,UAAqB,GAAQ;QACzB,oEAAoE;QACpE,OAAO,GAAG,IAAI,GAAG,CAAC,SAAS,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,CAAC;IACvE,CAAC;IAEO,8CAAsB,GAA9B,UAA+B,UAAkB,EAAE,IAAW,EAAE,SAAmB;QAC/E,IAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QACvC,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,OAAO;YACH,SAAS,EAAE,IAAI;YACf,YAAY,EAAE,YAAY,CAAC,QAAQ,EAAE;YACrC,SAAS;YACT,MAAM,EAAE,UAAU;YAClB,IAAI,EAAE,yDAAW,CAAC,gBAAgB;SACrC,CAAC;IACN,CAAC;IAEO,8CAAsB,GAA9B,UAA+B,EAAU;QACrC,OAAO;YACH,YAAY,EAAE,EAAE;YAChB,IAAI,EAAE,yDAAW,CAAC,gBAAgB;SACrC,CAAC;IACN,CAAC;IAEO,+CAAuB,GAA/B,UAAgC,EAAU,EAAE,IAAS;QACjD,OAAO;YACH,YAAY,EAAE,EAAE;YAChB,IAAI;YACJ,IAAI,EAAE,yDAAW,CAAC,UAAU;SAC/B,CAAC;IACN,CAAC;IAEO,+CAAuB,GAA/B,UAAgC,EAAU,EAAE,KAAW,EAAE,MAAY;QACjE,IAAI,KAAK,EAAE;YACP,OAAO;gBACH,KAAK;gBACL,YAAY,EAAE,EAAE;gBAChB,IAAI,EAAE,yDAAW,CAAC,UAAU;aAC/B,CAAC;SACL;QAED,OAAO;YACH,YAAY,EAAE,EAAE;YAChB,MAAM;YACN,IAAI,EAAE,yDAAW,CAAC,UAAU;SAC/B,CAAC;IACN,CAAC;IACL,oBAAC;AAAD,CAAC;;;;;;;;;AC56BD;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAEvD;AAChB;AAcxC,eAAe;AACf;IAAA;IAkDA,CAAC;IAjDG,mCAAmC;IAC5B,iDAAqB,GAA5B,UAA6B,gBAAyC;QAClE,OAAO,oEAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACrE,CAAC;IAEM,kDAAsB,GAA7B,UAA8B,IAAS;QACnC,IAAI,eAAyC,CAAC;QAC9C,IAAI,WAAmB,CAAC;QACxB,IAAI,aAAkB,CAAC;QAEvB,IAAI,4DAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,IAAI,YAAY,MAAM,CAAC,EAAE;YAClF,4EAA4E;YAC5E,IAAM,UAAU,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;YACxC,IAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,oEAAiB,CAAC,mBAAmB,CAAC,CAAC;YACjF,IAAI,cAAc,KAAK,CAAC,CAAC,EAAE;gBACvB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;aAC7C;YAED,iDAAiD;YACjD,gDAAgD;YAChD,IAAM,cAAc,GAAG,cAAc,GAAG,CAAC,CAAC;YAC1C,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;YACnF,aAAa,GAAG,CAAC,UAAU,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;SAC7G;aAAM;YACH,IAAM,QAAQ,GAAW,IAAI,CAAC;YAC9B,IAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,oEAAiB,CAAC,eAAe,CAAC,CAAC;YAC3E,IAAI,cAAc,KAAK,CAAC,CAAC,EAAE;gBACvB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;aAC7C;YAED,iDAAiD;YACjD,gDAAgD;YAChD,IAAM,cAAc,GAAG,cAAc,GAAG,CAAC,CAAC;YAC1C,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;YACpD,aAAa,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;SAClG;QAED,iEAAiE;QACjE,IAAM,QAAQ,GAAG,oEAAiB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACtD,IAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,IAAI,QAAQ,CAAC,IAAI,EAAE;YACf,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;SACrE;QACD,eAAe,GAAG,QAAQ,CAAC;QAE3B,sDAAsD;QACtD,sEAAsE;QACtE,OAAO,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IAC5C,CAAC;IACL,wBAAC;AAAD,CAAC;;;;;;;;;ACrED;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAE/G,0BAA0B;AAC1B,eAAe;AACf;IAAA;IAiBA,CAAC;IAbiB,uBAAK,GAAnB,UAAoB,MAAc;QAC9B,OAAO,KAAG,MAAM,GAAG,iBAAiB,CAAC,eAAiB,CAAC;IAC3D,CAAC;IAEa,uBAAK,GAAnB,UAAoB,KAAa;QAC7B,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,iBAAiB,CAAC,eAAe,EAAE;YAC/D,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;SAC7C;QAED,IAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;QAChE,QAAQ,CAAC,GAAG,EAAE,CAAC;QACf,OAAO,QAAQ,CAAC;IACpB,CAAC;IAfa,qCAAmB,GAAG,IAAI,CAAC;IAC3B,iCAAe,GAAG,MAAM,CAAC,YAAY,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;IAe/F,wBAAC;CAAA;AAjB6B;;;;;;;;ACL9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjE;AACP;AAIvC,eAAe;AACf;IAAA;IAaA,CAAC;IAZiB,cAAU,GAAxB,UAAyB,GAAQ,EAAE,IAAY;QAC3C,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,EAAE;YACnC,MAAM,IAAI,KAAK,CAAC,UAAQ,IAAI,4BAAyB,CAAC,CAAC;SAC1D;IACL,CAAC;IAEa,QAAI,GAAlB,UAAmB,GAAQ,EAAE,MAAW,EAAE,IAAY;QAClD,yGAAyG;QACzG,IAAI,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC,EAAE;YAClB,MAAM,IAAI,KAAK,CAAC,aAAW,IAAI,gBAAW,GAAG,MAAG,CAAC,CAAC;SACrD;IACL,CAAC;IACL,UAAC;AAAD,CAAC;;AAED,eAAe;AACf;IAAA;IAaA,CAAC;IAXG,sBAAkB,qBAAS;aAA3B;YACI,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC;QACtC,CAAC;;;OAAA;IAED,sBAAkB,uBAAW;aAA7B;YACI,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,eAAe,IAAI,IAAI,CAAC;QAC/D,CAAC;;;OAAA;IAED,sBAAkB,kBAAM;aAAxB;YACI,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;QAChD,CAAC;;;OAAA;IACL,eAAC;AAAD,CAAC;;AAED,eAAe;AACR,uBAAuB,IAAS,EAAE,cAAuB;IAC5D,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE;QACrB,MAAM,GAAG,2BAAyB,IAAI,CAAC,UAAY,CAAC;QACpD,IAAI,cAAc,EAAE;YAChB,MAAM,IAAI,iBAAe,iBAAiB,CAAC,IAAI,CAAC,MAAG,CAAC;SACvD;KACJ;SAAM,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QACjC,MAAM,GAAG,2BAAyB,IAAI,CAAC,MAAQ,CAAC;QAChD,IAAI,cAAc,EAAE;YAChB,MAAM,IAAI,iBAAe,IAAI,MAAG,CAAC;SACpC;KACJ;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,eAAe;AACR,2BAA2B,IAAiB;IAC/C,IAAM,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAElC,6DAA6D;IAC7D,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,CAAC,OAAO,CAAC,UAAC,GAAG;QACb,IAAM,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAChC,GAAG,IAAI,OAAK,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAG,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,4CAA4C;AAC5C,eAAe;AACR,uBAAuB,GAAQ;IAClC,OAAO,GAAG,IAAI,OAAO,WAAW,KAAK,WAAW;QAC5C,CAAC,GAAG,YAAY,WAAW;YACvB,kEAAkE;YAClE,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,eAAe;AACR,qBAA2B,MAAe,EAAE,aAAqB,EAAE,UAAsB,EAAE,GAAW,EAAE,kBAAgE,EAAE,OAA6B,EAAE,iBAA0B;;;;;;yBAElO,kBAAkB,EAAlB,wBAAkB;oBACJ,qBAAM,kBAAkB,EAAE;;oBAAlC,KAAK,GAAG,SAA0B;oBACxC,IAAI,KAAK,EAAE;wBACP,OAAO;4BACH,GAAC,eAAe,IAAG,YAAU,KAAO;+BACvC,CAAC;qBACL;;;oBAGL,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,MAAI,aAAa,kCAA6B,aAAa,CAAC,OAAO,EAAE,iBAAiB,CAAC,MAAG,CAAC,CAAC;oBAEjH,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;oBACpD,qBAAM,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE;4BACxC,OAAO;4BACP,OAAO;4BACP,YAAY;yBACf,CAAC;;oBAJI,QAAQ,GAAG,SAIf;oBAEF,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,MAAI,aAAa,uDAAkD,QAAQ,CAAC,UAAU,MAAG,CAAC,CAAC;;;;;CACzH;AAED,eAAe;AACR,sBAAsB,MAA2B;IACpD,IAAI,MAAM,KAAK,SAAS,EAAE;QACtB,OAAO,IAAI,aAAa,CAAC,iDAAQ,CAAC,WAAW,CAAC,CAAC;KAClD;IAED,IAAI,MAAM,KAAK,IAAI,EAAE;QACjB,OAAO,mDAAU,CAAC,QAAQ,CAAC;KAC9B;IAED,IAAK,MAAkB,CAAC,GAAG,EAAE;QACzB,OAAO,MAAiB,CAAC;KAC5B;IAED,OAAO,IAAI,aAAa,CAAC,MAAkB,CAAC,CAAC;AACjD,CAAC;AAED,eAAe;AACf;IAII,6BAAY,OAAmB,EAAE,QAA8B;QAC3D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAEM,qCAAO,GAAd;QACI,IAAM,KAAK,GAAW,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpE,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE;YACZ,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;SAC3C;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE;YACpE,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,UAAC,CAAC,IAAO,CAAC,CAAC,CAAC;SACnD;IACL,CAAC;IACL,0BAAC;AAAD,CAAC;;AAED,eAAe;AACf;IAWI,uBAAY,eAAyB;QACjC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;IACjC,CAAC;IAEM,2BAAG,GAAV,UAAW,QAAkB,EAAE,OAAe;QAC1C,IAAI,QAAQ,IAAI,IAAI,CAAC,eAAe,EAAE;YAClC,QAAQ,QAAQ,EAAE;gBACd,KAAK,iDAAQ,CAAC,QAAQ,CAAC;gBACvB,KAAK,iDAAQ,CAAC,KAAK;oBACf,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,MAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,UAAK,iDAAQ,CAAC,QAAQ,CAAC,UAAK,OAAS,CAAC,CAAC;oBAC5F,MAAM;gBACV,KAAK,iDAAQ,CAAC,OAAO;oBACjB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,UAAK,iDAAQ,CAAC,QAAQ,CAAC,UAAK,OAAS,CAAC,CAAC;oBAC3F,MAAM;gBACV,KAAK,iDAAQ,CAAC,WAAW;oBACrB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,UAAK,iDAAQ,CAAC,QAAQ,CAAC,UAAK,OAAS,CAAC,CAAC;oBAC3F,MAAM;gBACV;oBACI,mGAAmG;oBACnG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,UAAK,iDAAQ,CAAC,QAAQ,CAAC,UAAK,OAAS,CAAC,CAAC;oBAC1F,MAAM;aACb;SACJ;IACL,CAAC;IACL,oBAAC;AAAD,CAAC;;;;;;;;;ACtLD;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAI/G,mEAAmE;AACnE;IAII;IAAuB,CAAC;IAExB,kBAAkB;IAClB,2BAA2B;IACpB,wBAAG,GAAV,UAAW,SAAmB,EAAE,QAAgB;IAChD,CAAC;IARD,2EAA2E;IAC7D,mBAAQ,GAAY,IAAI,UAAU,EAAE,CAAC;IAQvD,iBAAC;CAAA;AAVsB;;;;;;;;ACNvB;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAK/G,yCAAyC;AACzC,IAAY,WAeX;AAfD,WAAY,WAAW;IACnB,gIAAgI;IAChI,yDAAc;IACd,+HAA+H;IAC/H,yDAAc;IACd,+HAA+H;IAC/H,yDAAc;IACd,4IAA4I;IAC5I,qEAAoB;IACpB,4IAA4I;IAC5I,qEAAoB;IACpB,mHAAmH;IACnH,6CAAQ;IACR,qHAAqH;IACrH,+CAAS;AACb,CAAC,EAfW,WAAW,KAAX,WAAW,QAetB;;;;;;;;ACtBD;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAGjE;AAE9C,2DAA2D;AAC3D;IAOI;QACI,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;IACxB,CAAC;IAEM,sBAAI,GAAX,UAAY,IAAO;QACf,KAAuB,UAAc,EAAd,SAAI,CAAC,SAAS,EAAd,cAAc,EAAd,IAAc,EAAE;YAAlC,IAAM,QAAQ;YACf,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACvB;IACL,CAAC;IAEM,uBAAK,GAAZ,UAAa,GAAQ;QACjB,KAAuB,UAAc,EAAd,SAAI,CAAC,SAAS,EAAd,cAAc,EAAd,IAAc,EAAE;YAAlC,IAAM,QAAQ;YACf,IAAI,QAAQ,CAAC,KAAK,EAAE;gBAChB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;aACvB;SACJ;IACL,CAAC;IAEM,0BAAQ,GAAf;QACI,KAAuB,UAAc,EAAd,SAAI,CAAC,SAAS,EAAd,cAAc,EAAd,IAAc,EAAE;YAAlC,IAAM,QAAQ;YACf,IAAI,QAAQ,CAAC,QAAQ,EAAE;gBACnB,QAAQ,CAAC,QAAQ,EAAE,CAAC;aACvB;SACJ;IACL,CAAC;IAEM,2BAAS,GAAhB,UAAiB,QAA8B;QAC3C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9B,OAAO,IAAI,0DAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC;IACL,cAAC;AAAD,CAAC;;;;;;;;;AC5CD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;AAE7C;AAChB;AACF;AAGF;AAGM;AACb;AACM;AAE7C,0CAA0C;AAC1C,IAAM,mBAAmB,GAAG;IACxB,KAAK,EAAE,iDAAQ,CAAC,KAAK;IACrB,KAAK,EAAE,iDAAQ,CAAC,KAAK;IACrB,IAAI,EAAE,iDAAQ,CAAC,WAAW;IAC1B,WAAW,EAAE,iDAAQ,CAAC,WAAW;IACjC,IAAI,EAAE,iDAAQ,CAAC,OAAO;IACtB,OAAO,EAAE,iDAAQ,CAAC,OAAO;IACzB,KAAK,EAAE,iDAAQ,CAAC,KAAK;IACrB,QAAQ,EAAE,iDAAQ,CAAC,QAAQ;IAC3B,IAAI,EAAE,iDAAQ,CAAC,IAAI;CACtB,CAAC;AAEF,uBAAuB,IAAY;IAC/B,6CAA6C;IAC7C,oFAAoF;IACpF,iFAAiF;IACjF,IAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACxD,IAAI,OAAO,OAAO,KAAK,WAAW,EAAE;QAChC,OAAO,OAAO,CAAC;KAClB;SAAM;QACH,MAAM,IAAI,KAAK,CAAC,wBAAsB,IAAM,CAAC,CAAC;KACjD;AACL,CAAC;AAED,oFAAoF;AACpF;IAAA;IA2KA,CAAC;IAjIU,+CAAgB,GAAvB,UAAwB,OAAoC;QACxD,0CAAG,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAEnC,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE;YACnB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;SACzB;aAAM,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;YACpC,IAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,GAAG,IAAI,oDAAa,CAAC,QAAQ,CAAC,CAAC;SAC7C;aAAM;YACH,IAAI,CAAC,MAAM,GAAG,IAAI,oDAAa,CAAC,OAAO,CAAC,CAAC;SAC5C;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IA0BM,sCAAO,GAAd,UAAe,GAAW,EAAE,sBAAmE;QAC3F,0CAAG,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAE3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QAEf,gHAAgH;QAChH,wHAAwH;QACxH,IAAI,OAAO,sBAAsB,KAAK,QAAQ,EAAE;YAC5C,IAAI,CAAC,qBAAqB,gBAAQ,IAAI,CAAC,qBAAqB,EAAK,sBAAsB,CAAE,CAAC;SAC7F;aAAM;YACH,IAAI,CAAC,qBAAqB,gBACnB,IAAI,CAAC,qBAAqB,IAC7B,SAAS,EAAE,sBAAsB,GACpC,CAAC;SACL;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACI,8CAAe,GAAtB,UAAuB,QAAsB;QACzC,0CAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAErC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,OAAO,IAAI,CAAC;IAChB,CAAC;IAmBM,qDAAsB,GAA7B,UAA8B,4BAAsD;QAChF,IAAI,IAAI,CAAC,eAAe,EAAE;YACtB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;SAC9D;QAED,IAAI,CAAC,4BAA4B,EAAE;YAC/B,IAAI,CAAC,eAAe,GAAG,IAAI,8EAAsB,EAAE,CAAC;SACvD;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,4BAA4B,CAAC,EAAE;YACpD,IAAI,CAAC,eAAe,GAAG,IAAI,8EAAsB,CAAC,4BAA4B,CAAC,CAAC;SACnF;aAAM;YACH,IAAI,CAAC,eAAe,GAAG,4BAA4B,CAAC;SACvD;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACI,oCAAK,GAAZ;QACI,qFAAqF;QACrF,8BAA8B;QAC9B,IAAM,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,IAAI,EAAE,CAAC;QAE/D,8EAA8E;QAC9E,IAAI,qBAAqB,CAAC,MAAM,KAAK,SAAS,EAAE;YAC5C,gGAAgG;YAChG,qBAAqB,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;SAC9C;QAED,4BAA4B;QAC5B,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;YACX,MAAM,IAAI,KAAK,CAAC,0FAA0F,CAAC,CAAC;SAC/G;QACD,IAAM,UAAU,GAAG,IAAI,8DAAc,CAAC,IAAI,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;QAEvE,OAAO,4DAAa,CAAC,MAAM,CACvB,UAAU,EACV,IAAI,CAAC,MAAM,IAAI,mDAAU,CAAC,QAAQ,EAClC,IAAI,CAAC,QAAQ,IAAI,IAAI,gEAAe,EAAE,EACtC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC9B,CAAC;IACL,2BAAC;AAAD,CAAC;;AAED,kBAAkB,MAAW;IACzB,OAAO,MAAM,CAAC,GAAG,KAAK,SAAS,CAAC;AACpC,CAAC;;;;;;;;ACxND;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAI/G,wDAAwD;AACxD,IAAM,oCAAoC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;AAE3E,eAAe;AACf;IAGI,gCAAY,WAAsB;QAC9B,IAAI,CAAC,WAAW,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAK,WAAW,SAAE,IAAI,GAAE,CAAC,CAAC,oCAAoC,CAAC;IACjH,CAAC;IAEM,6DAA4B,GAAnC,UAAoC,YAA0B;QAC1D,OAAO,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC;IAC7D,CAAC;IACL,6BAAC;AAAD,CAAC;;;;;;;;;ACnBD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEvD;AAIV;AAC+B;AACf;AACU;AAClB;AACI;AA2B1D,IAAM,aAAa,GAAG,GAAG,CAAC;AAE1B,IAAI,eAAe,GAAQ,IAAI,CAAC;AAChC,IAAI,iBAAiB,GAAQ,IAAI,CAAC;AAClC,IAAI,+CAAQ,CAAC,MAAM,IAAI,UAAc,KAAK,WAAW,EAAE;IACnD,oFAAoF;IACpF,gDAAgD;IAChD,IAAM,WAAW,GAAG,KAAyC,CAAC,CAAC,CAAC,OAAuB,CAAC,CAAC,CAAC,SAAO,CAAC;IAClG,eAAe,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACpC,iBAAiB,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;CAClD;AAED,eAAe;AACf;IAwBI,wBAAY,GAAW,EAAE,OAAoC;QAApC,sCAAoC;QAR7C,aAAQ,GAAQ,EAAE,CAAC;QAMlB,qBAAgB,GAAW,CAAC,CAAC;QAG1C,0CAAG,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAE3B,IAAI,CAAC,MAAM,GAAG,2DAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAEpC,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;QACxB,OAAO,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,KAAK,CAAC;QAE/D,IAAI,CAAC,+CAAQ,CAAC,MAAM,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE;YAC5E,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;SACjC;aAAM,IAAI,+CAAQ,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE;YAC9C,IAAI,eAAe,EAAE;gBACjB,OAAO,CAAC,SAAS,GAAG,eAAe,CAAC;aACvC;SACJ;QAED,IAAI,CAAC,+CAAQ,CAAC,MAAM,IAAI,OAAO,WAAW,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;YAChF,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;SACrC;aAAM,IAAI,+CAAQ,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;YAChD,IAAI,OAAO,iBAAiB,KAAK,WAAW,EAAE;gBAC1C,OAAO,CAAC,WAAW,GAAG,iBAAiB,CAAC;aAC3C;SACJ;QAED,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,oEAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3E,IAAI,CAAC,eAAe,oCAA+B,CAAC;QACpD,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QAEvB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;IAIY,8BAAK,GAAlB,UAAmB,cAA+B;;;;;;wBAC9C,cAAc,GAAG,cAAc,IAAI,0DAAc,CAAC,MAAM,CAAC;wBAEzD,0CAAG,CAAC,IAAI,CAAC,cAAc,EAAE,0DAAc,EAAE,gBAAgB,CAAC,CAAC;wBAE3D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,+CAA6C,0DAAc,CAAC,cAAc,CAAC,OAAI,CAAC,CAAC;wBAEjH,IAAI,IAAI,CAAC,eAAe,sCAAiC,EAAE;4BACvD,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC,EAAC;yBAC/G;wBAED,IAAI,CAAC,eAAe,iCAA6B,CAAC;wBAElD,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;wBAC/D,qBAAM,IAAI,CAAC,oBAAoB;;wBAA/B,SAA+B,CAAC;6BAG5B,KAAI,CAAC,eAAsB,wCAAkC,GAA7D,wBAA6D;wBAEvD,OAAO,GAAG,8DAA8D,CAAC;wBAC/E,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;wBAEzC,uGAAuG;wBACvG,qBAAM,IAAI,CAAC,WAAW;;wBADtB,uGAAuG;wBACvG,SAAsB,CAAC;wBAEvB,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAC;;wBACvC,IAAI,IAAI,CAAC,eAAsB,gCAA8B,EAAE;4BAE5D,OAAO,GAAG,6GAA6G,CAAC;4BAC9H,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;4BACzC,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAC;yBAC7C;;;wBAED,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;;;;;KACjC;IAEM,6BAAI,GAAX,UAAY,IAA0B;QAClC,IAAI,IAAI,CAAC,eAAe,gCAA8B,EAAE;YACpD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC,CAAC;SAC3G;QAED,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACjB,IAAI,CAAC,SAAS,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,SAAU,CAAC,CAAC;SAC5D;QAED,mDAAmD;QACnD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAEY,6BAAI,GAAjB,UAAkB,KAAa;;;;;;wBAC3B,IAAI,IAAI,CAAC,eAAe,sCAAiC,EAAE;4BACvD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,iCAA+B,KAAK,2EAAwE,CAAC,CAAC;4BAC9I,sBAAO,OAAO,CAAC,OAAO,EAAE,EAAC;yBAC5B;wBAED,IAAI,IAAI,CAAC,eAAe,wCAAkC,EAAE;4BACxD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,iCAA+B,KAAK,4EAAyE,CAAC,CAAC;4BAC/I,sBAAO,IAAI,CAAC,WAAW,EAAC;yBAC3B;wBAED,IAAI,CAAC,eAAe,sCAAgC,CAAC;wBAErD,IAAI,CAAC,WAAW,GAAG,IAAI,OAAO,CAAC,UAAC,OAAO;4BACnC,0DAA0D;4BAC1D,KAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC;wBACvC,CAAC,CAAC,CAAC;wBAEH,sDAAsD;wBACtD,qBAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;;wBAD9B,sDAAsD;wBACtD,SAA8B,CAAC;wBAC/B,qBAAM,IAAI,CAAC,WAAW;;wBAAtB,SAAsB,CAAC;;;;;KAC1B;IAEa,qCAAY,GAA1B,UAA2B,KAAa;;;;;;wBACpC,kEAAkE;wBAClE,kFAAkF;wBAClF,2CAA2C;wBAC3C,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;;;;wBAGnB,qBAAM,IAAI,CAAC,oBAAoB;;wBAA/B,SAA+B,CAAC;;;;;;6BAKhC,IAAI,CAAC,SAAS,EAAd,wBAAc;;;;wBAEV,qBAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;;wBAA3B,SAA2B,CAAC;;;;wBAE5B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,4CAA0C,GAAC,OAAI,CAAC,CAAC;;;wBAErF,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;;;6BAM3B,IAAI,CAAC,SAAS,EAAd,yBAAc;;;;wBAEV,qBAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;;wBAA3B,SAA2B,CAAC;;;;wBAE5B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,kDAAgD,GAAC,OAAI,CAAC,CAAC;wBACvF,IAAI,CAAC,cAAc,EAAE,CAAC;;;wBAG1B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;;;wBAE3B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,wFAAwF,CAAC,CAAC;wBAC1H,IAAI,CAAC,cAAc,EAAE,CAAC;;;;;;KAE7B;IAEa,sCAAa,GAA3B,UAA4B,cAA8B;;;;;;wBAGlD,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;wBACvB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC;;;;6BAGlD,IAAI,CAAC,OAAO,CAAC,eAAe,EAA5B,wBAA4B;6BACxB,KAAI,CAAC,OAAO,CAAC,SAAS,KAAK,6DAAiB,CAAC,UAAU,GAAvD,wBAAuD;wBACvD,8CAA8C;wBAC9C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,6DAAiB,CAAC,UAAU,CAAC,CAAC;wBACvE,qDAAqD;wBACrD,yCAAyC;wBACzC,qBAAM,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC;;wBAF9C,qDAAqD;wBACrD,yCAAyC;wBACzC,SAA8C,CAAC;;4BAE/C,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;;;wBAGhG,iBAAiB,GAA8B,IAAI,CAAC;wBACpD,SAAS,GAAG,CAAC,CAAC;;;;;4CAGM,qBAAM,OAAK,sBAAsB,CAAC,GAAG,CAAC;;wCAA1D,iBAAiB,GAAG,SAAsC,CAAC;wCAC3D,iEAAiE;wCACjE,IAAI,OAAK,eAAe,wCAAkC,IAAI,OAAK,eAAe,sCAAiC,EAAE;4CACjH,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;yCACrE;wCAED,IAAI,iBAAiB,CAAC,KAAK,EAAE;4CACzB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;yCAC5C;wCAED,IAAK,iBAAyB,CAAC,eAAe,EAAE;4CAC5C,MAAM,IAAI,KAAK,CAAC,8LAA8L,CAAC,CAAC;yCACnN;wCAED,IAAI,iBAAiB,CAAC,GAAG,EAAE;4CACvB,GAAG,GAAG,iBAAiB,CAAC,GAAG,CAAC;yCAC/B;wCAED,IAAI,iBAAiB,CAAC,WAAW,EAAE;4CAGzB,gBAAc,iBAAiB,CAAC,WAAW,CAAC;4CAClD,OAAK,kBAAkB,GAAG,cAAM,oBAAW,EAAX,CAAW,CAAC;yCAC/C;wCAED,SAAS,EAAE,CAAC;;;;;;;;;;;;4BAET,iBAAiB,CAAC,GAAG,IAAI,SAAS,GAAG,aAAa;;;wBAEzD,IAAI,SAAS,KAAK,aAAa,IAAI,iBAAiB,CAAC,GAAG,EAAE;4BACtD,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;yBAC5D;wBAED,qBAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,iBAAiB,EAAE,cAAc,CAAC;;wBAA1F,SAA0F,CAAC;;;wBAG/F,IAAI,IAAI,CAAC,SAAS,YAAY,0EAAoB,EAAE;4BAChD,IAAI,CAAC,QAAQ,CAAC,iBAAiB,GAAG,IAAI,CAAC;yBAC1C;wBAED,IAAI,IAAI,CAAC,eAAe,mCAA+B,EAAE;4BACrD,0GAA0G;4BAC1G,8GAA8G;4BAC9G,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,4CAA4C,CAAC,CAAC;4BAC9E,IAAI,CAAC,eAAe,8BAA4B,CAAC;yBACpD;;;;wBAMD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,kCAAkC,GAAG,GAAC,CAAC,CAAC;wBACxE,IAAI,CAAC,eAAe,oCAA+B,CAAC;wBACpD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;wBAC3B,sBAAO,OAAO,CAAC,MAAM,CAAC,GAAC,CAAC,EAAC;;;;;KAEhC;IAEa,+CAAsB,GAApC,UAAqC,GAAW;;;;;;6BAExC,IAAI,CAAC,kBAAkB,EAAvB,wBAAuB;wBACT,qBAAM,IAAI,CAAC,kBAAkB,EAAE;;wBAAvC,KAAK,GAAG,SAA+B;wBAC7C,IAAI,KAAK,EAAE;4BACP,OAAO;gCACH,GAAC,eAAe,IAAG,YAAU,KAAO;mCACvC,CAAC;yBACL;;;wBAGC,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;wBACnD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,kCAAgC,YAAY,MAAG,CAAC,CAAC;;;;wBAE5D,qBAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE;gCACtD,OAAO,EAAE,EAAE;gCACX,OAAO;6BACV,CAAC;;wBAHI,QAAQ,GAAG,SAGf;wBAEF,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE;4BAC7B,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oDAAkD,QAAQ,CAAC,UAAY,CAAC,CAAC,EAAC;yBAC7G;wBAEK,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAiB,CAAuB,CAAC;wBACvF,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,IAAI,iBAAiB,CAAC,gBAAgB,GAAG,CAAC,EAAE;4BAC/E,kDAAkD;4BAClD,2HAA2H;4BAC3H,iBAAiB,CAAC,eAAe,GAAG,iBAAiB,CAAC,YAAY,CAAC;yBACtE;wBACD,sBAAO,iBAAiB,EAAC;;;wBAEzB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,kDAAkD,GAAG,GAAC,CAAC,CAAC;wBACxF,sBAAO,OAAO,CAAC,MAAM,CAAC,GAAC,CAAC,EAAC;;;;;KAEhC;IAEO,yCAAgB,GAAxB,UAAyB,GAAW,EAAE,eAA0C;QAC5E,IAAI,CAAC,eAAe,EAAE;YAClB,OAAO,GAAG,CAAC;SACd;QAED,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAG,QAAM,eAAiB,EAAC;IACjF,CAAC;IAEa,wCAAe,GAA7B,UAA8B,GAAW,EAAE,kBAA8D,EAAE,iBAAqC,EAAE,uBAAuC;;;;;;wBACjL,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,CAAC,eAAe,CAAC,CAAC;6BAC3E,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,EAArC,wBAAqC;wBACrC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yEAAyE,CAAC,CAAC;wBAC3G,IAAI,CAAC,SAAS,GAAG,kBAAkB,CAAC;wBACpC,qBAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,uBAAuB,CAAC;;wBAA9D,SAA8D,CAAC;wBAE/D,IAAI,CAAC,YAAY,GAAG,iBAAiB,CAAC,YAAY,CAAC;wBACnD,sBAAO;;wBAGL,mBAAmB,GAAU,EAAE,CAAC;wBAChC,UAAU,GAAG,iBAAiB,CAAC,mBAAmB,IAAI,EAAE,CAAC;wBAC3D,SAAS,GAAmC,iBAAiB,CAAC;8BACjC,EAAV,yBAAU;;;6BAAV,yBAAU;wBAAtB,QAAQ;wBACT,gBAAgB,GAAG,IAAI,CAAC,uBAAuB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,uBAAuB,CAAC,CAAC;6BACzG,iBAAgB,YAAY,KAAK,GAAjC,wBAAiC;wBACjC,qFAAqF;wBACrF,mBAAmB,CAAC,IAAI,CAAI,QAAQ,CAAC,SAAS,iBAAY,gBAAkB,CAAC,CAAC;;;6BACvE,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,EAAnC,yBAAmC;wBAC1C,IAAI,CAAC,SAAS,GAAG,gBAAgB,CAAC;6BAC9B,CAAC,SAAS,EAAV,wBAAU;;;;wBAEM,qBAAM,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;;wBAAlD,SAAS,GAAG,SAAsC,CAAC;;;;wBAEnD,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAE,CAAC,EAAC;;wBAE9B,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;;;;wBAGnE,qBAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,uBAAuB,CAAC;;wBAA9D,SAA8D,CAAC;wBAC/D,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC;wBAC3C,sBAAO;;;wBAEP,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,oCAAkC,QAAQ,CAAC,SAAS,WAAM,IAAI,CAAC,CAAC;wBAChG,SAAS,GAAG,SAAS,CAAC;wBACtB,mBAAmB,CAAC,IAAI,CAAI,QAAQ,CAAC,SAAS,iBAAY,IAAI,CAAC,CAAC;wBAEhE,IAAI,IAAI,CAAC,eAAe,mCAA+B,EAAE;4BAC/C,OAAO,GAAG,sDAAsD,CAAC;4BACvE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;4BACzC,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAC;yBAC7C;;;wBA5BU,IAAU;;;wBAiCjC,IAAI,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE;4BAChC,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2EAAyE,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAG,CAAC,CAAC,EAAC;yBAC9I;wBACD,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC,EAAC;;;;KACnH;IAEO,2CAAkB,GAA1B,UAA2B,SAA4B;QACnD,QAAQ,SAAS,EAAE;YACf,KAAK,6DAAiB,CAAC,UAAU;gBAC7B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE;oBACzB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;iBACxE;gBACD,OAAO,IAAI,sEAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,IAAI,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC1J,KAAK,6DAAiB,CAAC,gBAAgB;gBACnC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;oBAC3B,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;iBAC1E;gBACD,OAAO,IAAI,oFAAyB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,IAAI,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YACnK,KAAK,6DAAiB,CAAC,WAAW;gBAC9B,OAAO,IAAI,0EAAoB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,IAAI,KAAK,CAAC,CAAC;YACpI;gBACI,MAAM,IAAI,KAAK,CAAC,wBAAsB,SAAS,MAAG,CAAC,CAAC;SAC3D;IACL,CAAC;IAEO,uCAAc,GAAtB,UAAuB,GAAW,EAAE,cAA8B;QAAlE,iBAIC;QAHG,IAAI,CAAC,SAAU,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3C,IAAI,CAAC,SAAU,CAAC,OAAO,GAAG,UAAC,CAAC,IAAK,YAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAtB,CAAsB,CAAC;QACxD,OAAO,IAAI,CAAC,SAAU,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACxD,CAAC;IAEO,gDAAuB,GAA/B,UAAgC,QAA6B,EAAE,kBAAiD,EAAE,uBAAuC;QACrJ,IAAM,SAAS,GAAG,6DAAiB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACxD,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,SAAS,EAAE;YAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yBAAuB,QAAQ,CAAC,SAAS,kDAA+C,CAAC,CAAC;YAC1H,OAAO,IAAI,KAAK,CAAC,yBAAuB,QAAQ,CAAC,SAAS,kDAA+C,CAAC,CAAC;SAC9G;aAAM;YACH,IAAI,gBAAgB,CAAC,kBAAkB,EAAE,SAAS,CAAC,EAAE;gBACjD,IAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC,GAAG,CAAC,UAAC,CAAC,IAAK,iEAAc,CAAC,CAAC,CAAC,EAAjB,CAAiB,CAAC,CAAC;gBAC/E,IAAI,eAAe,CAAC,OAAO,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE;oBACvD,IAAI,CAAC,SAAS,KAAK,6DAAiB,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;wBACvE,CAAC,SAAS,KAAK,6DAAiB,CAAC,gBAAgB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;wBACjF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yBAAuB,6DAAiB,CAAC,SAAS,CAAC,wDAAqD,CAAC,CAAC;wBAC1I,OAAO,IAAI,KAAK,CAAC,MAAI,6DAAiB,CAAC,SAAS,CAAC,4CAAyC,CAAC,CAAC;qBAC/F;yBAAM;wBACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,0BAAwB,6DAAiB,CAAC,SAAS,CAAC,OAAI,CAAC,CAAC;wBAC1F,IAAI;4BACA,OAAO,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;yBAC7C;wBAAC,OAAO,EAAE,EAAE;4BACT,OAAO,EAAE,CAAC;yBACb;qBACJ;iBACJ;qBAAM;oBACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yBAAuB,6DAAiB,CAAC,SAAS,CAAC,qEAAgE,0DAAc,CAAC,uBAAuB,CAAC,OAAI,CAAC,CAAC;oBAChM,OAAO,IAAI,KAAK,CAAC,MAAI,6DAAiB,CAAC,SAAS,CAAC,2BAAsB,0DAAc,CAAC,uBAAuB,CAAC,MAAG,CAAC,CAAC;iBACtH;aACJ;iBAAM;gBACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yBAAuB,6DAAiB,CAAC,SAAS,CAAC,6CAA0C,CAAC,CAAC;gBAC/H,OAAO,IAAI,KAAK,CAAC,MAAI,6DAAiB,CAAC,SAAS,CAAC,iCAA8B,CAAC,CAAC;aACpF;SACJ;IACL,CAAC;IAEO,qCAAY,GAApB,UAAqB,SAAc;QAC/B,OAAO,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,SAAS,IAAI,SAAS,CAAC;IAClF,CAAC;IAEO,uCAAc,GAAtB,UAAuB,KAAa;QAChC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,mCAAiC,KAAK,gCAA2B,IAAI,CAAC,eAAe,MAAG,CAAC,CAAC;QAE1H,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAE3B,gFAAgF;QAChF,KAAK,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAE3B,IAAI,IAAI,CAAC,eAAe,sCAAiC,EAAE;YACvD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,2CAAyC,KAAK,+EAA4E,CAAC,CAAC;YAC5J,OAAO;SACV;QAED,IAAI,IAAI,CAAC,eAAe,mCAA+B,EAAE;YACrD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,OAAO,EAAE,2CAAyC,KAAK,sFAAmF,CAAC,CAAC;YACrK,OAAO;SACV;QAED,IAAI,IAAI,CAAC,eAAe,wCAAkC,EAAE;YACxD,kFAAkF;YAClF,sFAAsF;YACtF,IAAI,CAAC,mBAAmB,EAAE,CAAC;SAC9B;QAED,IAAI,KAAK,EAAE;YACP,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,yCAAuC,KAAK,OAAI,CAAC,CAAC;SACrF;aAAM;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC,CAAC;SACrE;QAED,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAC9B,IAAI,CAAC,eAAe,oCAA+B,CAAC;QAEpD,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,iBAAiB,EAAE;YACxC,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;YAE/B,IAAI;gBACA,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;aACvB;YAAC,OAAO,CAAC,EAAE;gBACR,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,4BAA0B,KAAK,uBAAkB,CAAC,OAAI,CAAC,CAAC;aAC3F;SACJ;IACL,CAAC;IAEO,mCAAU,GAAlB,UAAmB,GAAW;QAC1B,oCAAoC;QACpC,IAAI,GAAG,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE;YAC7E,OAAO,GAAG,CAAC;SACd;QAED,IAAI,CAAC,+CAAQ,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,qBAAmB,GAAG,OAAI,CAAC,CAAC;SAC/C;QAED,6EAA6E;QAC7E,kCAAkC;QAClC,wEAAwE;QACxE,2EAA2E;QAC3E,mGAAmG;QACnG,IAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAEhB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,kBAAgB,GAAG,cAAS,IAAI,CAAC,IAAI,OAAI,CAAC,CAAC;QACjF,OAAO,IAAI,CAAC,IAAI,CAAC;IACrB,CAAC;IAEO,4CAAmB,GAA3B,UAA4B,GAAW;QACnC,IAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,YAAY,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACvE,IAAI,YAAY,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE;YAC/C,YAAY,IAAI,GAAG,CAAC;SACvB;QACD,YAAY,IAAI,WAAW,CAAC;QAC5B,YAAY,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAEzD,IAAI,YAAY,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE;YACjD,YAAY,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACzC,YAAY,IAAI,mBAAmB,GAAG,IAAI,CAAC,gBAAgB,CAAC;SAC/D;QACD,OAAO,YAAY,CAAC;IACxB,CAAC;IACL,qBAAC;AAAD,CAAC;;AAED,0BAA0B,kBAAiD,EAAE,eAAkC;IAC3G,OAAO,CAAC,kBAAkB,IAAI,CAAC,CAAC,eAAe,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,eAAe;AACf;IAOI,4BAA6B,SAAqB;QAArB,cAAS,GAAT,SAAS,CAAY;QAN1C,WAAM,GAAU,EAAE,CAAC;QAEnB,cAAS,GAAY,IAAI,CAAC;QAK9B,IAAI,CAAC,gBAAgB,GAAG,IAAI,aAAa,EAAE,CAAC;QAC5C,IAAI,CAAC,eAAe,GAAG,IAAI,aAAa,EAAE,CAAC;QAE3C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3C,CAAC;IAEM,iCAAI,GAAX,UAAY,IAA0B;QAClC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE;YACvB,IAAI,CAAC,eAAe,GAAG,IAAI,aAAa,EAAE,CAAC;SAC9C;QACD,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;IACxC,CAAC;IAEM,iCAAI,GAAX;QACI,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAEO,uCAAU,GAAlB,UAAmB,IAA0B;QACzC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,OAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,OAAM,CAAC,IAAI,CAAC,EAAE;YAC/D,MAAM,IAAI,KAAK,CAAC,iCAA+B,OAAM,CAAC,IAAI,CAAC,MAAM,CAAC,yBAAoB,OAAM,CAAC,IAAI,CAAG,CAAC,CAAC;SACzG;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;IACpC,CAAC;IAEa,qCAAQ,GAAtB;;;;;;iCACe,EAAE;wBACT,qBAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO;;wBAAnC,SAAmC,CAAC;wBAEpC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;4BACjB,IAAI,IAAI,CAAC,eAAe,EAAE;gCACtB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;6BACtD;4BAED,wBAAM;yBACT;wBAED,IAAI,CAAC,gBAAgB,GAAG,IAAI,aAAa,EAAE,CAAC;wBAEtC,eAAe,GAAG,IAAI,CAAC,eAAgB,CAAC;wBAC9C,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;wBAE3B,IAAI,GAAG,OAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;4BAC9C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;4BACtB,kBAAkB,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;wBAElD,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;;;;wBAGnB,qBAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;;wBAA/B,SAA+B,CAAC;wBAChC,eAAe,CAAC,OAAO,EAAE,CAAC;;;;wBAE1B,eAAe,CAAC,MAAM,CAAC,OAAK,CAAC,CAAC;;;;;;;KAGzC;IAEc,gCAAa,GAA5B,UAA6B,YAA2B;QACpD,IAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,UAAC,CAAC,IAAK,QAAC,CAAC,UAAU,EAAZ,CAAY,CAAC,CAAC,MAAM,CAAC,UAAC,CAAC,EAAE,CAAC,IAAK,QAAC,GAAG,CAAC,EAAL,CAAK,CAAC,CAAC;QAClF,IAAM,MAAM,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAmB,UAAY,EAAZ,6BAAY,EAAZ,0BAAY,EAAZ,IAAY,EAAE;YAA5B,IAAM,IAAI;YACX,MAAM,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YACzC,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC;SAC7B;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;IACL,yBAAC;AAAD,CAAC;;AAED;IAKI;QAAA,iBAEC;QADG,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC,UAAC,OAAO,EAAE,MAAM;;YAAK,6BAAkD,EAAjD,sBAAa,EAAE,sBAAa;QAA7B,CAAkD,CAAC,CAAC;IACxG,CAAC;IAEM,+BAAO,GAAd;QACI,IAAI,CAAC,QAAS,EAAE,CAAC;IACrB,CAAC;IAEM,8BAAM,GAAb,UAAc,MAAY;QACtB,IAAI,CAAC,QAAS,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IACL,oBAAC;AAAD,CAAC;;;;;;;;ACxoBD;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAE/G,6FAA6F;AAC7F,gDAAgD;AAChD,IAAY,iBASX;AATD,WAAY,iBAAiB;IACzB,yCAAyC;IACzC,yDAAQ;IACR,0CAA0C;IAC1C,qEAAc;IACd,kDAAkD;IAClD,iFAAoB;IACpB,4CAA4C;IAC5C,uEAAe;AACnB,CAAC,EATW,iBAAiB,KAAjB,iBAAiB,QAS5B;AAED,sDAAsD;AACtD,IAAY,cAKX;AALD,WAAY,cAAc;IACtB,6EAA6E;IAC7E,mDAAQ;IACR,0EAA0E;IAC1E,uDAAU;AACd,CAAC,EALW,cAAc,KAAd,cAAc,QAKzB;;;;;;;;ACtBD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE3D;AACD;AAEL;AACY;AACA;AAE1D,oDAAoD;AACpD,eAAe;AACf;IAoBI,8BAAY,UAAsB,EAAE,kBAAgE,EAAE,MAAe,EAAE,iBAA0B;QAC7I,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,IAAI,gEAAe,EAAE,CAAC;QACvC,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAE3C,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;IAfD,sBAAW,6CAAW;QADtB,uFAAuF;aACvF;YACI,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;QAClC,CAAC;;;OAAA;IAeY,sCAAO,GAApB,UAAqB,GAAW,EAAE,cAA8B;;;;;;wBAC5D,0CAAG,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;wBAC3B,0CAAG,CAAC,UAAU,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;wBACjD,0CAAG,CAAC,IAAI,CAAC,cAAc,EAAE,0DAAc,EAAE,gBAAgB,CAAC,CAAC;wBAE3D,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;wBAEf,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,qCAAqC,CAAC,CAAC;wBAEvE,4HAA4H;wBAC5H,IAAI,cAAc,KAAK,0DAAc,CAAC,MAAM;4BACxC,CAAC,OAAO,cAAc,KAAK,WAAW,IAAI,OAAO,IAAI,cAAc,EAAE,CAAC,YAAY,KAAK,QAAQ,CAAC,EAAE;4BAClG,MAAM,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAC;yBACjH;wBAEK,WAAW,GAAgB;4BAC7B,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM;4BAClC,OAAO,EAAE,EAAE;4BACX,OAAO,EAAE,MAAM;yBAClB,CAAC;wBAEF,IAAI,cAAc,KAAK,0DAAc,CAAC,MAAM,EAAE;4BAC1C,WAAW,CAAC,YAAY,GAAG,aAAa,CAAC;yBAC5C;wBAEa,qBAAM,IAAI,CAAC,cAAc,EAAE;;wBAAnC,KAAK,GAAG,SAA2B;wBACzC,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;wBAIrC,OAAO,GAAM,GAAG,WAAM,IAAI,CAAC,GAAG,EAAI,CAAC;wBACzC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,sCAAoC,OAAO,MAAG,CAAC,CAAC;wBAC/D,qBAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC;;wBAA1D,QAAQ,GAAG,SAA+C;wBAChE,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE;4BAC7B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,uDAAqD,QAAQ,CAAC,UAAU,MAAG,CAAC,CAAC;4BAE7G,mFAAmF;4BACnF,IAAI,CAAC,UAAU,GAAG,IAAI,iDAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,EAAE,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;4BAChF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;yBACxB;6BAAM;4BACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;yBACvB;wBAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;;;;;KACrD;IAEa,6CAAc,GAA5B;;;;;6BACQ,IAAI,CAAC,kBAAkB,EAAvB,wBAAuB;wBAChB,qBAAM,IAAI,CAAC,kBAAkB,EAAE;4BAAtC,sBAAO,SAA+B,EAAC;4BAG3C,sBAAO,IAAI,EAAC;;;;KACf;IAEO,gDAAiB,GAAzB,UAA0B,OAAoB,EAAE,KAAoB;QAChE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YAClB,OAAO,CAAC,OAAO,GAAG,EAAE,CAAC;SACxB;QACD,IAAI,KAAK,EAAE;YACP,6CAA6C;YAC7C,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,YAAU,KAAO,CAAC;YACrD,OAAO;SACV;QACD,6CAA6C;QAC7C,IAAI,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE;YAClC,6CAA6C;YAC7C,OAAO,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;SAC3C;IACL,CAAC;IAEa,mCAAI,GAAlB,UAAmB,GAAW,EAAE,WAAwB;;;;;;;;;6BAEzC,IAAI,CAAC,OAAO;wBAED,qBAAM,IAAI,CAAC,cAAc,EAAE;;wBAAnC,KAAK,GAAG,SAA2B;wBACzC,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;;;;wBAGjC,OAAO,GAAM,GAAG,WAAM,IAAI,CAAC,GAAG,EAAI,CAAC;wBACzC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,sCAAoC,OAAO,MAAG,CAAC,CAAC;wBAC/D,qBAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC;;wBAA1D,QAAQ,GAAG,SAA+C;wBAEhE,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE;4BAC7B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,oDAAoD,CAAC,CAAC;4BAE5F,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;yBACxB;6BAAM,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE;4BACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,uDAAqD,QAAQ,CAAC,UAAU,MAAG,CAAC,CAAC;4BAE7G,yBAAyB;4BACzB,IAAI,CAAC,UAAU,GAAG,IAAI,iDAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,EAAE,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;4BAChF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;yBACxB;6BAAM;4BACH,uBAAuB;4BACvB,IAAI,QAAQ,CAAC,OAAO,EAAE;gCAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,4CAA0C,4DAAa,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,CAAC,MAAG,CAAC,CAAC;gCACtI,IAAI,IAAI,CAAC,SAAS,EAAE;oCAChB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;iCACpC;6BACJ;iCAAM;gCACH,wCAAwC;gCACxC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,oDAAoD,CAAC,CAAC;6BACzF;yBACJ;;;;wBAED,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;4BACf,qDAAqD;4BACrD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,0DAAwD,GAAC,CAAC,OAAS,CAAC,CAAC;yBACxG;6BAAM;4BACH,IAAI,GAAC,YAAY,oDAAY,EAAE;gCAC3B,wCAAwC;gCACxC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,oDAAoD,CAAC,CAAC;6BACzF;iCAAM;gCACH,qDAAqD;gCACrD,IAAI,CAAC,UAAU,GAAG,GAAC,CAAC;gCACpB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;6BACxB;yBACJ;;;;;wBAIT,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,2CAA2C,CAAC,CAAC;wBAE7E,gHAAgH;wBAChH,2HAA2H;wBAC3H,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;4BACnB,IAAI,CAAC,YAAY,EAAE,CAAC;yBACvB;;;;;;KAER;IAEY,mCAAI,GAAjB,UAAkB,IAAS;;;gBACvB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;oBACf,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC,EAAC;iBACpF;gBACD,sBAAO,0DAAW,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAAC;;;KACrI;IAEY,mCAAI,GAAjB;;;;;;wBACI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,2CAA2C,CAAC,CAAC;wBAE7E,yFAAyF;wBACzF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;wBACrB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;;;;wBAGnB,qBAAM,IAAI,CAAC,SAAS;;wBAApB,SAAoB,CAAC;wBAErB,qDAAqD;wBACrD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,uDAAqD,IAAI,CAAC,GAAG,MAAG,CAAC,CAAC;wBAE5F,aAAa,GAAgB;4BAC/B,OAAO,EAAE,EAAE;yBACd,CAAC;wBACY,qBAAM,IAAI,CAAC,cAAc,EAAE;;wBAAnC,KAAK,GAAG,SAA2B;wBACzC,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;wBAC7C,qBAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAI,EAAE,aAAa,CAAC;;wBAAtD,SAAsD,CAAC;wBAEvD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,8CAA8C,CAAC,CAAC;;;wBAEhF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,wCAAwC,CAAC,CAAC;wBAE1E,+CAA+C;wBAC/C,sDAAsD;wBACtD,IAAI,CAAC,YAAY,EAAE,CAAC;;;;;;KAE3B;IAEO,2CAAY,GAApB;QACI,IAAI,IAAI,CAAC,OAAO,EAAE;YACd,IAAI,UAAU,GAAG,+CAA+C,CAAC;YACjE,IAAI,IAAI,CAAC,UAAU,EAAE;gBACjB,UAAU,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;aAC9C;YACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;SACjC;IACL,CAAC;IACL,2BAAC;AAAD,CAAC;;;;;;;;;AC/ND;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAE/G,qFAAqF;AACrF,0FAA0F;AAC1F,iCAAiC;AAEjC,2BAA2B;AAC3B,eAAe;AACf;IAAA;QACY,cAAS,GAAY,KAAK,CAAC;QAC5B,YAAO,GAAwB,IAAI,CAAC;IAkB/C,CAAC;IAhBU,+BAAK,GAAZ;QACI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACjB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,IAAI,CAAC,OAAO,EAAE;gBACd,IAAI,CAAC,OAAO,EAAE,CAAC;aAClB;SACJ;IACL,CAAC;IAED,sBAAI,mCAAM;aAAV;YACI,OAAO,IAAI,CAAC;QAChB,CAAC;;;OAAA;IAED,sBAAI,oCAAO;aAAX;YACI,OAAO,IAAI,CAAC,SAAS,CAAC;QAC1B,CAAC;;;OAAA;IACL,sBAAC;AAAD,CAAC;;;;;;;;;AC7BD;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjE;AACY;AAEU;AAEpE,eAAe;AACf;IAYI,mCAAY,UAAsB,EAAE,kBAAgE,EAAE,MAAe,EACzG,iBAA0B,EAAE,sBAA8C;QAClF,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,sBAAsB,GAAG,sBAAsB,CAAC;QAErD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;IAEY,2CAAO,GAApB,UAAqB,GAAW,EAAE,cAA8B;;;;;;;wBAC5D,0CAAG,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;wBAC3B,0CAAG,CAAC,UAAU,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;wBACjD,0CAAG,CAAC,IAAI,CAAC,cAAc,EAAE,0DAAc,EAAE,gBAAgB,CAAC,CAAC;wBAE3D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;wBAE/D,sIAAsI;wBACtI,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;6BAEX,IAAI,CAAC,kBAAkB,EAAvB,wBAAuB;wBACT,qBAAM,IAAI,CAAC,kBAAkB,EAAE;;wBAAvC,KAAK,GAAG,SAA+B;wBAC7C,IAAI,KAAK,EAAE;4BACP,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAG,kBAAgB,kBAAkB,CAAC,KAAK,CAAG,EAAC;yBAC3F;;4BAGL,sBAAO,IAAI,OAAO,CAAO,UAAC,OAAO,EAAE,MAAM;4BACrC,IAAI,MAAM,GAAG,KAAK,CAAC;4BACnB,IAAI,cAAc,KAAK,0DAAc,CAAC,IAAI,EAAE;gCACxC,MAAM,CAAC,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC,CAAC;gCAC/F,OAAO;6BACV;4BAED,IAAI,WAAwB,CAAC;4BAC7B,IAAI,+CAAQ,CAAC,SAAS,IAAI,+CAAQ,CAAC,WAAW,EAAE;gCAC5C,WAAW,GAAG,IAAI,KAAI,CAAC,sBAAsB,CAAC,GAAG,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;6BACjF;iCAAM;gCACH,gDAAgD;gCAChD,IAAM,OAAO,GAAG,KAAI,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;gCACrD,WAAW,GAAG,IAAI,KAAI,CAAC,sBAAsB,CAAC,GAAG,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAqB,CAAC,CAAC;6BAClI;4BAED,IAAI;gCACA,WAAW,CAAC,SAAS,GAAG,UAAC,CAAe;oCACpC,IAAI,KAAI,CAAC,SAAS,EAAE;wCAChB,IAAI;4CACA,KAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,oCAAkC,4DAAa,CAAC,CAAC,CAAC,IAAI,EAAE,KAAI,CAAC,iBAAiB,CAAC,MAAG,CAAC,CAAC;4CACpH,KAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;yCAC1B;wCAAC,OAAO,KAAK,EAAE;4CACZ,KAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;4CAClB,OAAO;yCACV;qCACJ;gCACL,CAAC,CAAC;gCAEF,WAAW,CAAC,OAAO,GAAG,UAAC,CAAe;oCAClC,IAAM,KAAK,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,gBAAgB,CAAC,CAAC;oCACpD,IAAI,MAAM,EAAE;wCACR,KAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;qCACrB;yCAAM;wCACH,MAAM,CAAC,KAAK,CAAC,CAAC;qCACjB;gCACL,CAAC,CAAC;gCAEF,WAAW,CAAC,MAAM,GAAG;oCACjB,KAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,sBAAoB,KAAI,CAAC,GAAK,CAAC,CAAC;oCACtE,KAAI,CAAC,WAAW,GAAG,WAAW,CAAC;oCAC/B,MAAM,GAAG,IAAI,CAAC;oCACd,OAAO,EAAE,CAAC;gCACd,CAAC,CAAC;6BACL;4BAAC,OAAO,CAAC,EAAE;gCACR,MAAM,CAAC,CAAC,CAAC,CAAC;gCACV,OAAO;6BACV;wBACL,CAAC,CAAC,EAAC;;;;KACN;IAEY,wCAAI,GAAjB,UAAkB,IAAS;;;gBACvB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;oBACnB,sBAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC,EAAC;iBACpF;gBACD,sBAAO,0DAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAAC;;;KAC7H;IAEM,wCAAI,GAAX;QACI,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAEO,yCAAK,GAAb,UAAc,CAAS;QACnB,IAAI,IAAI,CAAC,WAAW,EAAE;YAClB,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YAE7B,IAAI,IAAI,CAAC,OAAO,EAAE;gBACd,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;aACnB;SACJ;IACL,CAAC;IACL,gCAAC;AAAD,CAAC;;;;;;;;;AC5HD;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjE;AACY;AAEH;AAEvD,eAAe;AACf;IAWI,4BAAY,UAAsB,EAAE,kBAAgE,EAAE,MAAe,EACzG,iBAA0B,EAAE,oBAA0C;QAC9E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAC;QACjD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAE7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;IAEY,oCAAO,GAApB,UAAqB,GAAW,EAAE,cAA8B;;;;;;;wBAC5D,0CAAG,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;wBAC3B,0CAAG,CAAC,UAAU,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;wBACjD,0CAAG,CAAC,IAAI,CAAC,cAAc,EAAE,0DAAc,EAAE,gBAAgB,CAAC,CAAC;wBAE3D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,oCAAoC,CAAC,CAAC;6BAElE,IAAI,CAAC,kBAAkB,EAAvB,wBAAuB;wBACT,qBAAM,IAAI,CAAC,kBAAkB,EAAE;;wBAAvC,KAAK,GAAG,SAA+B;wBAC7C,IAAI,KAAK,EAAE;4BACP,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAG,kBAAgB,kBAAkB,CAAC,KAAK,CAAG,EAAC;yBAC3F;;4BAGL,sBAAO,IAAI,OAAO,CAAO,UAAC,OAAO,EAAE,MAAM;4BACrC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;4BACjC,IAAI,SAAgC,CAAC;4BACrC,IAAM,OAAO,GAAG,KAAI,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;4BACrD,IAAI,MAAM,GAAG,KAAK,CAAC;4BAEnB,IAAI,+CAAQ,CAAC,MAAM,IAAI,OAAO,EAAE;gCAC5B,qDAAqD;gCACrD,SAAS,GAAG,IAAI,KAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,SAAS,EAAE;oCACtD,OAAO,EAAE;wCACL,MAAM,EAAE,KAAG,OAAS;qCACvB;iCACJ,CAAC,CAAC;6BACN;4BAED,IAAI,CAAC,SAAS,EAAE;gCACZ,2DAA2D;gCAC3D,SAAS,GAAG,IAAI,KAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;6BAClD;4BAED,IAAI,cAAc,KAAK,0DAAc,CAAC,MAAM,EAAE;gCAC1C,SAAS,CAAC,UAAU,GAAG,aAAa,CAAC;6BACxC;4BAED,yCAAyC;4BACzC,SAAS,CAAC,MAAM,GAAG,UAAC,MAAa;gCAC7B,KAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,4BAA0B,GAAG,MAAG,CAAC,CAAC;gCACxE,KAAI,CAAC,SAAS,GAAG,SAAS,CAAC;gCAC3B,MAAM,GAAG,IAAI,CAAC;gCACd,OAAO,EAAE,CAAC;4BACd,CAAC,CAAC;4BAEF,SAAS,CAAC,OAAO,GAAG,UAAC,KAAY;gCAC7B,IAAI,KAAK,GAAQ,IAAI,CAAC;gCACtB,wFAAwF;gCACxF,IAAI,OAAO,UAAU,KAAK,WAAW,IAAI,KAAK,YAAY,UAAU,EAAE;oCAClE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;iCACvB;qCAAM;oCACH,KAAK,GAAG,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;iCAC/D;gCAED,MAAM,CAAC,KAAK,CAAC,CAAC;4BAClB,CAAC,CAAC;4BAEF,SAAS,CAAC,SAAS,GAAG,UAAC,OAAqB;gCACxC,KAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,2CAAyC,4DAAa,CAAC,OAAO,CAAC,IAAI,EAAE,KAAI,CAAC,iBAAiB,CAAC,MAAG,CAAC,CAAC;gCACjI,IAAI,KAAI,CAAC,SAAS,EAAE;oCAChB,KAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;iCAChC;4BACL,CAAC,CAAC;4BAEF,SAAS,CAAC,OAAO,GAAG,UAAC,KAAiB;gCAClC,+DAA+D;gCAC/D,wCAAwC;gCACxC,IAAI,MAAM,EAAE;oCACR,KAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;iCACrB;qCAAM;oCACH,IAAI,KAAK,GAAQ,IAAI,CAAC;oCACtB,wFAAwF;oCACxF,IAAI,OAAO,UAAU,KAAK,WAAW,IAAI,KAAK,YAAY,UAAU,EAAE;wCAClE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;qCACvB;yCAAM;wCACH,KAAK,GAAG,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;qCAC/D;oCAED,MAAM,CAAC,KAAK,CAAC,CAAC;iCACjB;4BACL,CAAC,CAAC;wBACN,CAAC,CAAC,EAAC;;;;KACN;IAEM,iCAAI,GAAX,UAAY,IAAS;QACjB,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,KAAK,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE;YAChF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,0CAAwC,4DAAa,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,CAAC,MAAG,CAAC,CAAC;YACxH,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;SAC5B;QAED,OAAO,OAAO,CAAC,MAAM,CAAC,oCAAoC,CAAC,CAAC;IAChE,CAAC;IAEM,iCAAI,GAAX;QACI,IAAI,IAAI,CAAC,SAAS,EAAE;YAChB,4EAA4E;YAC5E,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,cAAO,CAAC,CAAC;YAClC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,cAAO,CAAC,CAAC;YACpC,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,cAAO,CAAC,CAAC;YAClC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;YAE3B,6GAA6G;YAC7G,iHAAiH;YACjH,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;SACzB;QAED,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAEO,kCAAK,GAAb,UAAc,KAAkB;QAC5B,qEAAqE;QACrE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,KAAK,EAAE,uCAAuC,CAAC,CAAC;QACzE,IAAI,IAAI,CAAC,OAAO,EAAE;YACd,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE;gBAC5D,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,wCAAsC,KAAK,CAAC,IAAI,UAAK,KAAK,CAAC,MAAM,OAAI,CAAC,CAAC,CAAC;aAClG;iBAAM;gBACH,IAAI,CAAC,OAAO,EAAE,CAAC;aAClB;SACJ;IACL,CAAC;IACL,yBAAC;AAAD,CAAC;;;;;;;;;AC5JD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sDAAsD;AACtD,+GAA+G;AAEiB;AAClF;AACA;AACP;AACiB;AAExD,IAAM,sBAAsB,GAAW,MAAM,CAAC;AAE9C,wCAAwC;AACxC;IAAA;QAEI,kBAAkB;QACF,SAAI,GAAW,sBAAsB,CAAC;QACtD,kBAAkB;QACF,YAAO,GAAW,CAAC,CAAC;QAEpC,kBAAkB;QACF,mBAAc,GAAmB,0DAAc,CAAC,IAAI,CAAC;IAmGzE,CAAC;IAjGG;;;;OAIG;IACI,uCAAa,GAApB,UAAqB,KAAa,EAAE,MAAe;QAC/C,2HAA2H;QAC3H,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;SAC9E;QAED,IAAI,CAAC,KAAK,EAAE;YACR,OAAO,EAAE,CAAC;SACb;QAED,IAAI,MAAM,KAAK,IAAI,EAAE;YACjB,MAAM,GAAG,mDAAU,CAAC,QAAQ,CAAC;SAChC;QAED,qBAAqB;QACrB,IAAM,QAAQ,GAAG,oEAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEhD,IAAM,WAAW,GAAG,EAAE,CAAC;QACvB,KAAsB,UAAQ,EAAR,qBAAQ,EAAR,sBAAQ,EAAR,IAAQ,EAAE;YAA3B,IAAM,OAAO;YACd,IAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAe,CAAC;YACxD,IAAI,OAAO,aAAa,CAAC,IAAI,KAAK,QAAQ,EAAE;gBACxC,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;aACvC;YACD,QAAQ,aAAa,CAAC,IAAI,EAAE;gBACxB,KAAK,yDAAW,CAAC,UAAU;oBACvB,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;oBACxC,MAAM;gBACV,KAAK,yDAAW,CAAC,UAAU;oBACvB,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;oBACxC,MAAM;gBACV,KAAK,yDAAW,CAAC,UAAU;oBACvB,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;oBACxC,MAAM;gBACV,KAAK,yDAAW,CAAC,IAAI;oBACjB,oCAAoC;oBACpC,MAAM;gBACV,KAAK,yDAAW,CAAC,KAAK;oBAClB,2CAA2C;oBAC3C,MAAM;gBACV;oBACI,6EAA6E;oBAC7E,MAAM,CAAC,GAAG,CAAC,iDAAQ,CAAC,WAAW,EAAE,wBAAwB,GAAG,aAAa,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC;oBAC/F,SAAS;aAChB;YACD,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;SACnC;QAED,OAAO,WAAW,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACI,sCAAY,GAAnB,UAAoB,OAAmB;QACnC,OAAO,oEAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;IAEO,6CAAmB,GAA3B,UAA4B,OAA0B;QAClD,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,MAAM,EAAE,yCAAyC,CAAC,CAAC;QAErF,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE;YACpC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,YAAY,EAAE,yCAAyC,CAAC,CAAC;SAC9F;IACL,CAAC;IAEO,6CAAmB,GAA3B,UAA4B,OAA0B;QAClD,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,YAAY,EAAE,yCAAyC,CAAC,CAAC;QAE3F,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE;YAC5B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;SAC9D;IACL,CAAC;IAEO,6CAAmB,GAA3B,UAA4B,OAA0B;QAClD,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,EAAE;YACjC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;SAC9D;QAED,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,EAAE;YAClC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,yCAAyC,CAAC,CAAC;SACvF;QAED,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,YAAY,EAAE,yCAAyC,CAAC,CAAC;IAC/F,CAAC;IAEO,8CAAoB,GAA5B,UAA6B,KAAU,EAAE,YAAoB;QACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,EAAE;YAC3C,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;SACjC;IACL,CAAC;IACL,sBAAC;AAAD,CAAC","file":"signalr.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"signalR\"] = factory();\n\telse\n\t\troot[\"signalR\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\n// This is where we add any polyfills we'll need for the browser. It is the entry module for browser-specific builds.\r\n\r\nimport \"es6-promise/dist/es6-promise.auto.js\";\r\n\r\n// Copy from Array.prototype into Uint8Array to polyfill on IE. It's OK because the implementations of indexOf and slice use properties\r\n// that exist on Uint8Array with the same name, and JavaScript is magic.\r\n// We make them 'writable' because the Buffer polyfill messes with it as well.\r\nif (!Uint8Array.prototype.indexOf) {\r\n Object.defineProperty(Uint8Array.prototype, \"indexOf\", {\r\n value: Array.prototype.indexOf,\r\n writable: true,\r\n });\r\n}\r\nif (!Uint8Array.prototype.slice) {\r\n Object.defineProperty(Uint8Array.prototype, \"slice\", {\r\n // wrap the slice in Uint8Array so it looks like a Uint8Array.slice call\r\n // tslint:disable-next-line:object-literal-shorthand\r\n value: function(start?: number, end?: number) { return new Uint8Array(Array.prototype.slice.call(this, start, end)); },\r\n writable: true,\r\n });\r\n}\r\nif (!Uint8Array.prototype.forEach) {\r\n Object.defineProperty(Uint8Array.prototype, \"forEach\", {\r\n value: Array.prototype.forEach,\r\n writable: true,\r\n });\r\n}\r\n\r\nexport * from \"./index\";\r\n","/*!\n * @overview es6-promise - a tiny implementation of Promises/A+.\n * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)\n * @license Licensed under MIT license\n * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE\n * @version v4.2.2+97478eb6\n */\n\n(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n\ttypeof define === 'function' && define.amd ? define(factory) :\n\t(global.ES6Promise = factory());\n}(this, (function () { 'use strict';\n\nfunction objectOrFunction(x) {\n var type = typeof x;\n return x !== null && (type === 'object' || type === 'function');\n}\n\nfunction isFunction(x) {\n return typeof x === 'function';\n}\n\n\n\nvar _isArray = void 0;\nif (Array.isArray) {\n _isArray = Array.isArray;\n} else {\n _isArray = function (x) {\n return Object.prototype.toString.call(x) === '[object Array]';\n };\n}\n\nvar isArray = _isArray;\n\nvar len = 0;\nvar vertxNext = void 0;\nvar customSchedulerFn = void 0;\n\nvar asap = function asap(callback, arg) {\n queue[len] = callback;\n queue[len + 1] = arg;\n len += 2;\n if (len === 2) {\n // If len is 2, that means that we need to schedule an async flush.\n // If additional callbacks are queued before the queue is flushed, they\n // will be processed by this flush that we are scheduling.\n if (customSchedulerFn) {\n customSchedulerFn(flush);\n } else {\n scheduleFlush();\n }\n }\n};\n\nfunction setScheduler(scheduleFn) {\n customSchedulerFn = scheduleFn;\n}\n\nfunction setAsap(asapFn) {\n asap = asapFn;\n}\n\nvar browserWindow = typeof window !== 'undefined' ? window : undefined;\nvar browserGlobal = browserWindow || {};\nvar BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;\nvar isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';\n\n// test for web worker but not in IE10\nvar isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';\n\n// node\nfunction useNextTick() {\n // node version 0.10.x displays a deprecation warning when nextTick is used recursively\n // see https://github.com/cujojs/when/issues/410 for details\n return function () {\n return process.nextTick(flush);\n };\n}\n\n// vertx\nfunction useVertxTimer() {\n if (typeof vertxNext !== 'undefined') {\n return function () {\n vertxNext(flush);\n };\n }\n\n return useSetTimeout();\n}\n\nfunction useMutationObserver() {\n var iterations = 0;\n var observer = new BrowserMutationObserver(flush);\n var node = document.createTextNode('');\n observer.observe(node, { characterData: true });\n\n return function () {\n node.data = iterations = ++iterations % 2;\n };\n}\n\n// web worker\nfunction useMessageChannel() {\n var channel = new MessageChannel();\n channel.port1.onmessage = flush;\n return function () {\n return channel.port2.postMessage(0);\n };\n}\n\nfunction useSetTimeout() {\n // Store setTimeout reference so es6-promise will be unaffected by\n // other code modifying setTimeout (like sinon.useFakeTimers())\n var globalSetTimeout = setTimeout;\n return function () {\n return globalSetTimeout(flush, 1);\n };\n}\n\nvar queue = new Array(1000);\nfunction flush() {\n for (var i = 0; i < len; i += 2) {\n var callback = queue[i];\n var arg = queue[i + 1];\n\n callback(arg);\n\n queue[i] = undefined;\n queue[i + 1] = undefined;\n }\n\n len = 0;\n}\n\nfunction attemptVertx() {\n try {\n var r = require;\n var vertx = r('vertx');\n vertxNext = vertx.runOnLoop || vertx.runOnContext;\n return useVertxTimer();\n } catch (e) {\n return useSetTimeout();\n }\n}\n\nvar scheduleFlush = void 0;\n// Decide what async method to use to triggering processing of queued callbacks:\nif (isNode) {\n scheduleFlush = useNextTick();\n} else if (BrowserMutationObserver) {\n scheduleFlush = useMutationObserver();\n} else if (isWorker) {\n scheduleFlush = useMessageChannel();\n} else if (browserWindow === undefined && typeof require === 'function') {\n scheduleFlush = attemptVertx();\n} else {\n scheduleFlush = useSetTimeout();\n}\n\nfunction then(onFulfillment, onRejection) {\n var parent = this;\n\n var child = new this.constructor(noop);\n\n if (child[PROMISE_ID] === undefined) {\n makePromise(child);\n }\n\n var _state = parent._state;\n\n\n if (_state) {\n var callback = arguments[_state - 1];\n asap(function () {\n return invokeCallback(_state, child, callback, parent._result);\n });\n } else {\n subscribe(parent, child, onFulfillment, onRejection);\n }\n\n return child;\n}\n\n/**\n `Promise.resolve` returns a promise that will become resolved with the\n passed `value`. It is shorthand for the following:\n\n ```javascript\n let promise = new Promise(function(resolve, reject){\n resolve(1);\n });\n\n promise.then(function(value){\n // value === 1\n });\n ```\n\n Instead of writing the above, your code now simply becomes the following:\n\n ```javascript\n let promise = Promise.resolve(1);\n\n promise.then(function(value){\n // value === 1\n });\n ```\n\n @method resolve\n @static\n @param {Any} value value that the returned promise will be resolved with\n Useful for tooling.\n @return {Promise} a promise that will become fulfilled with the given\n `value`\n*/\nfunction resolve$1(object) {\n /*jshint validthis:true */\n var Constructor = this;\n\n if (object && typeof object === 'object' && object.constructor === Constructor) {\n return object;\n }\n\n var promise = new Constructor(noop);\n resolve(promise, object);\n return promise;\n}\n\nvar PROMISE_ID = Math.random().toString(36).substring(16);\n\nfunction noop() {}\n\nvar PENDING = void 0;\nvar FULFILLED = 1;\nvar REJECTED = 2;\n\nvar GET_THEN_ERROR = new ErrorObject();\n\nfunction selfFulfillment() {\n return new TypeError(\"You cannot resolve a promise with itself\");\n}\n\nfunction cannotReturnOwn() {\n return new TypeError('A promises callback cannot return that same promise.');\n}\n\nfunction getThen(promise) {\n try {\n return promise.then;\n } catch (error) {\n GET_THEN_ERROR.error = error;\n return GET_THEN_ERROR;\n }\n}\n\nfunction tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {\n try {\n then$$1.call(value, fulfillmentHandler, rejectionHandler);\n } catch (e) {\n return e;\n }\n}\n\nfunction handleForeignThenable(promise, thenable, then$$1) {\n asap(function (promise) {\n var sealed = false;\n var error = tryThen(then$$1, thenable, function (value) {\n if (sealed) {\n return;\n }\n sealed = true;\n if (thenable !== value) {\n resolve(promise, value);\n } else {\n fulfill(promise, value);\n }\n }, function (reason) {\n if (sealed) {\n return;\n }\n sealed = true;\n\n reject(promise, reason);\n }, 'Settle: ' + (promise._label || ' unknown promise'));\n\n if (!sealed && error) {\n sealed = true;\n reject(promise, error);\n }\n }, promise);\n}\n\nfunction handleOwnThenable(promise, thenable) {\n if (thenable._state === FULFILLED) {\n fulfill(promise, thenable._result);\n } else if (thenable._state === REJECTED) {\n reject(promise, thenable._result);\n } else {\n subscribe(thenable, undefined, function (value) {\n return resolve(promise, value);\n }, function (reason) {\n return reject(promise, reason);\n });\n }\n}\n\nfunction handleMaybeThenable(promise, maybeThenable, then$$1) {\n if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {\n handleOwnThenable(promise, maybeThenable);\n } else {\n if (then$$1 === GET_THEN_ERROR) {\n reject(promise, GET_THEN_ERROR.error);\n GET_THEN_ERROR.error = null;\n } else if (then$$1 === undefined) {\n fulfill(promise, maybeThenable);\n } else if (isFunction(then$$1)) {\n handleForeignThenable(promise, maybeThenable, then$$1);\n } else {\n fulfill(promise, maybeThenable);\n }\n }\n}\n\nfunction resolve(promise, value) {\n if (promise === value) {\n reject(promise, selfFulfillment());\n } else if (objectOrFunction(value)) {\n handleMaybeThenable(promise, value, getThen(value));\n } else {\n fulfill(promise, value);\n }\n}\n\nfunction publishRejection(promise) {\n if (promise._onerror) {\n promise._onerror(promise._result);\n }\n\n publish(promise);\n}\n\nfunction fulfill(promise, value) {\n if (promise._state !== PENDING) {\n return;\n }\n\n promise._result = value;\n promise._state = FULFILLED;\n\n if (promise._subscribers.length !== 0) {\n asap(publish, promise);\n }\n}\n\nfunction reject(promise, reason) {\n if (promise._state !== PENDING) {\n return;\n }\n promise._state = REJECTED;\n promise._result = reason;\n\n asap(publishRejection, promise);\n}\n\nfunction subscribe(parent, child, onFulfillment, onRejection) {\n var _subscribers = parent._subscribers;\n var length = _subscribers.length;\n\n\n parent._onerror = null;\n\n _subscribers[length] = child;\n _subscribers[length + FULFILLED] = onFulfillment;\n _subscribers[length + REJECTED] = onRejection;\n\n if (length === 0 && parent._state) {\n asap(publish, parent);\n }\n}\n\nfunction publish(promise) {\n var subscribers = promise._subscribers;\n var settled = promise._state;\n\n if (subscribers.length === 0) {\n return;\n }\n\n var child = void 0,\n callback = void 0,\n detail = promise._result;\n\n for (var i = 0; i < subscribers.length; i += 3) {\n child = subscribers[i];\n callback = subscribers[i + settled];\n\n if (child) {\n invokeCallback(settled, child, callback, detail);\n } else {\n callback(detail);\n }\n }\n\n promise._subscribers.length = 0;\n}\n\nfunction ErrorObject() {\n this.error = null;\n}\n\nvar TRY_CATCH_ERROR = new ErrorObject();\n\nfunction tryCatch(callback, detail) {\n try {\n return callback(detail);\n } catch (e) {\n TRY_CATCH_ERROR.error = e;\n return TRY_CATCH_ERROR;\n }\n}\n\nfunction invokeCallback(settled, promise, callback, detail) {\n var hasCallback = isFunction(callback),\n value = void 0,\n error = void 0,\n succeeded = void 0,\n failed = void 0;\n\n if (hasCallback) {\n value = tryCatch(callback, detail);\n\n if (value === TRY_CATCH_ERROR) {\n failed = true;\n error = value.error;\n value.error = null;\n } else {\n succeeded = true;\n }\n\n if (promise === value) {\n reject(promise, cannotReturnOwn());\n return;\n }\n } else {\n value = detail;\n succeeded = true;\n }\n\n if (promise._state !== PENDING) {\n // noop\n } else if (hasCallback && succeeded) {\n resolve(promise, value);\n } else if (failed) {\n reject(promise, error);\n } else if (settled === FULFILLED) {\n fulfill(promise, value);\n } else if (settled === REJECTED) {\n reject(promise, value);\n }\n}\n\nfunction initializePromise(promise, resolver) {\n try {\n resolver(function resolvePromise(value) {\n resolve(promise, value);\n }, function rejectPromise(reason) {\n reject(promise, reason);\n });\n } catch (e) {\n reject(promise, e);\n }\n}\n\nvar id = 0;\nfunction nextId() {\n return id++;\n}\n\nfunction makePromise(promise) {\n promise[PROMISE_ID] = id++;\n promise._state = undefined;\n promise._result = undefined;\n promise._subscribers = [];\n}\n\nfunction validationError() {\n return new Error('Array Methods must be provided an Array');\n}\n\nfunction validationError() {\n return new Error('Array Methods must be provided an Array');\n}\n\nvar Enumerator = function () {\n function Enumerator(Constructor, input) {\n this._instanceConstructor = Constructor;\n this.promise = new Constructor(noop);\n\n if (!this.promise[PROMISE_ID]) {\n makePromise(this.promise);\n }\n\n if (isArray(input)) {\n this.length = input.length;\n this._remaining = input.length;\n\n this._result = new Array(this.length);\n\n if (this.length === 0) {\n fulfill(this.promise, this._result);\n } else {\n this.length = this.length || 0;\n this._enumerate(input);\n if (this._remaining === 0) {\n fulfill(this.promise, this._result);\n }\n }\n } else {\n reject(this.promise, validationError());\n }\n }\n\n Enumerator.prototype._enumerate = function _enumerate(input) {\n for (var i = 0; this._state === PENDING && i < input.length; i++) {\n this._eachEntry(input[i], i);\n }\n };\n\n Enumerator.prototype._eachEntry = function _eachEntry(entry, i) {\n var c = this._instanceConstructor;\n var resolve$$1 = c.resolve;\n\n\n if (resolve$$1 === resolve$1) {\n var _then = getThen(entry);\n\n if (_then === then && entry._state !== PENDING) {\n this._settledAt(entry._state, i, entry._result);\n } else if (typeof _then !== 'function') {\n this._remaining--;\n this._result[i] = entry;\n } else if (c === Promise$2) {\n var promise = new c(noop);\n handleMaybeThenable(promise, entry, _then);\n this._willSettleAt(promise, i);\n } else {\n this._willSettleAt(new c(function (resolve$$1) {\n return resolve$$1(entry);\n }), i);\n }\n } else {\n this._willSettleAt(resolve$$1(entry), i);\n }\n };\n\n Enumerator.prototype._settledAt = function _settledAt(state, i, value) {\n var promise = this.promise;\n\n\n if (promise._state === PENDING) {\n this._remaining--;\n\n if (state === REJECTED) {\n reject(promise, value);\n } else {\n this._result[i] = value;\n }\n }\n\n if (this._remaining === 0) {\n fulfill(promise, this._result);\n }\n };\n\n Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) {\n var enumerator = this;\n\n subscribe(promise, undefined, function (value) {\n return enumerator._settledAt(FULFILLED, i, value);\n }, function (reason) {\n return enumerator._settledAt(REJECTED, i, reason);\n });\n };\n\n return Enumerator;\n}();\n\n/**\n `Promise.all` accepts an array of promises, and returns a new promise which\n is fulfilled with an array of fulfillment values for the passed promises, or\n rejected with the reason of the first passed promise to be rejected. It casts all\n elements of the passed iterable to promises as it runs this algorithm.\n\n Example:\n\n ```javascript\n let promise1 = resolve(1);\n let promise2 = resolve(2);\n let promise3 = resolve(3);\n let promises = [ promise1, promise2, promise3 ];\n\n Promise.all(promises).then(function(array){\n // The array here would be [ 1, 2, 3 ];\n });\n ```\n\n If any of the `promises` given to `all` are rejected, the first promise\n that is rejected will be given as an argument to the returned promises's\n rejection handler. For example:\n\n Example:\n\n ```javascript\n let promise1 = resolve(1);\n let promise2 = reject(new Error(\"2\"));\n let promise3 = reject(new Error(\"3\"));\n let promises = [ promise1, promise2, promise3 ];\n\n Promise.all(promises).then(function(array){\n // Code here never runs because there are rejected promises!\n }, function(error) {\n // error.message === \"2\"\n });\n ```\n\n @method all\n @static\n @param {Array} entries array of promises\n @param {String} label optional string for labeling the promise.\n Useful for tooling.\n @return {Promise} promise that is fulfilled when all `promises` have been\n fulfilled, or rejected if any of them become rejected.\n @static\n*/\nfunction all(entries) {\n return new Enumerator(this, entries).promise;\n}\n\n/**\n `Promise.race` returns a new promise which is settled in the same way as the\n first passed promise to settle.\n\n Example:\n\n ```javascript\n let promise1 = new Promise(function(resolve, reject){\n setTimeout(function(){\n resolve('promise 1');\n }, 200);\n });\n\n let promise2 = new Promise(function(resolve, reject){\n setTimeout(function(){\n resolve('promise 2');\n }, 100);\n });\n\n Promise.race([promise1, promise2]).then(function(result){\n // result === 'promise 2' because it was resolved before promise1\n // was resolved.\n });\n ```\n\n `Promise.race` is deterministic in that only the state of the first\n settled promise matters. For example, even if other promises given to the\n `promises` array argument are resolved, but the first settled promise has\n become rejected before the other promises became fulfilled, the returned\n promise will become rejected:\n\n ```javascript\n let promise1 = new Promise(function(resolve, reject){\n setTimeout(function(){\n resolve('promise 1');\n }, 200);\n });\n\n let promise2 = new Promise(function(resolve, reject){\n setTimeout(function(){\n reject(new Error('promise 2'));\n }, 100);\n });\n\n Promise.race([promise1, promise2]).then(function(result){\n // Code here never runs\n }, function(reason){\n // reason.message === 'promise 2' because promise 2 became rejected before\n // promise 1 became fulfilled\n });\n ```\n\n An example real-world use case is implementing timeouts:\n\n ```javascript\n Promise.race([ajax('foo.json'), timeout(5000)])\n ```\n\n @method race\n @static\n @param {Array} promises array of promises to observe\n Useful for tooling.\n @return {Promise} a promise which settles in the same way as the first passed\n promise to settle.\n*/\nfunction race(entries) {\n /*jshint validthis:true */\n var Constructor = this;\n\n if (!isArray(entries)) {\n return new Constructor(function (_, reject) {\n return reject(new TypeError('You must pass an array to race.'));\n });\n } else {\n return new Constructor(function (resolve, reject) {\n var length = entries.length;\n for (var i = 0; i < length; i++) {\n Constructor.resolve(entries[i]).then(resolve, reject);\n }\n });\n }\n}\n\n/**\n `Promise.reject` returns a promise rejected with the passed `reason`.\n It is shorthand for the following:\n\n ```javascript\n let promise = new Promise(function(resolve, reject){\n reject(new Error('WHOOPS'));\n });\n\n promise.then(function(value){\n // Code here doesn't run because the promise is rejected!\n }, function(reason){\n // reason.message === 'WHOOPS'\n });\n ```\n\n Instead of writing the above, your code now simply becomes the following:\n\n ```javascript\n let promise = Promise.reject(new Error('WHOOPS'));\n\n promise.then(function(value){\n // Code here doesn't run because the promise is rejected!\n }, function(reason){\n // reason.message === 'WHOOPS'\n });\n ```\n\n @method reject\n @static\n @param {Any} reason value that the returned promise will be rejected with.\n Useful for tooling.\n @return {Promise} a promise rejected with the given `reason`.\n*/\nfunction reject$1(reason) {\n /*jshint validthis:true */\n var Constructor = this;\n var promise = new Constructor(noop);\n reject(promise, reason);\n return promise;\n}\n\nfunction needsResolver() {\n throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');\n}\n\nfunction needsNew() {\n throw new TypeError(\"Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.\");\n}\n\n/**\n Promise objects represent the eventual result of an asynchronous operation. The\n primary way of interacting with a promise is through its `then` method, which\n registers callbacks to receive either a promise's eventual value or the reason\n why the promise cannot be fulfilled.\n\n Terminology\n -----------\n\n - `promise` is an object or function with a `then` method whose behavior conforms to this specification.\n - `thenable` is an object or function that defines a `then` method.\n - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).\n - `exception` is a value that is thrown using the throw statement.\n - `reason` is a value that indicates why a promise was rejected.\n - `settled` the final resting state of a promise, fulfilled or rejected.\n\n A promise can be in one of three states: pending, fulfilled, or rejected.\n\n Promises that are fulfilled have a fulfillment value and are in the fulfilled\n state. Promises that are rejected have a rejection reason and are in the\n rejected state. A fulfillment value is never a thenable.\n\n Promises can also be said to *resolve* a value. If this value is also a\n promise, then the original promise's settled state will match the value's\n settled state. So a promise that *resolves* a promise that rejects will\n itself reject, and a promise that *resolves* a promise that fulfills will\n itself fulfill.\n\n\n Basic Usage:\n ------------\n\n ```js\n let promise = new Promise(function(resolve, reject) {\n // on success\n resolve(value);\n\n // on failure\n reject(reason);\n });\n\n promise.then(function(value) {\n // on fulfillment\n }, function(reason) {\n // on rejection\n });\n ```\n\n Advanced Usage:\n ---------------\n\n Promises shine when abstracting away asynchronous interactions such as\n `XMLHttpRequest`s.\n\n ```js\n function getJSON(url) {\n return new Promise(function(resolve, reject){\n let xhr = new XMLHttpRequest();\n\n xhr.open('GET', url);\n xhr.onreadystatechange = handler;\n xhr.responseType = 'json';\n xhr.setRequestHeader('Accept', 'application/json');\n xhr.send();\n\n function handler() {\n if (this.readyState === this.DONE) {\n if (this.status === 200) {\n resolve(this.response);\n } else {\n reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));\n }\n }\n };\n });\n }\n\n getJSON('/posts.json').then(function(json) {\n // on fulfillment\n }, function(reason) {\n // on rejection\n });\n ```\n\n Unlike callbacks, promises are great composable primitives.\n\n ```js\n Promise.all([\n getJSON('/posts'),\n getJSON('/comments')\n ]).then(function(values){\n values[0] // => postsJSON\n values[1] // => commentsJSON\n\n return values;\n });\n ```\n\n @class Promise\n @param {Function} resolver\n Useful for tooling.\n @constructor\n*/\n\nvar Promise$2 = function () {\n function Promise(resolver) {\n this[PROMISE_ID] = nextId();\n this._result = this._state = undefined;\n this._subscribers = [];\n\n if (noop !== resolver) {\n typeof resolver !== 'function' && needsResolver();\n this instanceof Promise ? initializePromise(this, resolver) : needsNew();\n }\n }\n\n /**\n The primary way of interacting with a promise is through its `then` method,\n which registers callbacks to receive either a promise's eventual value or the\n reason why the promise cannot be fulfilled.\n ```js\n findUser().then(function(user){\n // user is available\n }, function(reason){\n // user is unavailable, and you are given the reason why\n });\n ```\n Chaining\n --------\n The return value of `then` is itself a promise. This second, 'downstream'\n promise is resolved with the return value of the first promise's fulfillment\n or rejection handler, or rejected if the handler throws an exception.\n ```js\n findUser().then(function (user) {\n return user.name;\n }, function (reason) {\n return 'default name';\n }).then(function (userName) {\n // If `findUser` fulfilled, `userName` will be the user's name, otherwise it\n // will be `'default name'`\n });\n findUser().then(function (user) {\n throw new Error('Found user, but still unhappy');\n }, function (reason) {\n throw new Error('`findUser` rejected and we're unhappy');\n }).then(function (value) {\n // never reached\n }, function (reason) {\n // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.\n // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.\n });\n ```\n If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.\n ```js\n findUser().then(function (user) {\n throw new PedagogicalException('Upstream error');\n }).then(function (value) {\n // never reached\n }).then(function (value) {\n // never reached\n }, function (reason) {\n // The `PedgagocialException` is propagated all the way down to here\n });\n ```\n Assimilation\n ------------\n Sometimes the value you want to propagate to a downstream promise can only be\n retrieved asynchronously. This can be achieved by returning a promise in the\n fulfillment or rejection handler. The downstream promise will then be pending\n until the returned promise is settled. This is called *assimilation*.\n ```js\n findUser().then(function (user) {\n return findCommentsByAuthor(user);\n }).then(function (comments) {\n // The user's comments are now available\n });\n ```\n If the assimliated promise rejects, then the downstream promise will also reject.\n ```js\n findUser().then(function (user) {\n return findCommentsByAuthor(user);\n }).then(function (comments) {\n // If `findCommentsByAuthor` fulfills, we'll have the value here\n }, function (reason) {\n // If `findCommentsByAuthor` rejects, we'll have the reason here\n });\n ```\n Simple Example\n --------------\n Synchronous Example\n ```javascript\n let result;\n try {\n result = findResult();\n // success\n } catch(reason) {\n // failure\n }\n ```\n Errback Example\n ```js\n findResult(function(result, err){\n if (err) {\n // failure\n } else {\n // success\n }\n });\n ```\n Promise Example;\n ```javascript\n findResult().then(function(result){\n // success\n }, function(reason){\n // failure\n });\n ```\n Advanced Example\n --------------\n Synchronous Example\n ```javascript\n let author, books;\n try {\n author = findAuthor();\n books = findBooksByAuthor(author);\n // success\n } catch(reason) {\n // failure\n }\n ```\n Errback Example\n ```js\n function foundBooks(books) {\n }\n function failure(reason) {\n }\n findAuthor(function(author, err){\n if (err) {\n failure(err);\n // failure\n } else {\n try {\n findBoooksByAuthor(author, function(books, err) {\n if (err) {\n failure(err);\n } else {\n try {\n foundBooks(books);\n } catch(reason) {\n failure(reason);\n }\n }\n });\n } catch(error) {\n failure(err);\n }\n // success\n }\n });\n ```\n Promise Example;\n ```javascript\n findAuthor().\n then(findBooksByAuthor).\n then(function(books){\n // found books\n }).catch(function(reason){\n // something went wrong\n });\n ```\n @method then\n @param {Function} onFulfilled\n @param {Function} onRejected\n Useful for tooling.\n @return {Promise}\n */\n\n /**\n `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same\n as the catch block of a try/catch statement.\n ```js\n function findAuthor(){\n throw new Error('couldn't find that author');\n }\n // synchronous\n try {\n findAuthor();\n } catch(reason) {\n // something went wrong\n }\n // async with promises\n findAuthor().catch(function(reason){\n // something went wrong\n });\n ```\n @method catch\n @param {Function} onRejection\n Useful for tooling.\n @return {Promise}\n */\n\n\n Promise.prototype.catch = function _catch(onRejection) {\n return this.then(null, onRejection);\n };\n\n /**\n `finally` will be invoked regardless of the promise's fate just as native\n try/catch/finally behaves\n \n Synchronous example:\n \n ```js\n findAuthor() {\n if (Math.random() > 0.5) {\n throw new Error();\n }\n return new Author();\n }\n \n try {\n return findAuthor(); // succeed or fail\n } catch(error) {\n return findOtherAuther();\n } finally {\n // always runs\n // doesn't affect the return value\n }\n ```\n \n Asynchronous example:\n \n ```js\n findAuthor().catch(function(reason){\n return findOtherAuther();\n }).finally(function(){\n // author was either found, or not\n });\n ```\n \n @method finally\n @param {Function} callback\n @return {Promise}\n */\n\n\n Promise.prototype.finally = function _finally(callback) {\n var promise = this;\n var constructor = promise.constructor;\n\n return promise.then(function (value) {\n return constructor.resolve(callback()).then(function () {\n return value;\n });\n }, function (reason) {\n return constructor.resolve(callback()).then(function () {\n throw reason;\n });\n });\n };\n\n return Promise;\n}();\n\nPromise$2.prototype.then = then;\nPromise$2.all = all;\nPromise$2.race = race;\nPromise$2.resolve = resolve$1;\nPromise$2.reject = reject$1;\nPromise$2._setScheduler = setScheduler;\nPromise$2._setAsap = setAsap;\nPromise$2._asap = asap;\n\n/*global self*/\nfunction polyfill() {\n var local = void 0;\n\n if (typeof global !== 'undefined') {\n local = global;\n } else if (typeof self !== 'undefined') {\n local = self;\n } else {\n try {\n local = Function('return this')();\n } catch (e) {\n throw new Error('polyfill failed because global object is unavailable in this environment');\n }\n }\n\n var P = local.Promise;\n\n if (P) {\n var promiseToString = null;\n try {\n promiseToString = Object.prototype.toString.call(P.resolve());\n } catch (e) {\n // silently ignored\n }\n\n if (promiseToString === '[object Promise]' && !P.cast) {\n return;\n }\n }\n\n local.Promise = Promise$2;\n}\n\n// Strange compat..\nPromise$2.polyfill = polyfill;\nPromise$2.Promise = Promise$2;\n\nPromise$2.polyfill();\n\nreturn Promise$2;\n\n})));\n\n\n\n//# sourceMappingURL=es6-promise.auto.map\n","var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\n// Version token that will be replaced by the prepack command\r\n/** The version of the SignalR client. */\r\nexport const VERSION: string = \"0.0.0-DEV_BUILD\";\r\n\r\n// Everything that users need to access must be exported here. Including interfaces.\r\nexport { AbortSignal } from \"./AbortController\";\r\nexport { AbortError, HttpError, TimeoutError } from \"./Errors\";\r\nexport { HttpClient, HttpRequest, HttpResponse } from \"./HttpClient\";\r\nexport { DefaultHttpClient } from \"./DefaultHttpClient\";\r\nexport { IHttpConnectionOptions } from \"./IHttpConnectionOptions\";\r\nexport { HubConnection, HubConnectionState } from \"./HubConnection\";\r\nexport { HubConnectionBuilder } from \"./HubConnectionBuilder\";\r\nexport { MessageType, MessageHeaders, HubMessage, HubMessageBase, HubInvocationMessage, InvocationMessage, StreamInvocationMessage, StreamItemMessage, CompletionMessage,\r\n PingMessage, CloseMessage, CancelInvocationMessage, IHubProtocol } from \"./IHubProtocol\";\r\nexport { ILogger, LogLevel } from \"./ILogger\";\r\nexport { HttpTransportType, TransferFormat, ITransport } from \"./ITransport\";\r\nexport { IStreamSubscriber, IStreamResult, ISubscription } from \"./Stream\";\r\nexport { NullLogger } from \"./Loggers\";\r\nexport { JsonHubProtocol } from \"./JsonHubProtocol\";\r\nexport { Subject } from \"./Subject\";\r\nexport { IRetryPolicy, RetryContext } from \"./IRetryPolicy\";\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\n/** Error thrown when an HTTP request fails. */\r\nexport class HttpError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // tslint:disable-next-line:variable-name\r\n private __proto__: Error;\r\n\r\n /** The HTTP status code represented by this error. */\r\n public statusCode: number;\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpError}.\r\n *\r\n * @param {string} errorMessage A descriptive error message.\r\n * @param {number} statusCode The HTTP status code represented by this error.\r\n */\r\n constructor(errorMessage: string, statusCode: number) {\r\n const trueProto = new.target.prototype;\r\n super(errorMessage);\r\n this.statusCode = statusCode;\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when a timeout elapses. */\r\nexport class TimeoutError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // tslint:disable-next-line:variable-name\r\n private __proto__: Error;\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.TimeoutError}.\r\n *\r\n * @param {string} errorMessage A descriptive error message.\r\n */\r\n constructor(errorMessage: string = \"A timeout occurred.\") {\r\n const trueProto = new.target.prototype;\r\n super(errorMessage);\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n\r\n/** Error thrown when an action is aborted. */\r\nexport class AbortError extends Error {\r\n // @ts-ignore: Intentionally unused.\r\n // tslint:disable-next-line:variable-name\r\n private __proto__: Error;\r\n\r\n /** Constructs a new instance of {@link AbortError}.\r\n *\r\n * @param {string} errorMessage A descriptive error message.\r\n */\r\n constructor(errorMessage: string = \"An abort occurred.\") {\r\n const trueProto = new.target.prototype;\r\n super(errorMessage);\r\n\r\n // Workaround issue in Typescript compiler\r\n // https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200\r\n this.__proto__ = trueProto;\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { AbortSignal } from \"./AbortController\";\r\n\r\n/** Represents an HTTP request. */\r\nexport interface HttpRequest {\r\n /** The HTTP method to use for the request. */\r\n method?: string;\r\n\r\n /** The URL for the request. */\r\n url?: string;\r\n\r\n /** The body content for the request. May be a string or an ArrayBuffer (for binary data). */\r\n content?: string | ArrayBuffer;\r\n\r\n /** An object describing headers to apply to the request. */\r\n headers?: { [key: string]: string };\r\n\r\n /** The XMLHttpRequestResponseType to apply to the request. */\r\n responseType?: XMLHttpRequestResponseType;\r\n\r\n /** An AbortSignal that can be monitored for cancellation. */\r\n abortSignal?: AbortSignal;\r\n\r\n /** The time to wait for the request to complete before throwing a TimeoutError. Measured in milliseconds. */\r\n timeout?: number;\r\n}\r\n\r\n/** Represents an HTTP response. */\r\nexport class HttpResponse {\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n */\r\n constructor(statusCode: number);\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code and message.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n * @param {string} statusText The status message of the response.\r\n */\r\n constructor(statusCode: number, statusText: string);\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code, message and string content.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n * @param {string} statusText The status message of the response.\r\n * @param {string} content The content of the response.\r\n */\r\n constructor(statusCode: number, statusText: string, content: string);\r\n\r\n /** Constructs a new instance of {@link @microsoft/signalr.HttpResponse} with the specified status code, message and binary content.\r\n *\r\n * @param {number} statusCode The status code of the response.\r\n * @param {string} statusText The status message of the response.\r\n * @param {ArrayBuffer} content The content of the response.\r\n */\r\n constructor(statusCode: number, statusText: string, content: ArrayBuffer);\r\n constructor(\r\n public readonly statusCode: number,\r\n public readonly statusText?: string,\r\n public readonly content?: string | ArrayBuffer) {\r\n }\r\n}\r\n\r\n/** Abstraction over an HTTP client.\r\n *\r\n * This class provides an abstraction over an HTTP client so that a different implementation can be provided on different platforms.\r\n */\r\nexport abstract class HttpClient {\r\n /** Issues an HTTP GET request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public get(url: string): Promise;\r\n\r\n /** Issues an HTTP GET request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public get(url: string, options: HttpRequest): Promise;\r\n public get(url: string, options?: HttpRequest): Promise {\r\n return this.send({\r\n ...options,\r\n method: \"GET\",\r\n url,\r\n });\r\n }\r\n\r\n /** Issues an HTTP POST request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public post(url: string): Promise;\r\n\r\n /** Issues an HTTP POST request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public post(url: string, options: HttpRequest): Promise;\r\n public post(url: string, options?: HttpRequest): Promise {\r\n return this.send({\r\n ...options,\r\n method: \"POST\",\r\n url,\r\n });\r\n }\r\n\r\n /** Issues an HTTP DELETE request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public delete(url: string): Promise;\r\n\r\n /** Issues an HTTP DELETE request to the specified URL, returning a Promise that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {string} url The URL for the request.\r\n * @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.\r\n * @returns {Promise} A Promise that resolves with an {@link @microsoft/signalr.HttpResponse} describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public delete(url: string, options: HttpRequest): Promise;\r\n public delete(url: string, options?: HttpRequest): Promise {\r\n return this.send({\r\n ...options,\r\n method: \"DELETE\",\r\n url,\r\n });\r\n }\r\n\r\n /** Issues an HTTP request to the specified URL, returning a {@link Promise} that resolves with an {@link @microsoft/signalr.HttpResponse} representing the result.\r\n *\r\n * @param {HttpRequest} request An {@link @microsoft/signalr.HttpRequest} describing the request to send.\r\n * @returns {Promise} A Promise that resolves with an HttpResponse describing the response, or rejects with an Error indicating a failure.\r\n */\r\n public abstract send(request: HttpRequest): Promise;\r\n\r\n /** Gets all cookies that apply to the specified URL.\r\n *\r\n * @param url The URL that the cookies are valid for.\r\n * @returns {string} A string containing all the key-value cookie pairs for the specified URL.\r\n */\r\n // @ts-ignore\r\n public getCookieString(url: string): string {\r\n return \"\";\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { AbortError } from \"./Errors\";\r\nimport { HttpClient, HttpRequest, HttpResponse } from \"./HttpClient\";\r\nimport { ILogger } from \"./ILogger\";\r\nimport { NodeHttpClient } from \"./NodeHttpClient\";\r\nimport { XhrHttpClient } from \"./XhrHttpClient\";\r\n\r\n/** Default implementation of {@link @microsoft/signalr.HttpClient}. */\r\nexport class DefaultHttpClient extends HttpClient {\r\n private readonly httpClient: HttpClient;\r\n\r\n /** Creates a new instance of the {@link @microsoft/signalr.DefaultHttpClient}, using the provided {@link @microsoft/signalr.ILogger} to log messages. */\r\n public constructor(logger: ILogger) {\r\n super();\r\n\r\n if (typeof XMLHttpRequest !== \"undefined\") {\r\n this.httpClient = new XhrHttpClient(logger);\r\n } else {\r\n this.httpClient = new NodeHttpClient(logger);\r\n }\r\n }\r\n\r\n /** @inheritDoc */\r\n public send(request: HttpRequest): Promise {\r\n // Check that abort was not signaled before calling send\r\n if (request.abortSignal && request.abortSignal.aborted) {\r\n return Promise.reject(new AbortError());\r\n }\r\n\r\n if (!request.method) {\r\n return Promise.reject(new Error(\"No method defined.\"));\r\n }\r\n if (!request.url) {\r\n return Promise.reject(new Error(\"No url defined.\"));\r\n }\r\n\r\n return this.httpClient.send(request);\r\n }\r\n\r\n public getCookieString(url: string): string {\r\n return this.httpClient.getCookieString(url);\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\n// This is an empty implementation of the NodeHttpClient that will be included in browser builds so the output file will be smaller\r\n\r\nimport { HttpClient, HttpResponse } from \"./HttpClient\";\r\nimport { ILogger } from \"./ILogger\";\r\n\r\n/** @private */\r\nexport class NodeHttpClient extends HttpClient {\r\n // @ts-ignore: Need ILogger to compile, but unused variables generate errors\r\n public constructor(logger: ILogger) {\r\n super();\r\n }\r\n\r\n public send(): Promise {\r\n return Promise.reject(new Error(\"If using Node either provide an XmlHttpRequest polyfill or consume the cjs or esm script instead of the browser/signalr.js one.\"));\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { AbortError, HttpError, TimeoutError } from \"./Errors\";\r\nimport { HttpClient, HttpRequest, HttpResponse } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\n\r\nexport class XhrHttpClient extends HttpClient {\r\n private readonly logger: ILogger;\r\n\r\n public constructor(logger: ILogger) {\r\n super();\r\n this.logger = logger;\r\n }\r\n\r\n /** @inheritDoc */\r\n public send(request: HttpRequest): Promise {\r\n // Check that abort was not signaled before calling send\r\n if (request.abortSignal && request.abortSignal.aborted) {\r\n return Promise.reject(new AbortError());\r\n }\r\n\r\n if (!request.method) {\r\n return Promise.reject(new Error(\"No method defined.\"));\r\n }\r\n if (!request.url) {\r\n return Promise.reject(new Error(\"No url defined.\"));\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const xhr = new XMLHttpRequest();\r\n\r\n xhr.open(request.method!, request.url!, true);\r\n xhr.withCredentials = true;\r\n xhr.setRequestHeader(\"X-Requested-With\", \"XMLHttpRequest\");\r\n // Explicitly setting the Content-Type header for React Native on Android platform.\r\n xhr.setRequestHeader(\"Content-Type\", \"text/plain;charset=UTF-8\");\r\n\r\n const headers = request.headers;\r\n if (headers) {\r\n Object.keys(headers)\r\n .forEach((header) => {\r\n xhr.setRequestHeader(header, headers[header]);\r\n });\r\n }\r\n\r\n if (request.responseType) {\r\n xhr.responseType = request.responseType;\r\n }\r\n\r\n if (request.abortSignal) {\r\n request.abortSignal.onabort = () => {\r\n xhr.abort();\r\n reject(new AbortError());\r\n };\r\n }\r\n\r\n if (request.timeout) {\r\n xhr.timeout = request.timeout;\r\n }\r\n\r\n xhr.onload = () => {\r\n if (request.abortSignal) {\r\n request.abortSignal.onabort = null;\r\n }\r\n\r\n if (xhr.status >= 200 && xhr.status < 300) {\r\n resolve(new HttpResponse(xhr.status, xhr.statusText, xhr.response || xhr.responseText));\r\n } else {\r\n reject(new HttpError(xhr.statusText, xhr.status));\r\n }\r\n };\r\n\r\n xhr.onerror = () => {\r\n this.logger.log(LogLevel.Warning, `Error from HTTP request. ${xhr.status}: ${xhr.statusText}.`);\r\n reject(new HttpError(xhr.statusText, xhr.status));\r\n };\r\n\r\n xhr.ontimeout = () => {\r\n this.logger.log(LogLevel.Warning, `Timeout from HTTP request.`);\r\n reject(new TimeoutError());\r\n };\r\n\r\n xhr.send(request.content || \"\");\r\n });\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\n// These values are designed to match the ASP.NET Log Levels since that's the pattern we're emulating here.\r\n/** Indicates the severity of a log message.\r\n *\r\n * Log Levels are ordered in increasing severity. So `Debug` is more severe than `Trace`, etc.\r\n */\r\nexport enum LogLevel {\r\n /** Log level for very low severity diagnostic messages. */\r\n Trace = 0,\r\n /** Log level for low severity diagnostic messages. */\r\n Debug = 1,\r\n /** Log level for informational diagnostic messages. */\r\n Information = 2,\r\n /** Log level for diagnostic messages that indicate a non-fatal problem. */\r\n Warning = 3,\r\n /** Log level for diagnostic messages that indicate a failure in the current operation. */\r\n Error = 4,\r\n /** Log level for diagnostic messages that indicate a failure that will terminate the entire application. */\r\n Critical = 5,\r\n /** The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted. */\r\n None = 6,\r\n}\r\n\r\n/** An abstraction that provides a sink for diagnostic messages. */\r\nexport interface ILogger {\r\n /** Called by the framework to emit a diagnostic message.\r\n *\r\n * @param {LogLevel} logLevel The severity level of the message.\r\n * @param {string} message The message.\r\n */\r\n log(logLevel: LogLevel, message: string): void;\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { HandshakeProtocol, HandshakeRequestMessage, HandshakeResponseMessage } from \"./HandshakeProtocol\";\r\nimport { IConnection } from \"./IConnection\";\r\nimport { CancelInvocationMessage, CompletionMessage, IHubProtocol, InvocationMessage, MessageType, StreamInvocationMessage, StreamItemMessage } from \"./IHubProtocol\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { IRetryPolicy } from \"./IRetryPolicy\";\r\nimport { IStreamResult } from \"./Stream\";\r\nimport { Subject } from \"./Subject\";\r\nimport { Arg } from \"./Utils\";\r\n\r\nconst DEFAULT_TIMEOUT_IN_MS: number = 30 * 1000;\r\nconst DEFAULT_PING_INTERVAL_IN_MS: number = 15 * 1000;\r\n\r\n/** Describes the current state of the {@link HubConnection} to the server. */\r\nexport enum HubConnectionState {\r\n /** The hub connection is disconnected. */\r\n Disconnected = \"Disconnected\",\r\n /** The hub connection is connecting. */\r\n Connecting = \"Connecting\",\r\n /** The hub connection is connected. */\r\n Connected = \"Connected\",\r\n /** The hub connection is disconnecting. */\r\n Disconnecting = \"Disconnecting\",\r\n /** The hub connection is reconnecting. */\r\n Reconnecting = \"Reconnecting\",\r\n}\r\n\r\n/** Represents a connection to a SignalR Hub. */\r\nexport class HubConnection {\r\n private readonly cachedPingMessage: string | ArrayBuffer;\r\n private readonly connection: IConnection;\r\n private readonly logger: ILogger;\r\n private readonly reconnectPolicy?: IRetryPolicy;\r\n private protocol: IHubProtocol;\r\n private handshakeProtocol: HandshakeProtocol;\r\n private callbacks: { [invocationId: string]: (invocationEvent: StreamItemMessage | CompletionMessage | null, error?: Error) => void };\r\n private methods: { [name: string]: Array<(...args: any[]) => void> };\r\n private invocationId: number;\r\n\r\n private closedCallbacks: Array<(error?: Error) => void>;\r\n private reconnectingCallbacks: Array<(error?: Error) => void>;\r\n private reconnectedCallbacks: Array<(connectionId?: string) => void>;\r\n\r\n private receivedHandshakeResponse: boolean;\r\n private handshakeResolver!: (value?: PromiseLike<{}>) => void;\r\n private handshakeRejecter!: (reason?: any) => void;\r\n private stopDuringStartError?: Error;\r\n\r\n private connectionState: HubConnectionState;\r\n // connectionStarted is tracked independently from connectionState, so we can check if the\r\n // connection ever did successfully transition from connecting to connected before disconnecting.\r\n private connectionStarted: boolean;\r\n private startPromise?: Promise;\r\n private stopPromise?: Promise;\r\n\r\n // The type of these a) doesn't matter and b) varies when building in browser and node contexts\r\n // Since we're building the WebPack bundle directly from the TypeScript, this matters (previously\r\n // we built the bundle from the compiled JavaScript).\r\n private reconnectDelayHandle?: any;\r\n private timeoutHandle?: any;\r\n private pingServerHandle?: any;\r\n\r\n /** The server timeout in milliseconds.\r\n *\r\n * If this timeout elapses without receiving any messages from the server, the connection will be terminated with an error.\r\n * The default timeout value is 30,000 milliseconds (30 seconds).\r\n */\r\n public serverTimeoutInMilliseconds: number;\r\n\r\n /** Default interval at which to ping the server.\r\n *\r\n * The default value is 15,000 milliseconds (15 seconds).\r\n * Allows the server to detect hard disconnects (like when a client unplugs their computer).\r\n */\r\n public keepAliveIntervalInMilliseconds: number;\r\n\r\n /** @internal */\r\n // Using a public static factory method means we can have a private constructor and an _internal_\r\n // create method that can be used by HubConnectionBuilder. An \"internal\" constructor would just\r\n // be stripped away and the '.d.ts' file would have no constructor, which is interpreted as a\r\n // public parameter-less constructor.\r\n public static create(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IRetryPolicy): HubConnection {\r\n return new HubConnection(connection, logger, protocol, reconnectPolicy);\r\n }\r\n\r\n private constructor(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IRetryPolicy) {\r\n Arg.isRequired(connection, \"connection\");\r\n Arg.isRequired(logger, \"logger\");\r\n Arg.isRequired(protocol, \"protocol\");\r\n\r\n this.serverTimeoutInMilliseconds = DEFAULT_TIMEOUT_IN_MS;\r\n this.keepAliveIntervalInMilliseconds = DEFAULT_PING_INTERVAL_IN_MS;\r\n\r\n this.logger = logger;\r\n this.protocol = protocol;\r\n this.connection = connection;\r\n this.reconnectPolicy = reconnectPolicy;\r\n this.handshakeProtocol = new HandshakeProtocol();\r\n\r\n this.connection.onreceive = (data: any) => this.processIncomingData(data);\r\n this.connection.onclose = (error?: Error) => this.connectionClosed(error);\r\n\r\n this.callbacks = {};\r\n this.methods = {};\r\n this.closedCallbacks = [];\r\n this.reconnectingCallbacks = [];\r\n this.reconnectedCallbacks = [];\r\n this.invocationId = 0;\r\n this.receivedHandshakeResponse = false;\r\n this.connectionState = HubConnectionState.Disconnected;\r\n this.connectionStarted = false;\r\n\r\n this.cachedPingMessage = this.protocol.writeMessage({ type: MessageType.Ping });\r\n }\r\n\r\n /** Indicates the state of the {@link HubConnection} to the server. */\r\n get state(): HubConnectionState {\r\n return this.connectionState;\r\n }\r\n\r\n /** Represents the connection id of the {@link HubConnection} on the server. The connection id will be null when the connection is either\r\n * in the disconnected state or if the negotiation step was skipped.\r\n */\r\n get connectionId(): string | null {\r\n return this.connection ? (this.connection.connectionId || null) : null;\r\n }\r\n\r\n /** Indicates the url of the {@link HubConnection} to the server. */\r\n get baseUrl(): string {\r\n return this.connection.baseUrl || \"\";\r\n }\r\n\r\n /**\r\n * Sets a new url for the HubConnection. Note that the url can only be changed when the connection is in either the Disconnected or\r\n * Reconnecting states.\r\n * @param {string} url The url to connect to.\r\n */\r\n set baseUrl(url: string) {\r\n if (this.connectionState !== HubConnectionState.Disconnected && this.connectionState !== HubConnectionState.Reconnecting) {\r\n throw new Error(\"The HubConnection must be in the Disconnected or Reconnecting state to change the url.\");\r\n }\r\n\r\n if (!url) {\r\n throw new Error(\"The HubConnection url must be a valid url.\");\r\n }\r\n\r\n this.connection.baseUrl = url;\r\n }\r\n\r\n /** Starts the connection.\r\n *\r\n * @returns {Promise} A Promise that resolves when the connection has been successfully established, or rejects with an error.\r\n */\r\n public start(): Promise {\r\n this.startPromise = this.startWithStateTransitions();\r\n return this.startPromise;\r\n }\r\n\r\n private async startWithStateTransitions(): Promise {\r\n if (this.connectionState !== HubConnectionState.Disconnected) {\r\n return Promise.reject(new Error(\"Cannot start a HubConnection that is not in the 'Disconnected' state.\"));\r\n }\r\n\r\n this.connectionState = HubConnectionState.Connecting;\r\n this.logger.log(LogLevel.Debug, \"Starting HubConnection.\");\r\n\r\n try {\r\n await this.startInternal();\r\n\r\n this.connectionState = HubConnectionState.Connected;\r\n this.connectionStarted = true;\r\n this.logger.log(LogLevel.Debug, \"HubConnection connected successfully.\");\r\n } catch (e) {\r\n this.connectionState = HubConnectionState.Disconnected;\r\n this.logger.log(LogLevel.Debug, `HubConnection failed to start successfully because of error '${e}'.`);\r\n return Promise.reject(e);\r\n }\r\n }\r\n\r\n private async startInternal() {\r\n this.stopDuringStartError = undefined;\r\n this.receivedHandshakeResponse = false;\r\n // Set up the promise before any connection is (re)started otherwise it could race with received messages\r\n const handshakePromise = new Promise((resolve, reject) => {\r\n this.handshakeResolver = resolve;\r\n this.handshakeRejecter = reject;\r\n });\r\n\r\n await this.connection.start(this.protocol.transferFormat);\r\n\r\n try {\r\n const handshakeRequest: HandshakeRequestMessage = {\r\n protocol: this.protocol.name,\r\n version: this.protocol.version,\r\n };\r\n\r\n this.logger.log(LogLevel.Debug, \"Sending handshake request.\");\r\n\r\n await this.sendMessage(this.handshakeProtocol.writeHandshakeRequest(handshakeRequest));\r\n\r\n this.logger.log(LogLevel.Information, `Using HubProtocol '${this.protocol.name}'.`);\r\n\r\n // defensively cleanup timeout in case we receive a message from the server before we finish start\r\n this.cleanupTimeout();\r\n this.resetTimeoutPeriod();\r\n this.resetKeepAliveInterval();\r\n\r\n await handshakePromise;\r\n\r\n // It's important to check the stopDuringStartError instead of just relying on the handshakePromise\r\n // being rejected on close, because this continuation can run after both the handshake completed successfully\r\n // and the connection was closed.\r\n if (this.stopDuringStartError) {\r\n // It's important to throw instead of returning a rejected promise, because we don't want to allow any state\r\n // transitions to occur between now and the calling code observing the exceptions. Returning a rejected promise\r\n // will cause the calling continuation to get scheduled to run later.\r\n throw this.stopDuringStartError;\r\n }\r\n } catch (e) {\r\n this.logger.log(LogLevel.Debug, `Hub handshake failed with error '${e}' during start(). Stopping HubConnection.`);\r\n\r\n this.cleanupTimeout();\r\n this.cleanupPingTimer();\r\n\r\n // HttpConnection.stop() should not complete until after the onclose callback is invoked.\r\n // This will transition the HubConnection to the disconnected state before HttpConnection.stop() completes.\r\n await this.connection.stop(e);\r\n throw e;\r\n }\r\n }\r\n\r\n /** Stops the connection.\r\n *\r\n * @returns {Promise} A Promise that resolves when the connection has been successfully terminated, or rejects with an error.\r\n */\r\n public async stop(): Promise {\r\n // Capture the start promise before the connection might be restarted in an onclose callback.\r\n const startPromise = this.startPromise;\r\n\r\n this.stopPromise = this.stopInternal();\r\n await this.stopPromise;\r\n\r\n try {\r\n // Awaiting undefined continues immediately\r\n await startPromise;\r\n } catch (e) {\r\n // This exception is returned to the user as a rejected Promise from the start method.\r\n }\r\n }\r\n\r\n private stopInternal(error?: Error): Promise {\r\n if (this.connectionState === HubConnectionState.Disconnected) {\r\n this.logger.log(LogLevel.Debug, `Call to HubConnection.stop(${error}) ignored because it is already in the disconnected state.`);\r\n return Promise.resolve();\r\n }\r\n\r\n if (this.connectionState === HubConnectionState.Disconnecting) {\r\n this.logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnecting state.`);\r\n return this.stopPromise!;\r\n }\r\n\r\n this.connectionState = HubConnectionState.Disconnecting;\r\n\r\n this.logger.log(LogLevel.Debug, \"Stopping HubConnection.\");\r\n\r\n if (this.reconnectDelayHandle) {\r\n // We're in a reconnect delay which means the underlying connection is currently already stopped.\r\n // Just clear the handle to stop the reconnect loop (which no one is waiting on thankfully) and\r\n // fire the onclose callbacks.\r\n this.logger.log(LogLevel.Debug, \"Connection stopped during reconnect delay. Done reconnecting.\");\r\n\r\n clearTimeout(this.reconnectDelayHandle);\r\n this.reconnectDelayHandle = undefined;\r\n\r\n this.completeClose();\r\n return Promise.resolve();\r\n }\r\n\r\n this.cleanupTimeout();\r\n this.cleanupPingTimer();\r\n this.stopDuringStartError = error || new Error(\"The connection was stopped before the hub handshake could complete.\");\r\n\r\n // HttpConnection.stop() should not complete until after either HttpConnection.start() fails\r\n // or the onclose callback is invoked. The onclose callback will transition the HubConnection\r\n // to the disconnected state if need be before HttpConnection.stop() completes.\r\n return this.connection.stop(error);\r\n }\r\n\r\n /** Invokes a streaming hub method on the server using the specified name and arguments.\r\n *\r\n * @typeparam T The type of the items returned by the server.\r\n * @param {string} methodName The name of the server method to invoke.\r\n * @param {any[]} args The arguments used to invoke the server method.\r\n * @returns {IStreamResult} An object that yields results from the server as they are received.\r\n */\r\n public stream(methodName: string, ...args: any[]): IStreamResult {\r\n const [streams, streamIds] = this.replaceStreamingParams(args);\r\n const invocationDescriptor = this.createStreamInvocation(methodName, args, streamIds);\r\n\r\n let promiseQueue: Promise;\r\n const subject = new Subject();\r\n subject.cancelCallback = () => {\r\n const cancelInvocation: CancelInvocationMessage = this.createCancelInvocation(invocationDescriptor.invocationId);\r\n\r\n delete this.callbacks[invocationDescriptor.invocationId];\r\n\r\n return promiseQueue.then(() => {\r\n return this.sendWithProtocol(cancelInvocation);\r\n });\r\n };\r\n\r\n this.callbacks[invocationDescriptor.invocationId] = (invocationEvent: CompletionMessage | StreamItemMessage | null, error?: Error) => {\r\n if (error) {\r\n subject.error(error);\r\n return;\r\n } else if (invocationEvent) {\r\n // invocationEvent will not be null when an error is not passed to the callback\r\n if (invocationEvent.type === MessageType.Completion) {\r\n if (invocationEvent.error) {\r\n subject.error(new Error(invocationEvent.error));\r\n } else {\r\n subject.complete();\r\n }\r\n } else {\r\n subject.next((invocationEvent.item) as T);\r\n }\r\n }\r\n };\r\n\r\n promiseQueue = this.sendWithProtocol(invocationDescriptor)\r\n .catch((e) => {\r\n subject.error(e);\r\n delete this.callbacks[invocationDescriptor.invocationId];\r\n });\r\n\r\n this.launchStreams(streams, promiseQueue);\r\n\r\n return subject;\r\n }\r\n\r\n private sendMessage(message: any) {\r\n this.resetKeepAliveInterval();\r\n return this.connection.send(message);\r\n }\r\n\r\n /**\r\n * Sends a js object to the server.\r\n * @param message The js object to serialize and send.\r\n */\r\n private sendWithProtocol(message: any) {\r\n return this.sendMessage(this.protocol.writeMessage(message));\r\n }\r\n\r\n /** Invokes a hub method on the server using the specified name and arguments. Does not wait for a response from the receiver.\r\n *\r\n * The Promise returned by this method resolves when the client has sent the invocation to the server. The server may still\r\n * be processing the invocation.\r\n *\r\n * @param {string} methodName The name of the server method to invoke.\r\n * @param {any[]} args The arguments used to invoke the server method.\r\n * @returns {Promise} A Promise that resolves when the invocation has been successfully sent, or rejects with an error.\r\n */\r\n public send(methodName: string, ...args: any[]): Promise {\r\n const [streams, streamIds] = this.replaceStreamingParams(args);\r\n const sendPromise = this.sendWithProtocol(this.createInvocation(methodName, args, true, streamIds));\r\n\r\n this.launchStreams(streams, sendPromise);\r\n\r\n return sendPromise;\r\n }\r\n\r\n /** Invokes a hub method on the server using the specified name and arguments.\r\n *\r\n * The Promise returned by this method resolves when the server indicates it has finished invoking the method. When the promise\r\n * resolves, the server has finished invoking the method. If the server method returns a result, it is produced as the result of\r\n * resolving the Promise.\r\n *\r\n * @typeparam T The expected return type.\r\n * @param {string} methodName The name of the server method to invoke.\r\n * @param {any[]} args The arguments used to invoke the server method.\r\n * @returns {Promise} A Promise that resolves with the result of the server method (if any), or rejects with an error.\r\n */\r\n public invoke(methodName: string, ...args: any[]): Promise {\r\n const [streams, streamIds] = this.replaceStreamingParams(args);\r\n const invocationDescriptor = this.createInvocation(methodName, args, false, streamIds);\r\n\r\n const p = new Promise((resolve, reject) => {\r\n // invocationId will always have a value for a non-blocking invocation\r\n this.callbacks[invocationDescriptor.invocationId!] = (invocationEvent: StreamItemMessage | CompletionMessage | null, error?: Error) => {\r\n if (error) {\r\n reject(error);\r\n return;\r\n } else if (invocationEvent) {\r\n // invocationEvent will not be null when an error is not passed to the callback\r\n if (invocationEvent.type === MessageType.Completion) {\r\n if (invocationEvent.error) {\r\n reject(new Error(invocationEvent.error));\r\n } else {\r\n resolve(invocationEvent.result);\r\n }\r\n } else {\r\n reject(new Error(`Unexpected message type: ${invocationEvent.type}`));\r\n }\r\n }\r\n };\r\n\r\n const promiseQueue = this.sendWithProtocol(invocationDescriptor)\r\n .catch((e) => {\r\n reject(e);\r\n // invocationId will always have a value for a non-blocking invocation\r\n delete this.callbacks[invocationDescriptor.invocationId!];\r\n });\r\n\r\n this.launchStreams(streams, promiseQueue);\r\n });\r\n\r\n return p;\r\n }\r\n\r\n /** Registers a handler that will be invoked when the hub method with the specified method name is invoked.\r\n *\r\n * @param {string} methodName The name of the hub method to define.\r\n * @param {Function} newMethod The handler that will be raised when the hub method is invoked.\r\n */\r\n public on(methodName: string, newMethod: (...args: any[]) => void) {\r\n if (!methodName || !newMethod) {\r\n return;\r\n }\r\n\r\n methodName = methodName.toLowerCase();\r\n if (!this.methods[methodName]) {\r\n this.methods[methodName] = [];\r\n }\r\n\r\n // Preventing adding the same handler multiple times.\r\n if (this.methods[methodName].indexOf(newMethod) !== -1) {\r\n return;\r\n }\r\n\r\n this.methods[methodName].push(newMethod);\r\n }\r\n\r\n /** Removes all handlers for the specified hub method.\r\n *\r\n * @param {string} methodName The name of the method to remove handlers for.\r\n */\r\n public off(methodName: string): void;\r\n\r\n /** Removes the specified handler for the specified hub method.\r\n *\r\n * You must pass the exact same Function instance as was previously passed to {@link @microsoft/signalr.HubConnection.on}. Passing a different instance (even if the function\r\n * body is the same) will not remove the handler.\r\n *\r\n * @param {string} methodName The name of the method to remove handlers for.\r\n * @param {Function} method The handler to remove. This must be the same Function instance as the one passed to {@link @microsoft/signalr.HubConnection.on}.\r\n */\r\n public off(methodName: string, method: (...args: any[]) => void): void;\r\n public off(methodName: string, method?: (...args: any[]) => void): void {\r\n if (!methodName) {\r\n return;\r\n }\r\n\r\n methodName = methodName.toLowerCase();\r\n const handlers = this.methods[methodName];\r\n if (!handlers) {\r\n return;\r\n }\r\n if (method) {\r\n const removeIdx = handlers.indexOf(method);\r\n if (removeIdx !== -1) {\r\n handlers.splice(removeIdx, 1);\r\n if (handlers.length === 0) {\r\n delete this.methods[methodName];\r\n }\r\n }\r\n } else {\r\n delete this.methods[methodName];\r\n }\r\n\r\n }\r\n\r\n /** Registers a handler that will be invoked when the connection is closed.\r\n *\r\n * @param {Function} callback The handler that will be invoked when the connection is closed. Optionally receives a single argument containing the error that caused the connection to close (if any).\r\n */\r\n public onclose(callback: (error?: Error) => void) {\r\n if (callback) {\r\n this.closedCallbacks.push(callback);\r\n }\r\n }\r\n\r\n /** Registers a handler that will be invoked when the connection starts reconnecting.\r\n *\r\n * @param {Function} callback The handler that will be invoked when the connection starts reconnecting. Optionally receives a single argument containing the error that caused the connection to start reconnecting (if any).\r\n */\r\n public onreconnecting(callback: (error?: Error) => void) {\r\n if (callback) {\r\n this.reconnectingCallbacks.push(callback);\r\n }\r\n }\r\n\r\n /** Registers a handler that will be invoked when the connection successfully reconnects.\r\n *\r\n * @param {Function} callback The handler that will be invoked when the connection successfully reconnects.\r\n */\r\n public onreconnected(callback: (connectionId?: string) => void) {\r\n if (callback) {\r\n this.reconnectedCallbacks.push(callback);\r\n }\r\n }\r\n\r\n private processIncomingData(data: any) {\r\n this.cleanupTimeout();\r\n\r\n if (!this.receivedHandshakeResponse) {\r\n data = this.processHandshakeResponse(data);\r\n this.receivedHandshakeResponse = true;\r\n }\r\n\r\n // Data may have all been read when processing handshake response\r\n if (data) {\r\n // Parse the messages\r\n const messages = this.protocol.parseMessages(data, this.logger);\r\n\r\n for (const message of messages) {\r\n switch (message.type) {\r\n case MessageType.Invocation:\r\n this.invokeClientMethod(message);\r\n break;\r\n case MessageType.StreamItem:\r\n case MessageType.Completion:\r\n const callback = this.callbacks[message.invocationId];\r\n if (callback) {\r\n if (message.type === MessageType.Completion) {\r\n delete this.callbacks[message.invocationId];\r\n }\r\n callback(message);\r\n }\r\n break;\r\n case MessageType.Ping:\r\n // Don't care about pings\r\n break;\r\n case MessageType.Close:\r\n this.logger.log(LogLevel.Information, \"Close message received from server.\");\r\n\r\n const error = message.error ? new Error(\"Server returned an error on close: \" + message.error) : undefined;\r\n\r\n if (message.allowReconnect === true) {\r\n // It feels wrong not to await connection.stop() here, but processIncomingData is called as part of an onreceive callback which is not async,\r\n // this is already the behavior for serverTimeout(), and HttpConnection.Stop() should catch and log all possible exceptions.\r\n\r\n // tslint:disable-next-line:no-floating-promises\r\n this.connection.stop(error);\r\n } else {\r\n // We cannot await stopInternal() here, but subsequent calls to stop() will await this if stopInternal() is still ongoing.\r\n this.stopPromise = this.stopInternal(error);\r\n }\r\n\r\n break;\r\n default:\r\n this.logger.log(LogLevel.Warning, `Invalid message type: ${message.type}.`);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n this.resetTimeoutPeriod();\r\n }\r\n\r\n private processHandshakeResponse(data: any): any {\r\n let responseMessage: HandshakeResponseMessage;\r\n let remainingData: any;\r\n\r\n try {\r\n [remainingData, responseMessage] = this.handshakeProtocol.parseHandshakeResponse(data);\r\n } catch (e) {\r\n const message = \"Error parsing handshake response: \" + e;\r\n this.logger.log(LogLevel.Error, message);\r\n\r\n const error = new Error(message);\r\n this.handshakeRejecter(error);\r\n throw error;\r\n }\r\n if (responseMessage.error) {\r\n const message = \"Server returned handshake error: \" + responseMessage.error;\r\n this.logger.log(LogLevel.Error, message);\r\n\r\n const error = new Error(message);\r\n this.handshakeRejecter(error);\r\n throw error;\r\n } else {\r\n this.logger.log(LogLevel.Debug, \"Server handshake complete.\");\r\n }\r\n\r\n this.handshakeResolver();\r\n return remainingData;\r\n }\r\n\r\n private resetKeepAliveInterval() {\r\n this.cleanupPingTimer();\r\n this.pingServerHandle = setTimeout(async () => {\r\n if (this.connectionState === HubConnectionState.Connected) {\r\n try {\r\n await this.sendMessage(this.cachedPingMessage);\r\n } catch {\r\n // We don't care about the error. It should be seen elsewhere in the client.\r\n // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering\r\n this.cleanupPingTimer();\r\n }\r\n }\r\n }, this.keepAliveIntervalInMilliseconds);\r\n }\r\n\r\n private resetTimeoutPeriod() {\r\n if (!this.connection.features || !this.connection.features.inherentKeepAlive) {\r\n // Set the timeout timer\r\n this.timeoutHandle = setTimeout(() => this.serverTimeout(), this.serverTimeoutInMilliseconds);\r\n }\r\n }\r\n\r\n private serverTimeout() {\r\n // The server hasn't talked to us in a while. It doesn't like us anymore ... :(\r\n // Terminate the connection, but we don't need to wait on the promise. This could trigger reconnecting.\r\n // tslint:disable-next-line:no-floating-promises\r\n this.connection.stop(new Error(\"Server timeout elapsed without receiving a message from the server.\"));\r\n }\r\n\r\n private invokeClientMethod(invocationMessage: InvocationMessage) {\r\n const methods = this.methods[invocationMessage.target.toLowerCase()];\r\n if (methods) {\r\n try {\r\n methods.forEach((m) => m.apply(this, invocationMessage.arguments));\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, `A callback for the method ${invocationMessage.target.toLowerCase()} threw error '${e}'.`);\r\n }\r\n\r\n if (invocationMessage.invocationId) {\r\n // This is not supported in v1. So we return an error to avoid blocking the server waiting for the response.\r\n const message = \"Server requested a response, which is not supported in this version of the client.\";\r\n this.logger.log(LogLevel.Error, message);\r\n\r\n // We don't want to wait on the stop itself.\r\n this.stopPromise = this.stopInternal(new Error(message));\r\n }\r\n } else {\r\n this.logger.log(LogLevel.Warning, `No client method with the name '${invocationMessage.target}' found.`);\r\n }\r\n }\r\n\r\n private connectionClosed(error?: Error) {\r\n this.logger.log(LogLevel.Debug, `HubConnection.connectionClosed(${error}) called while in state ${this.connectionState}.`);\r\n\r\n // Triggering this.handshakeRejecter is insufficient because it could already be resolved without the continuation having run yet.\r\n this.stopDuringStartError = this.stopDuringStartError || error || new Error(\"The underlying connection was closed before the hub handshake could complete.\");\r\n\r\n // If the handshake is in progress, start will be waiting for the handshake promise, so we complete it.\r\n // If it has already completed, this should just noop.\r\n if (this.handshakeResolver) {\r\n this.handshakeResolver();\r\n }\r\n\r\n this.cancelCallbacksWithError(error || new Error(\"Invocation canceled due to the underlying connection being closed.\"));\r\n\r\n this.cleanupTimeout();\r\n this.cleanupPingTimer();\r\n\r\n if (this.connectionState === HubConnectionState.Disconnecting) {\r\n this.completeClose(error);\r\n } else if (this.connectionState === HubConnectionState.Connected && this.reconnectPolicy) {\r\n // tslint:disable-next-line:no-floating-promises\r\n this.reconnect(error);\r\n } else if (this.connectionState === HubConnectionState.Connected) {\r\n this.completeClose(error);\r\n }\r\n\r\n // If none of the above if conditions were true were called the HubConnection must be in either:\r\n // 1. The Connecting state in which case the handshakeResolver will complete it and stopDuringStartError will fail it.\r\n // 2. The Reconnecting state in which case the handshakeResolver will complete it and stopDuringStartError will fail the current reconnect attempt\r\n // and potentially continue the reconnect() loop.\r\n // 3. The Disconnected state in which case we're already done.\r\n }\r\n\r\n private completeClose(error?: Error) {\r\n if (this.connectionStarted) {\r\n this.connectionState = HubConnectionState.Disconnected;\r\n this.connectionStarted = false;\r\n\r\n try {\r\n this.closedCallbacks.forEach((c) => c.apply(this, [error]));\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, `An onclose callback called with error '${error}' threw error '${e}'.`);\r\n }\r\n }\r\n }\r\n\r\n private async reconnect(error?: Error) {\r\n const reconnectStartTime = Date.now();\r\n let previousReconnectAttempts = 0;\r\n let retryError = error !== undefined ? error : new Error(\"Attempting to reconnect due to a unknown error.\");\r\n\r\n let nextRetryDelay = this.getNextRetryDelay(previousReconnectAttempts++, 0, retryError);\r\n\r\n if (nextRetryDelay === null) {\r\n this.logger.log(LogLevel.Debug, \"Connection not reconnecting because the IRetryPolicy returned null on the first reconnect attempt.\");\r\n this.completeClose(error);\r\n return;\r\n }\r\n\r\n this.connectionState = HubConnectionState.Reconnecting;\r\n\r\n if (error) {\r\n this.logger.log(LogLevel.Information, `Connection reconnecting because of error '${error}'.`);\r\n } else {\r\n this.logger.log(LogLevel.Information, \"Connection reconnecting.\");\r\n }\r\n\r\n if (this.onreconnecting) {\r\n try {\r\n this.reconnectingCallbacks.forEach((c) => c.apply(this, [error]));\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, `An onreconnecting callback called with error '${error}' threw error '${e}'.`);\r\n }\r\n\r\n // Exit early if an onreconnecting callback called connection.stop().\r\n if (this.connectionState !== HubConnectionState.Reconnecting) {\r\n this.logger.log(LogLevel.Debug, \"Connection left the reconnecting state in onreconnecting callback. Done reconnecting.\");\r\n return;\r\n }\r\n }\r\n\r\n while (nextRetryDelay !== null) {\r\n this.logger.log(LogLevel.Information, `Reconnect attempt number ${previousReconnectAttempts} will start in ${nextRetryDelay} ms.`);\r\n\r\n await new Promise((resolve) => {\r\n this.reconnectDelayHandle = setTimeout(resolve, nextRetryDelay!);\r\n });\r\n this.reconnectDelayHandle = undefined;\r\n\r\n if (this.connectionState !== HubConnectionState.Reconnecting) {\r\n this.logger.log(LogLevel.Debug, \"Connection left the reconnecting state during reconnect delay. Done reconnecting.\");\r\n return;\r\n }\r\n\r\n try {\r\n await this.startInternal();\r\n\r\n this.connectionState = HubConnectionState.Connected;\r\n this.logger.log(LogLevel.Information, \"HubConnection reconnected successfully.\");\r\n\r\n if (this.onreconnected) {\r\n try {\r\n this.reconnectedCallbacks.forEach((c) => c.apply(this, [this.connection.connectionId]));\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, `An onreconnected callback called with connectionId '${this.connection.connectionId}; threw error '${e}'.`);\r\n }\r\n }\r\n\r\n return;\r\n } catch (e) {\r\n this.logger.log(LogLevel.Information, `Reconnect attempt failed because of error '${e}'.`);\r\n\r\n if (this.connectionState !== HubConnectionState.Reconnecting) {\r\n this.logger.log(LogLevel.Debug, \"Connection left the reconnecting state during reconnect attempt. Done reconnecting.\");\r\n return;\r\n }\r\n\r\n retryError = e instanceof Error ? e : new Error(e.toString());\r\n nextRetryDelay = this.getNextRetryDelay(previousReconnectAttempts++, Date.now() - reconnectStartTime, retryError);\r\n }\r\n }\r\n\r\n this.logger.log(LogLevel.Information, `Reconnect retries have been exhausted after ${Date.now() - reconnectStartTime} ms and ${previousReconnectAttempts} failed attempts. Connection disconnecting.`);\r\n\r\n this.completeClose();\r\n }\r\n\r\n private getNextRetryDelay(previousRetryCount: number, elapsedMilliseconds: number, retryReason: Error) {\r\n try {\r\n return this.reconnectPolicy!.nextRetryDelayInMilliseconds({\r\n elapsedMilliseconds,\r\n previousRetryCount,\r\n retryReason,\r\n });\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, `IRetryPolicy.nextRetryDelayInMilliseconds(${previousRetryCount}, ${elapsedMilliseconds}) threw error '${e}'.`);\r\n return null;\r\n }\r\n }\r\n\r\n private cancelCallbacksWithError(error: Error) {\r\n const callbacks = this.callbacks;\r\n this.callbacks = {};\r\n\r\n Object.keys(callbacks)\r\n .forEach((key) => {\r\n const callback = callbacks[key];\r\n callback(null, error);\r\n });\r\n }\r\n\r\n private cleanupPingTimer(): void {\r\n if (this.pingServerHandle) {\r\n clearTimeout(this.pingServerHandle);\r\n }\r\n }\r\n\r\n private cleanupTimeout(): void {\r\n if (this.timeoutHandle) {\r\n clearTimeout(this.timeoutHandle);\r\n }\r\n }\r\n\r\n private createInvocation(methodName: string, args: any[], nonblocking: boolean, streamIds: string[]): InvocationMessage {\r\n if (nonblocking) {\r\n return {\r\n arguments: args,\r\n streamIds,\r\n target: methodName,\r\n type: MessageType.Invocation,\r\n };\r\n } else {\r\n const invocationId = this.invocationId;\r\n this.invocationId++;\r\n\r\n return {\r\n arguments: args,\r\n invocationId: invocationId.toString(),\r\n streamIds,\r\n target: methodName,\r\n type: MessageType.Invocation,\r\n };\r\n }\r\n }\r\n\r\n private launchStreams(streams: Array>, promiseQueue: Promise): void {\r\n if (streams.length === 0) {\r\n return;\r\n }\r\n\r\n // Synchronize stream data so they arrive in-order on the server\r\n if (!promiseQueue) {\r\n promiseQueue = Promise.resolve();\r\n }\r\n\r\n // We want to iterate over the keys, since the keys are the stream ids\r\n // tslint:disable-next-line:forin\r\n for (const streamId in streams) {\r\n streams[streamId].subscribe({\r\n complete: () => {\r\n promiseQueue = promiseQueue.then(() => this.sendWithProtocol(this.createCompletionMessage(streamId)));\r\n },\r\n error: (err) => {\r\n let message: string;\r\n if (err instanceof Error) {\r\n message = err.message;\r\n } else if (err && err.toString) {\r\n message = err.toString();\r\n } else {\r\n message = \"Unknown error\";\r\n }\r\n\r\n promiseQueue = promiseQueue.then(() => this.sendWithProtocol(this.createCompletionMessage(streamId, message)));\r\n },\r\n next: (item) => {\r\n promiseQueue = promiseQueue.then(() => this.sendWithProtocol(this.createStreamItemMessage(streamId, item)));\r\n },\r\n });\r\n }\r\n }\r\n\r\n private replaceStreamingParams(args: any[]): [Array>, string[]] {\r\n const streams: Array> = [];\r\n const streamIds: string[] = [];\r\n for (let i = 0; i < args.length; i++) {\r\n const argument = args[i];\r\n if (this.isObservable(argument)) {\r\n const streamId = this.invocationId;\r\n this.invocationId++;\r\n // Store the stream for later use\r\n streams[streamId] = argument;\r\n streamIds.push(streamId.toString());\r\n\r\n // remove stream from args\r\n args.splice(i, 1);\r\n }\r\n }\r\n\r\n return [streams, streamIds];\r\n }\r\n\r\n private isObservable(arg: any): arg is IStreamResult {\r\n // This allows other stream implementations to just work (like rxjs)\r\n return arg && arg.subscribe && typeof arg.subscribe === \"function\";\r\n }\r\n\r\n private createStreamInvocation(methodName: string, args: any[], streamIds: string[]): StreamInvocationMessage {\r\n const invocationId = this.invocationId;\r\n this.invocationId++;\r\n\r\n return {\r\n arguments: args,\r\n invocationId: invocationId.toString(),\r\n streamIds,\r\n target: methodName,\r\n type: MessageType.StreamInvocation,\r\n };\r\n }\r\n\r\n private createCancelInvocation(id: string): CancelInvocationMessage {\r\n return {\r\n invocationId: id,\r\n type: MessageType.CancelInvocation,\r\n };\r\n }\r\n\r\n private createStreamItemMessage(id: string, item: any): StreamItemMessage {\r\n return {\r\n invocationId: id,\r\n item,\r\n type: MessageType.StreamItem,\r\n };\r\n }\r\n\r\n private createCompletionMessage(id: string, error?: any, result?: any): CompletionMessage {\r\n if (error) {\r\n return {\r\n error,\r\n invocationId: id,\r\n type: MessageType.Completion,\r\n };\r\n }\r\n\r\n return {\r\n invocationId: id,\r\n result,\r\n type: MessageType.Completion,\r\n };\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { TextMessageFormat } from \"./TextMessageFormat\";\r\nimport { isArrayBuffer } from \"./Utils\";\r\n\r\n/** @private */\r\nexport interface HandshakeRequestMessage {\r\n readonly protocol: string;\r\n readonly version: number;\r\n}\r\n\r\n/** @private */\r\nexport interface HandshakeResponseMessage {\r\n readonly error: string;\r\n readonly minorVersion: number;\r\n}\r\n\r\n/** @private */\r\nexport class HandshakeProtocol {\r\n // Handshake request is always JSON\r\n public writeHandshakeRequest(handshakeRequest: HandshakeRequestMessage): string {\r\n return TextMessageFormat.write(JSON.stringify(handshakeRequest));\r\n }\r\n\r\n public parseHandshakeResponse(data: any): [any, HandshakeResponseMessage] {\r\n let responseMessage: HandshakeResponseMessage;\r\n let messageData: string;\r\n let remainingData: any;\r\n\r\n if (isArrayBuffer(data) || (typeof Buffer !== \"undefined\" && data instanceof Buffer)) {\r\n // Format is binary but still need to read JSON text from handshake response\r\n const binaryData = new Uint8Array(data);\r\n const separatorIndex = binaryData.indexOf(TextMessageFormat.RecordSeparatorCode);\r\n if (separatorIndex === -1) {\r\n throw new Error(\"Message is incomplete.\");\r\n }\r\n\r\n // content before separator is handshake response\r\n // optional content after is additional messages\r\n const responseLength = separatorIndex + 1;\r\n messageData = String.fromCharCode.apply(null, binaryData.slice(0, responseLength));\r\n remainingData = (binaryData.byteLength > responseLength) ? binaryData.slice(responseLength).buffer : null;\r\n } else {\r\n const textData: string = data;\r\n const separatorIndex = textData.indexOf(TextMessageFormat.RecordSeparator);\r\n if (separatorIndex === -1) {\r\n throw new Error(\"Message is incomplete.\");\r\n }\r\n\r\n // content before separator is handshake response\r\n // optional content after is additional messages\r\n const responseLength = separatorIndex + 1;\r\n messageData = textData.substring(0, responseLength);\r\n remainingData = (textData.length > responseLength) ? textData.substring(responseLength) : null;\r\n }\r\n\r\n // At this point we should have just the single handshake message\r\n const messages = TextMessageFormat.parse(messageData);\r\n const response = JSON.parse(messages[0]);\r\n if (response.type) {\r\n throw new Error(\"Expected a handshake response from the server.\");\r\n }\r\n responseMessage = response;\r\n\r\n // multiple messages could have arrived with handshake\r\n // return additional data to be parsed as usual, or null if all parsed\r\n return [remainingData, responseMessage];\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\n// Not exported from index\r\n/** @private */\r\nexport class TextMessageFormat {\r\n public static RecordSeparatorCode = 0x1e;\r\n public static RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode);\r\n\r\n public static write(output: string): string {\r\n return `${output}${TextMessageFormat.RecordSeparator}`;\r\n }\r\n\r\n public static parse(input: string): string[] {\r\n if (input[input.length - 1] !== TextMessageFormat.RecordSeparator) {\r\n throw new Error(\"Message is incomplete.\");\r\n }\r\n\r\n const messages = input.split(TextMessageFormat.RecordSeparator);\r\n messages.pop();\r\n return messages;\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { HttpClient } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { NullLogger } from \"./Loggers\";\r\nimport { IStreamSubscriber, ISubscription } from \"./Stream\";\r\nimport { Subject } from \"./Subject\";\r\n\r\n/** @private */\r\nexport class Arg {\r\n public static isRequired(val: any, name: string): void {\r\n if (val === null || val === undefined) {\r\n throw new Error(`The '${name}' argument is required.`);\r\n }\r\n }\r\n\r\n public static isIn(val: any, values: any, name: string): void {\r\n // TypeScript enums have keys for **both** the name and the value of each enum member on the type itself.\r\n if (!(val in values)) {\r\n throw new Error(`Unknown ${name} value: ${val}.`);\r\n }\r\n }\r\n}\r\n\r\n/** @private */\r\nexport class Platform {\r\n\r\n public static get isBrowser(): boolean {\r\n return typeof window === \"object\";\r\n }\r\n\r\n public static get isWebWorker(): boolean {\r\n return typeof self === \"object\" && \"importScripts\" in self;\r\n }\r\n\r\n public static get isNode(): boolean {\r\n return !this.isBrowser && !this.isWebWorker;\r\n }\r\n}\r\n\r\n/** @private */\r\nexport function getDataDetail(data: any, includeContent: boolean): string {\r\n let detail = \"\";\r\n if (isArrayBuffer(data)) {\r\n detail = `Binary data of length ${data.byteLength}`;\r\n if (includeContent) {\r\n detail += `. Content: '${formatArrayBuffer(data)}'`;\r\n }\r\n } else if (typeof data === \"string\") {\r\n detail = `String data of length ${data.length}`;\r\n if (includeContent) {\r\n detail += `. Content: '${data}'`;\r\n }\r\n }\r\n return detail;\r\n}\r\n\r\n/** @private */\r\nexport function formatArrayBuffer(data: ArrayBuffer): string {\r\n const view = new Uint8Array(data);\r\n\r\n // Uint8Array.map only supports returning another Uint8Array?\r\n let str = \"\";\r\n view.forEach((num) => {\r\n const pad = num < 16 ? \"0\" : \"\";\r\n str += `0x${pad}${num.toString(16)} `;\r\n });\r\n\r\n // Trim of trailing space.\r\n return str.substr(0, str.length - 1);\r\n}\r\n\r\n// Also in signalr-protocol-msgpack/Utils.ts\r\n/** @private */\r\nexport function isArrayBuffer(val: any): val is ArrayBuffer {\r\n return val && typeof ArrayBuffer !== \"undefined\" &&\r\n (val instanceof ArrayBuffer ||\r\n // Sometimes we get an ArrayBuffer that doesn't satisfy instanceof\r\n (val.constructor && val.constructor.name === \"ArrayBuffer\"));\r\n}\r\n\r\n/** @private */\r\nexport async function sendMessage(logger: ILogger, transportName: string, httpClient: HttpClient, url: string, accessTokenFactory: (() => string | Promise) | undefined, content: string | ArrayBuffer, logMessageContent: boolean): Promise {\r\n let headers;\r\n if (accessTokenFactory) {\r\n const token = await accessTokenFactory();\r\n if (token) {\r\n headers = {\r\n [\"Authorization\"]: `Bearer ${token}`,\r\n };\r\n }\r\n }\r\n\r\n logger.log(LogLevel.Trace, `(${transportName} transport) sending data. ${getDataDetail(content, logMessageContent)}.`);\r\n\r\n const responseType = isArrayBuffer(content) ? \"arraybuffer\" : \"text\";\r\n const response = await httpClient.post(url, {\r\n content,\r\n headers,\r\n responseType,\r\n });\r\n\r\n logger.log(LogLevel.Trace, `(${transportName} transport) request complete. Response status: ${response.statusCode}.`);\r\n}\r\n\r\n/** @private */\r\nexport function createLogger(logger?: ILogger | LogLevel) {\r\n if (logger === undefined) {\r\n return new ConsoleLogger(LogLevel.Information);\r\n }\r\n\r\n if (logger === null) {\r\n return NullLogger.instance;\r\n }\r\n\r\n if ((logger as ILogger).log) {\r\n return logger as ILogger;\r\n }\r\n\r\n return new ConsoleLogger(logger as LogLevel);\r\n}\r\n\r\n/** @private */\r\nexport class SubjectSubscription implements ISubscription {\r\n private subject: Subject;\r\n private observer: IStreamSubscriber;\r\n\r\n constructor(subject: Subject, observer: IStreamSubscriber) {\r\n this.subject = subject;\r\n this.observer = observer;\r\n }\r\n\r\n public dispose(): void {\r\n const index: number = this.subject.observers.indexOf(this.observer);\r\n if (index > -1) {\r\n this.subject.observers.splice(index, 1);\r\n }\r\n\r\n if (this.subject.observers.length === 0 && this.subject.cancelCallback) {\r\n this.subject.cancelCallback().catch((_) => { });\r\n }\r\n }\r\n}\r\n\r\n/** @private */\r\nexport class ConsoleLogger implements ILogger {\r\n private readonly minimumLogLevel: LogLevel;\r\n\r\n // Public for testing purposes.\r\n public outputConsole: {\r\n error(message: any): void,\r\n warn(message: any): void,\r\n info(message: any): void,\r\n log(message: any): void,\r\n };\r\n\r\n constructor(minimumLogLevel: LogLevel) {\r\n this.minimumLogLevel = minimumLogLevel;\r\n this.outputConsole = console;\r\n }\r\n\r\n public log(logLevel: LogLevel, message: string): void {\r\n if (logLevel >= this.minimumLogLevel) {\r\n switch (logLevel) {\r\n case LogLevel.Critical:\r\n case LogLevel.Error:\r\n this.outputConsole.error(`[${new Date().toISOString()}] ${LogLevel[logLevel]}: ${message}`);\r\n break;\r\n case LogLevel.Warning:\r\n this.outputConsole.warn(`[${new Date().toISOString()}] ${LogLevel[logLevel]}: ${message}`);\r\n break;\r\n case LogLevel.Information:\r\n this.outputConsole.info(`[${new Date().toISOString()}] ${LogLevel[logLevel]}: ${message}`);\r\n break;\r\n default:\r\n // console.debug only goes to attached debuggers in Node, so we use console.log for Trace and Debug\r\n this.outputConsole.log(`[${new Date().toISOString()}] ${LogLevel[logLevel]}: ${message}`);\r\n break;\r\n }\r\n }\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\n\r\n/** A logger that does nothing when log messages are sent to it. */\r\nexport class NullLogger implements ILogger {\r\n /** The singleton instance of the {@link @microsoft/signalr.NullLogger}. */\r\n public static instance: ILogger = new NullLogger();\r\n\r\n private constructor() {}\r\n\r\n /** @inheritDoc */\r\n // tslint:disable-next-line\r\n public log(_logLevel: LogLevel, _message: string): void {\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { ILogger } from \"./ILogger\";\r\nimport { TransferFormat } from \"./ITransport\";\r\n\r\n/** Defines the type of a Hub Message. */\r\nexport enum MessageType {\r\n /** Indicates the message is an Invocation message and implements the {@link @microsoft/signalr.InvocationMessage} interface. */\r\n Invocation = 1,\r\n /** Indicates the message is a StreamItem message and implements the {@link @microsoft/signalr.StreamItemMessage} interface. */\r\n StreamItem = 2,\r\n /** Indicates the message is a Completion message and implements the {@link @microsoft/signalr.CompletionMessage} interface. */\r\n Completion = 3,\r\n /** Indicates the message is a Stream Invocation message and implements the {@link @microsoft/signalr.StreamInvocationMessage} interface. */\r\n StreamInvocation = 4,\r\n /** Indicates the message is a Cancel Invocation message and implements the {@link @microsoft/signalr.CancelInvocationMessage} interface. */\r\n CancelInvocation = 5,\r\n /** Indicates the message is a Ping message and implements the {@link @microsoft/signalr.PingMessage} interface. */\r\n Ping = 6,\r\n /** Indicates the message is a Close message and implements the {@link @microsoft/signalr.CloseMessage} interface. */\r\n Close = 7,\r\n}\r\n\r\n/** Defines a dictionary of string keys and string values representing headers attached to a Hub message. */\r\nexport interface MessageHeaders {\r\n /** Gets or sets the header with the specified key. */\r\n [key: string]: string;\r\n}\r\n\r\n/** Union type of all known Hub messages. */\r\nexport type HubMessage =\r\n InvocationMessage |\r\n StreamInvocationMessage |\r\n StreamItemMessage |\r\n CompletionMessage |\r\n CancelInvocationMessage |\r\n PingMessage |\r\n CloseMessage;\r\n\r\n/** Defines properties common to all Hub messages. */\r\nexport interface HubMessageBase {\r\n /** A {@link @microsoft/signalr.MessageType} value indicating the type of this message. */\r\n readonly type: MessageType;\r\n}\r\n\r\n/** Defines properties common to all Hub messages relating to a specific invocation. */\r\nexport interface HubInvocationMessage extends HubMessageBase {\r\n /** A {@link @microsoft/signalr.MessageHeaders} dictionary containing headers attached to the message. */\r\n readonly headers?: MessageHeaders;\r\n /** The ID of the invocation relating to this message.\r\n *\r\n * This is expected to be present for {@link @microsoft/signalr.StreamInvocationMessage} and {@link @microsoft/signalr.CompletionMessage}. It may\r\n * be 'undefined' for an {@link @microsoft/signalr.InvocationMessage} if the sender does not expect a response.\r\n */\r\n readonly invocationId?: string;\r\n}\r\n\r\n/** A hub message representing a non-streaming invocation. */\r\nexport interface InvocationMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.Invocation;\r\n /** The target method name. */\r\n readonly target: string;\r\n /** The target method arguments. */\r\n readonly arguments: any[];\r\n /** The target methods stream IDs. */\r\n readonly streamIds: string[];\r\n}\r\n\r\n/** A hub message representing a streaming invocation. */\r\nexport interface StreamInvocationMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.StreamInvocation;\r\n\r\n /** The invocation ID. */\r\n readonly invocationId: string;\r\n /** The target method name. */\r\n readonly target: string;\r\n /** The target method arguments. */\r\n readonly arguments: any[];\r\n /** The target methods stream IDs. */\r\n readonly streamIds: string[];\r\n}\r\n\r\n/** A hub message representing a single item produced as part of a result stream. */\r\nexport interface StreamItemMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.StreamItem;\r\n\r\n /** The invocation ID. */\r\n readonly invocationId: string;\r\n\r\n /** The item produced by the server. */\r\n readonly item?: any;\r\n}\r\n\r\n/** A hub message representing the result of an invocation. */\r\nexport interface CompletionMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.Completion;\r\n /** The invocation ID. */\r\n readonly invocationId: string;\r\n /** The error produced by the invocation, if any.\r\n *\r\n * Either {@link @microsoft/signalr.CompletionMessage.error} or {@link @microsoft/signalr.CompletionMessage.result} must be defined, but not both.\r\n */\r\n readonly error?: string;\r\n /** The result produced by the invocation, if any.\r\n *\r\n * Either {@link @microsoft/signalr.CompletionMessage.error} or {@link @microsoft/signalr.CompletionMessage.result} must be defined, but not both.\r\n */\r\n readonly result?: any;\r\n}\r\n\r\n/** A hub message indicating that the sender is still active. */\r\nexport interface PingMessage extends HubMessageBase {\r\n /** @inheritDoc */\r\n readonly type: MessageType.Ping;\r\n}\r\n\r\n/** A hub message indicating that the sender is closing the connection.\r\n *\r\n * If {@link @microsoft/signalr.CloseMessage.error} is defined, the sender is closing the connection due to an error.\r\n */\r\nexport interface CloseMessage extends HubMessageBase {\r\n /** @inheritDoc */\r\n readonly type: MessageType.Close;\r\n /** The error that triggered the close, if any.\r\n *\r\n * If this property is undefined, the connection was closed normally and without error.\r\n */\r\n readonly error?: string;\r\n\r\n /** If true, clients with automatic reconnects enabled should attempt to reconnect after receiving the CloseMessage. Otherwise, they should not. */\r\n readonly allowReconnect?: boolean;\r\n}\r\n\r\n/** A hub message sent to request that a streaming invocation be canceled. */\r\nexport interface CancelInvocationMessage extends HubInvocationMessage {\r\n /** @inheritDoc */\r\n readonly type: MessageType.CancelInvocation;\r\n /** The invocation ID. */\r\n readonly invocationId: string;\r\n}\r\n\r\n/** A protocol abstraction for communicating with SignalR Hubs. */\r\nexport interface IHubProtocol {\r\n /** The name of the protocol. This is used by SignalR to resolve the protocol between the client and server. */\r\n readonly name: string;\r\n /** The version of the protocol. */\r\n readonly version: number;\r\n /** The {@link @microsoft/signalr.TransferFormat} of the protocol. */\r\n readonly transferFormat: TransferFormat;\r\n\r\n /** Creates an array of {@link @microsoft/signalr.HubMessage} objects from the specified serialized representation.\r\n *\r\n * If {@link @microsoft/signalr.IHubProtocol.transferFormat} is 'Text', the `input` parameter must be a string, otherwise it must be an ArrayBuffer.\r\n *\r\n * @param {string | ArrayBuffer | Buffer} input A string, ArrayBuffer, or Buffer containing the serialized representation.\r\n * @param {ILogger} logger A logger that will be used to log messages that occur during parsing.\r\n */\r\n parseMessages(input: string | ArrayBuffer | Buffer, logger: ILogger): HubMessage[];\r\n\r\n /** Writes the specified {@link @microsoft/signalr.HubMessage} to a string or ArrayBuffer and returns it.\r\n *\r\n * If {@link @microsoft/signalr.IHubProtocol.transferFormat} is 'Text', the result of this method will be a string, otherwise it will be an ArrayBuffer.\r\n *\r\n * @param {HubMessage} message The message to write.\r\n * @returns {string | ArrayBuffer} A string or ArrayBuffer containing the serialized representation of the message.\r\n */\r\n writeMessage(message: HubMessage): string | ArrayBuffer;\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { IStreamResult, IStreamSubscriber, ISubscription } from \"./Stream\";\r\nimport { SubjectSubscription } from \"./Utils\";\r\n\r\n/** Stream implementation to stream items to the server. */\r\nexport class Subject implements IStreamResult {\r\n /** @internal */\r\n public observers: Array>;\r\n\r\n /** @internal */\r\n public cancelCallback?: () => Promise;\r\n\r\n constructor() {\r\n this.observers = [];\r\n }\r\n\r\n public next(item: T): void {\r\n for (const observer of this.observers) {\r\n observer.next(item);\r\n }\r\n }\r\n\r\n public error(err: any): void {\r\n for (const observer of this.observers) {\r\n if (observer.error) {\r\n observer.error(err);\r\n }\r\n }\r\n }\r\n\r\n public complete(): void {\r\n for (const observer of this.observers) {\r\n if (observer.complete) {\r\n observer.complete();\r\n }\r\n }\r\n }\r\n\r\n public subscribe(observer: IStreamSubscriber): ISubscription {\r\n this.observers.push(observer);\r\n return new SubjectSubscription(this, observer);\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { DefaultReconnectPolicy } from \"./DefaultReconnectPolicy\";\r\nimport { HttpConnection } from \"./HttpConnection\";\r\nimport { HubConnection } from \"./HubConnection\";\r\nimport { IHttpConnectionOptions } from \"./IHttpConnectionOptions\";\r\nimport { IHubProtocol } from \"./IHubProtocol\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { IRetryPolicy } from \"./IRetryPolicy\";\r\nimport { HttpTransportType } from \"./ITransport\";\r\nimport { JsonHubProtocol } from \"./JsonHubProtocol\";\r\nimport { NullLogger } from \"./Loggers\";\r\nimport { Arg, ConsoleLogger } from \"./Utils\";\r\n\r\n// tslint:disable:object-literal-sort-keys\r\nconst LogLevelNameMapping = {\r\n trace: LogLevel.Trace,\r\n debug: LogLevel.Debug,\r\n info: LogLevel.Information,\r\n information: LogLevel.Information,\r\n warn: LogLevel.Warning,\r\n warning: LogLevel.Warning,\r\n error: LogLevel.Error,\r\n critical: LogLevel.Critical,\r\n none: LogLevel.None,\r\n};\r\n\r\nfunction parseLogLevel(name: string): LogLevel {\r\n // Case-insensitive matching via lower-casing\r\n // Yes, I know case-folding is a complicated problem in Unicode, but we only support\r\n // the ASCII strings defined in LogLevelNameMapping anyway, so it's fine -anurse.\r\n const mapping = LogLevelNameMapping[name.toLowerCase()];\r\n if (typeof mapping !== \"undefined\") {\r\n return mapping;\r\n } else {\r\n throw new Error(`Unknown log level: ${name}`);\r\n }\r\n}\r\n\r\n/** A builder for configuring {@link @microsoft/signalr.HubConnection} instances. */\r\nexport class HubConnectionBuilder {\r\n /** @internal */\r\n public protocol?: IHubProtocol;\r\n /** @internal */\r\n public httpConnectionOptions?: IHttpConnectionOptions;\r\n /** @internal */\r\n public url?: string;\r\n /** @internal */\r\n public logger?: ILogger;\r\n\r\n /** If defined, this indicates the client should automatically attempt to reconnect if the connection is lost. */\r\n /** @internal */\r\n public reconnectPolicy?: IRetryPolicy;\r\n\r\n /** Configures console logging for the {@link @microsoft/signalr.HubConnection}.\r\n *\r\n * @param {LogLevel} logLevel The minimum level of messages to log. Anything at this level, or a more severe level, will be logged.\r\n * @returns The {@link @microsoft/signalr.HubConnectionBuilder} instance, for chaining.\r\n */\r\n public configureLogging(logLevel: LogLevel): HubConnectionBuilder;\r\n\r\n /** Configures custom logging for the {@link @microsoft/signalr.HubConnection}.\r\n *\r\n * @param {ILogger} logger An object implementing the {@link @microsoft/signalr.ILogger} interface, which will be used to write all log messages.\r\n * @returns The {@link @microsoft/signalr.HubConnectionBuilder} instance, for chaining.\r\n */\r\n public configureLogging(logger: ILogger): HubConnectionBuilder;\r\n\r\n /** Configures custom logging for the {@link @microsoft/signalr.HubConnection}.\r\n *\r\n * @param {string} logLevel A string representing a LogLevel setting a minimum level of messages to log.\r\n * See {@link https://docs.microsoft.com/en-us/aspnet/core/signalr/configuration#configure-logging|the documentation for client logging configuration} for more details.\r\n */\r\n public configureLogging(logLevel: string): HubConnectionBuilder;\r\n\r\n /** Configures custom logging for the {@link @microsoft/signalr.HubConnection}.\r\n *\r\n * @param {LogLevel | string | ILogger} logging A {@link @microsoft/signalr.LogLevel}, a string representing a LogLevel, or an object implementing the {@link @microsoft/signalr.ILogger} interface.\r\n * See {@link https://docs.microsoft.com/en-us/aspnet/core/signalr/configuration#configure-logging|the documentation for client logging configuration} for more details.\r\n * @returns The {@link @microsoft/signalr.HubConnectionBuilder} instance, for chaining.\r\n */\r\n public configureLogging(logging: LogLevel | string | ILogger): HubConnectionBuilder;\r\n public configureLogging(logging: LogLevel | string | ILogger): HubConnectionBuilder {\r\n Arg.isRequired(logging, \"logging\");\r\n\r\n if (isLogger(logging)) {\r\n this.logger = logging;\r\n } else if (typeof logging === \"string\") {\r\n const logLevel = parseLogLevel(logging);\r\n this.logger = new ConsoleLogger(logLevel);\r\n } else {\r\n this.logger = new ConsoleLogger(logging);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /** Configures the {@link @microsoft/signalr.HubConnection} to use HTTP-based transports to connect to the specified URL.\r\n *\r\n * The transport will be selected automatically based on what the server and client support.\r\n *\r\n * @param {string} url The URL the connection will use.\r\n * @returns The {@link @microsoft/signalr.HubConnectionBuilder} instance, for chaining.\r\n */\r\n public withUrl(url: string): HubConnectionBuilder;\r\n\r\n /** Configures the {@link @microsoft/signalr.HubConnection} to use the specified HTTP-based transport to connect to the specified URL.\r\n *\r\n * @param {string} url The URL the connection will use.\r\n * @param {HttpTransportType} transportType The specific transport to use.\r\n * @returns The {@link @microsoft/signalr.HubConnectionBuilder} instance, for chaining.\r\n */\r\n public withUrl(url: string, transportType: HttpTransportType): HubConnectionBuilder;\r\n\r\n /** Configures the {@link @microsoft/signalr.HubConnection} to use HTTP-based transports to connect to the specified URL.\r\n *\r\n * @param {string} url The URL the connection will use.\r\n * @param {IHttpConnectionOptions} options An options object used to configure the connection.\r\n * @returns The {@link @microsoft/signalr.HubConnectionBuilder} instance, for chaining.\r\n */\r\n public withUrl(url: string, options: IHttpConnectionOptions): HubConnectionBuilder;\r\n public withUrl(url: string, transportTypeOrOptions?: IHttpConnectionOptions | HttpTransportType): HubConnectionBuilder {\r\n Arg.isRequired(url, \"url\");\r\n\r\n this.url = url;\r\n\r\n // Flow-typing knows where it's at. Since HttpTransportType is a number and IHttpConnectionOptions is guaranteed\r\n // to be an object, we know (as does TypeScript) this comparison is all we need to figure out which overload was called.\r\n if (typeof transportTypeOrOptions === \"object\") {\r\n this.httpConnectionOptions = { ...this.httpConnectionOptions, ...transportTypeOrOptions };\r\n } else {\r\n this.httpConnectionOptions = {\r\n ...this.httpConnectionOptions,\r\n transport: transportTypeOrOptions,\r\n };\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /** Configures the {@link @microsoft/signalr.HubConnection} to use the specified Hub Protocol.\r\n *\r\n * @param {IHubProtocol} protocol The {@link @microsoft/signalr.IHubProtocol} implementation to use.\r\n */\r\n public withHubProtocol(protocol: IHubProtocol): HubConnectionBuilder {\r\n Arg.isRequired(protocol, \"protocol\");\r\n\r\n this.protocol = protocol;\r\n return this;\r\n }\r\n\r\n /** Configures the {@link @microsoft/signalr.HubConnection} to automatically attempt to reconnect if the connection is lost.\r\n * By default, the client will wait 0, 2, 10 and 30 seconds respectively before trying up to 4 reconnect attempts.\r\n */\r\n public withAutomaticReconnect(): HubConnectionBuilder;\r\n\r\n /** Configures the {@link @microsoft/signalr.HubConnection} to automatically attempt to reconnect if the connection is lost.\r\n *\r\n * @param {number[]} retryDelays An array containing the delays in milliseconds before trying each reconnect attempt.\r\n * The length of the array represents how many failed reconnect attempts it takes before the client will stop attempting to reconnect.\r\n */\r\n public withAutomaticReconnect(retryDelays: number[]): HubConnectionBuilder;\r\n\r\n /** Configures the {@link @microsoft/signalr.HubConnection} to automatically attempt to reconnect if the connection is lost.\r\n *\r\n * @param {IRetryPolicy} reconnectPolicy An {@link @microsoft/signalR.IRetryPolicy} that controls the timing and number of reconnect attempts.\r\n */\r\n public withAutomaticReconnect(reconnectPolicy: IRetryPolicy): HubConnectionBuilder;\r\n public withAutomaticReconnect(retryDelaysOrReconnectPolicy?: number[] | IRetryPolicy): HubConnectionBuilder {\r\n if (this.reconnectPolicy) {\r\n throw new Error(\"A reconnectPolicy has already been set.\");\r\n }\r\n\r\n if (!retryDelaysOrReconnectPolicy) {\r\n this.reconnectPolicy = new DefaultReconnectPolicy();\r\n } else if (Array.isArray(retryDelaysOrReconnectPolicy)) {\r\n this.reconnectPolicy = new DefaultReconnectPolicy(retryDelaysOrReconnectPolicy);\r\n } else {\r\n this.reconnectPolicy = retryDelaysOrReconnectPolicy;\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /** Creates a {@link @microsoft/signalr.HubConnection} from the configuration options specified in this builder.\r\n *\r\n * @returns {HubConnection} The configured {@link @microsoft/signalr.HubConnection}.\r\n */\r\n public build(): HubConnection {\r\n // If httpConnectionOptions has a logger, use it. Otherwise, override it with the one\r\n // provided to configureLogger\r\n const httpConnectionOptions = this.httpConnectionOptions || {};\r\n\r\n // If it's 'null', the user **explicitly** asked for null, don't mess with it.\r\n if (httpConnectionOptions.logger === undefined) {\r\n // If our logger is undefined or null, that's OK, the HttpConnection constructor will handle it.\r\n httpConnectionOptions.logger = this.logger;\r\n }\r\n\r\n // Now create the connection\r\n if (!this.url) {\r\n throw new Error(\"The 'HubConnectionBuilder.withUrl' method must be called before building the connection.\");\r\n }\r\n const connection = new HttpConnection(this.url, httpConnectionOptions);\r\n\r\n return HubConnection.create(\r\n connection,\r\n this.logger || NullLogger.instance,\r\n this.protocol || new JsonHubProtocol(),\r\n this.reconnectPolicy);\r\n }\r\n}\r\n\r\nfunction isLogger(logger: any): logger is ILogger {\r\n return logger.log !== undefined;\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { IRetryPolicy, RetryContext } from \"./IRetryPolicy\";\r\n\r\n// 0, 2, 10, 30 second delays before reconnect attempts.\r\nconst DEFAULT_RETRY_DELAYS_IN_MILLISECONDS = [0, 2000, 10000, 30000, null];\r\n\r\n/** @private */\r\nexport class DefaultReconnectPolicy implements IRetryPolicy {\r\n private readonly retryDelays: Array;\r\n\r\n constructor(retryDelays?: number[]) {\r\n this.retryDelays = retryDelays !== undefined ? [...retryDelays, null] : DEFAULT_RETRY_DELAYS_IN_MILLISECONDS;\r\n }\r\n\r\n public nextRetryDelayInMilliseconds(retryContext: RetryContext): number | null {\r\n return this.retryDelays[retryContext.previousRetryCount];\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { DefaultHttpClient } from \"./DefaultHttpClient\";\r\nimport { HttpClient } from \"./HttpClient\";\r\nimport { IConnection } from \"./IConnection\";\r\nimport { IHttpConnectionOptions } from \"./IHttpConnectionOptions\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { HttpTransportType, ITransport, TransferFormat } from \"./ITransport\";\r\nimport { LongPollingTransport } from \"./LongPollingTransport\";\r\nimport { ServerSentEventsTransport } from \"./ServerSentEventsTransport\";\r\nimport { Arg, createLogger, Platform } from \"./Utils\";\r\nimport { WebSocketTransport } from \"./WebSocketTransport\";\r\n\r\n/** @private */\r\nconst enum ConnectionState {\r\n Connecting = \"Connecting \",\r\n Connected = \"Connected\",\r\n Disconnected = \"Disconnected\",\r\n Disconnecting = \"Disconnecting\",\r\n}\r\n\r\n/** @private */\r\nexport interface INegotiateResponse {\r\n connectionId?: string;\r\n connectionToken?: string;\r\n negotiateVersion?: number;\r\n availableTransports?: IAvailableTransport[];\r\n url?: string;\r\n accessToken?: string;\r\n error?: string;\r\n}\r\n\r\n/** @private */\r\nexport interface IAvailableTransport {\r\n transport: keyof typeof HttpTransportType;\r\n transferFormats: Array;\r\n}\r\n\r\nconst MAX_REDIRECTS = 100;\r\n\r\nlet WebSocketModule: any = null;\r\nlet EventSourceModule: any = null;\r\nif (Platform.isNode && typeof require !== \"undefined\") {\r\n // In order to ignore the dynamic require in webpack builds we need to do this magic\r\n // @ts-ignore: TS doesn't know about these names\r\n const requireFunc = typeof __webpack_require__ === \"function\" ? __non_webpack_require__ : require;\r\n WebSocketModule = requireFunc(\"ws\");\r\n EventSourceModule = requireFunc(\"eventsource\");\r\n}\r\n\r\n/** @private */\r\nexport class HttpConnection implements IConnection {\r\n private connectionState: ConnectionState;\r\n // connectionStarted is tracked independently from connectionState, so we can check if the\r\n // connection ever did successfully transition from connecting to connected before disconnecting.\r\n private connectionStarted: boolean;\r\n private readonly httpClient: HttpClient;\r\n private readonly logger: ILogger;\r\n private readonly options: IHttpConnectionOptions;\r\n private transport?: ITransport;\r\n private startInternalPromise?: Promise;\r\n private stopPromise?: Promise;\r\n private stopPromiseResolver!: (value?: PromiseLike) => void;\r\n private stopError?: Error;\r\n private accessTokenFactory?: () => string | Promise;\r\n private sendQueue?: TransportSendQueue;\r\n\r\n public readonly features: any = {};\r\n public baseUrl: string;\r\n public connectionId?: string;\r\n public onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n public onclose: ((e?: Error) => void) | null;\r\n\r\n private readonly negotiateVersion: number = 1;\r\n\r\n constructor(url: string, options: IHttpConnectionOptions = {}) {\r\n Arg.isRequired(url, \"url\");\r\n\r\n this.logger = createLogger(options.logger);\r\n this.baseUrl = this.resolveUrl(url);\r\n\r\n options = options || {};\r\n options.logMessageContent = options.logMessageContent || false;\r\n\r\n if (!Platform.isNode && typeof WebSocket !== \"undefined\" && !options.WebSocket) {\r\n options.WebSocket = WebSocket;\r\n } else if (Platform.isNode && !options.WebSocket) {\r\n if (WebSocketModule) {\r\n options.WebSocket = WebSocketModule;\r\n }\r\n }\r\n\r\n if (!Platform.isNode && typeof EventSource !== \"undefined\" && !options.EventSource) {\r\n options.EventSource = EventSource;\r\n } else if (Platform.isNode && !options.EventSource) {\r\n if (typeof EventSourceModule !== \"undefined\") {\r\n options.EventSource = EventSourceModule;\r\n }\r\n }\r\n\r\n this.httpClient = options.httpClient || new DefaultHttpClient(this.logger);\r\n this.connectionState = ConnectionState.Disconnected;\r\n this.connectionStarted = false;\r\n this.options = options;\r\n\r\n this.onreceive = null;\r\n this.onclose = null;\r\n }\r\n\r\n public start(): Promise;\r\n public start(transferFormat: TransferFormat): Promise;\r\n public async start(transferFormat?: TransferFormat): Promise {\r\n transferFormat = transferFormat || TransferFormat.Binary;\r\n\r\n Arg.isIn(transferFormat, TransferFormat, \"transferFormat\");\r\n\r\n this.logger.log(LogLevel.Debug, `Starting connection with transfer format '${TransferFormat[transferFormat]}'.`);\r\n\r\n if (this.connectionState !== ConnectionState.Disconnected) {\r\n return Promise.reject(new Error(\"Cannot start an HttpConnection that is not in the 'Disconnected' state.\"));\r\n }\r\n\r\n this.connectionState = ConnectionState.Connecting;\r\n\r\n this.startInternalPromise = this.startInternal(transferFormat);\r\n await this.startInternalPromise;\r\n\r\n // The TypeScript compiler thinks that connectionState must be Connecting here. The TypeScript compiler is wrong.\r\n if (this.connectionState as any === ConnectionState.Disconnecting) {\r\n // stop() was called and transitioned the client into the Disconnecting state.\r\n const message = \"Failed to start the HttpConnection before stop() was called.\";\r\n this.logger.log(LogLevel.Error, message);\r\n\r\n // We cannot await stopPromise inside startInternal since stopInternal awaits the startInternalPromise.\r\n await this.stopPromise;\r\n\r\n return Promise.reject(new Error(message));\r\n } else if (this.connectionState as any !== ConnectionState.Connected) {\r\n // stop() was called and transitioned the client into the Disconnecting state.\r\n const message = \"HttpConnection.startInternal completed gracefully but didn't enter the connection into the connected state!\";\r\n this.logger.log(LogLevel.Error, message);\r\n return Promise.reject(new Error(message));\r\n }\r\n\r\n this.connectionStarted = true;\r\n }\r\n\r\n public send(data: string | ArrayBuffer): Promise {\r\n if (this.connectionState !== ConnectionState.Connected) {\r\n return Promise.reject(new Error(\"Cannot send data if the connection is not in the 'Connected' State.\"));\r\n }\r\n\r\n if (!this.sendQueue) {\r\n this.sendQueue = new TransportSendQueue(this.transport!);\r\n }\r\n\r\n // Transport will not be null if state is connected\r\n return this.sendQueue.send(data);\r\n }\r\n\r\n public async stop(error?: Error): Promise {\r\n if (this.connectionState === ConnectionState.Disconnected) {\r\n this.logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnected state.`);\r\n return Promise.resolve();\r\n }\r\n\r\n if (this.connectionState === ConnectionState.Disconnecting) {\r\n this.logger.log(LogLevel.Debug, `Call to HttpConnection.stop(${error}) ignored because the connection is already in the disconnecting state.`);\r\n return this.stopPromise;\r\n }\r\n\r\n this.connectionState = ConnectionState.Disconnecting;\r\n\r\n this.stopPromise = new Promise((resolve) => {\r\n // Don't complete stop() until stopConnection() completes.\r\n this.stopPromiseResolver = resolve;\r\n });\r\n\r\n // stopInternal should never throw so just observe it.\r\n await this.stopInternal(error);\r\n await this.stopPromise;\r\n }\r\n\r\n private async stopInternal(error?: Error): Promise {\r\n // Set error as soon as possible otherwise there is a race between\r\n // the transport closing and providing an error and the error from a close message\r\n // We would prefer the close message error.\r\n this.stopError = error;\r\n\r\n try {\r\n await this.startInternalPromise;\r\n } catch (e) {\r\n // This exception is returned to the user as a rejected Promise from the start method.\r\n }\r\n\r\n if (this.sendQueue) {\r\n try {\r\n await this.sendQueue.stop();\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, `TransportSendQueue.stop() threw error '${e}'.`);\r\n }\r\n this.sendQueue = undefined;\r\n }\r\n\r\n // The transport's onclose will trigger stopConnection which will run our onclose event.\r\n // The transport should always be set if currently connected. If it wasn't set, it's likely because\r\n // stop was called during start() and start() failed.\r\n if (this.transport) {\r\n try {\r\n await this.transport.stop();\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, `HttpConnection.transport.stop() threw error '${e}'.`);\r\n this.stopConnection();\r\n }\r\n\r\n this.transport = undefined;\r\n } else {\r\n this.logger.log(LogLevel.Debug, \"HttpConnection.transport is undefined in HttpConnection.stop() because start() failed.\");\r\n this.stopConnection();\r\n }\r\n }\r\n\r\n private async startInternal(transferFormat: TransferFormat): Promise {\r\n // Store the original base url and the access token factory since they may change\r\n // as part of negotiating\r\n let url = this.baseUrl;\r\n this.accessTokenFactory = this.options.accessTokenFactory;\r\n\r\n try {\r\n if (this.options.skipNegotiation) {\r\n if (this.options.transport === HttpTransportType.WebSockets) {\r\n // No need to add a connection ID in this case\r\n this.transport = this.constructTransport(HttpTransportType.WebSockets);\r\n // We should just call connect directly in this case.\r\n // No fallback or negotiate in this case.\r\n await this.startTransport(url, transferFormat);\r\n } else {\r\n throw new Error(\"Negotiation can only be skipped when using the WebSocket transport directly.\");\r\n }\r\n } else {\r\n let negotiateResponse: INegotiateResponse | null = null;\r\n let redirects = 0;\r\n\r\n do {\r\n negotiateResponse = await this.getNegotiationResponse(url);\r\n // the user tries to stop the connection when it is being started\r\n if (this.connectionState === ConnectionState.Disconnecting || this.connectionState === ConnectionState.Disconnected) {\r\n throw new Error(\"The connection was stopped during negotiation.\");\r\n }\r\n\r\n if (negotiateResponse.error) {\r\n throw new Error(negotiateResponse.error);\r\n }\r\n\r\n if ((negotiateResponse as any).ProtocolVersion) {\r\n throw new Error(\"Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.\");\r\n }\r\n\r\n if (negotiateResponse.url) {\r\n url = negotiateResponse.url;\r\n }\r\n\r\n if (negotiateResponse.accessToken) {\r\n // Replace the current access token factory with one that uses\r\n // the returned access token\r\n const accessToken = negotiateResponse.accessToken;\r\n this.accessTokenFactory = () => accessToken;\r\n }\r\n\r\n redirects++;\r\n }\r\n while (negotiateResponse.url && redirects < MAX_REDIRECTS);\r\n\r\n if (redirects === MAX_REDIRECTS && negotiateResponse.url) {\r\n throw new Error(\"Negotiate redirection limit exceeded.\");\r\n }\r\n\r\n await this.createTransport(url, this.options.transport, negotiateResponse, transferFormat);\r\n }\r\n\r\n if (this.transport instanceof LongPollingTransport) {\r\n this.features.inherentKeepAlive = true;\r\n }\r\n\r\n if (this.connectionState === ConnectionState.Connecting) {\r\n // Ensure the connection transitions to the connected state prior to completing this.startInternalPromise.\r\n // start() will handle the case when stop was called and startInternal exits still in the disconnecting state.\r\n this.logger.log(LogLevel.Debug, \"The HttpConnection connected successfully.\");\r\n this.connectionState = ConnectionState.Connected;\r\n }\r\n\r\n // stop() is waiting on us via this.startInternalPromise so keep this.transport around so it can clean up.\r\n // This is the only case startInternal can exit in neither the connected nor disconnected state because stopConnection()\r\n // will transition to the disconnected state. start() will wait for the transition using the stopPromise.\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, \"Failed to start the connection: \" + e);\r\n this.connectionState = ConnectionState.Disconnected;\r\n this.transport = undefined;\r\n return Promise.reject(e);\r\n }\r\n }\r\n\r\n private async getNegotiationResponse(url: string): Promise {\r\n let headers;\r\n if (this.accessTokenFactory) {\r\n const token = await this.accessTokenFactory();\r\n if (token) {\r\n headers = {\r\n [\"Authorization\"]: `Bearer ${token}`,\r\n };\r\n }\r\n }\r\n\r\n const negotiateUrl = this.resolveNegotiateUrl(url);\r\n this.logger.log(LogLevel.Debug, `Sending negotiation request: ${negotiateUrl}.`);\r\n try {\r\n const response = await this.httpClient.post(negotiateUrl, {\r\n content: \"\",\r\n headers,\r\n });\r\n\r\n if (response.statusCode !== 200) {\r\n return Promise.reject(new Error(`Unexpected status code returned from negotiate ${response.statusCode}`));\r\n }\r\n\r\n const negotiateResponse = JSON.parse(response.content as string) as INegotiateResponse;\r\n if (!negotiateResponse.negotiateVersion || negotiateResponse.negotiateVersion < 1) {\r\n // Negotiate version 0 doesn't use connectionToken\r\n // So we set it equal to connectionId so all our logic can use connectionToken without being aware of the negotiate version\r\n negotiateResponse.connectionToken = negotiateResponse.connectionId;\r\n }\r\n return negotiateResponse;\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, \"Failed to complete negotiation with the server: \" + e);\r\n return Promise.reject(e);\r\n }\r\n }\r\n\r\n private createConnectUrl(url: string, connectionToken: string | null | undefined) {\r\n if (!connectionToken) {\r\n return url;\r\n }\r\n\r\n return url + (url.indexOf(\"?\") === -1 ? \"?\" : \"&\") + `id=${connectionToken}`;\r\n }\r\n\r\n private async createTransport(url: string, requestedTransport: HttpTransportType | ITransport | undefined, negotiateResponse: INegotiateResponse, requestedTransferFormat: TransferFormat): Promise {\r\n let connectUrl = this.createConnectUrl(url, negotiateResponse.connectionToken);\r\n if (this.isITransport(requestedTransport)) {\r\n this.logger.log(LogLevel.Debug, \"Connection was provided an instance of ITransport, using that directly.\");\r\n this.transport = requestedTransport;\r\n await this.startTransport(connectUrl, requestedTransferFormat);\r\n\r\n this.connectionId = negotiateResponse.connectionId;\r\n return;\r\n }\r\n\r\n const transportExceptions: any[] = [];\r\n const transports = negotiateResponse.availableTransports || [];\r\n let negotiate: INegotiateResponse | undefined = negotiateResponse;\r\n for (const endpoint of transports) {\r\n const transportOrError = this.resolveTransportOrError(endpoint, requestedTransport, requestedTransferFormat);\r\n if (transportOrError instanceof Error) {\r\n // Store the error and continue, we don't want to cause a re-negotiate in these cases\r\n transportExceptions.push(`${endpoint.transport} failed: ${transportOrError}`);\r\n } else if (this.isITransport(transportOrError)) {\r\n this.transport = transportOrError;\r\n if (!negotiate) {\r\n try {\r\n negotiate = await this.getNegotiationResponse(url);\r\n } catch (ex) {\r\n return Promise.reject(ex);\r\n }\r\n connectUrl = this.createConnectUrl(url, negotiate.connectionToken);\r\n }\r\n try {\r\n await this.startTransport(connectUrl, requestedTransferFormat);\r\n this.connectionId = negotiate.connectionId;\r\n return;\r\n } catch (ex) {\r\n this.logger.log(LogLevel.Error, `Failed to start the transport '${endpoint.transport}': ${ex}`);\r\n negotiate = undefined;\r\n transportExceptions.push(`${endpoint.transport} failed: ${ex}`);\r\n\r\n if (this.connectionState !== ConnectionState.Connecting) {\r\n const message = \"Failed to select transport before stop() was called.\";\r\n this.logger.log(LogLevel.Debug, message);\r\n return Promise.reject(new Error(message));\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (transportExceptions.length > 0) {\r\n return Promise.reject(new Error(`Unable to connect to the server with any of the available transports. ${transportExceptions.join(\" \")}`));\r\n }\r\n return Promise.reject(new Error(\"None of the transports supported by the client are supported by the server.\"));\r\n }\r\n\r\n private constructTransport(transport: HttpTransportType): ITransport {\r\n switch (transport) {\r\n case HttpTransportType.WebSockets:\r\n if (!this.options.WebSocket) {\r\n throw new Error(\"'WebSocket' is not supported in your environment.\");\r\n }\r\n return new WebSocketTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.WebSocket);\r\n case HttpTransportType.ServerSentEvents:\r\n if (!this.options.EventSource) {\r\n throw new Error(\"'EventSource' is not supported in your environment.\");\r\n }\r\n return new ServerSentEventsTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.EventSource);\r\n case HttpTransportType.LongPolling:\r\n return new LongPollingTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false);\r\n default:\r\n throw new Error(`Unknown transport: ${transport}.`);\r\n }\r\n }\r\n\r\n private startTransport(url: string, transferFormat: TransferFormat): Promise {\r\n this.transport!.onreceive = this.onreceive;\r\n this.transport!.onclose = (e) => this.stopConnection(e);\r\n return this.transport!.connect(url, transferFormat);\r\n }\r\n\r\n private resolveTransportOrError(endpoint: IAvailableTransport, requestedTransport: HttpTransportType | undefined, requestedTransferFormat: TransferFormat): ITransport | Error {\r\n const transport = HttpTransportType[endpoint.transport];\r\n if (transport === null || transport === undefined) {\r\n this.logger.log(LogLevel.Debug, `Skipping transport '${endpoint.transport}' because it is not supported by this client.`);\r\n return new Error(`Skipping transport '${endpoint.transport}' because it is not supported by this client.`);\r\n } else {\r\n if (transportMatches(requestedTransport, transport)) {\r\n const transferFormats = endpoint.transferFormats.map((s) => TransferFormat[s]);\r\n if (transferFormats.indexOf(requestedTransferFormat) >= 0) {\r\n if ((transport === HttpTransportType.WebSockets && !this.options.WebSocket) ||\r\n (transport === HttpTransportType.ServerSentEvents && !this.options.EventSource)) {\r\n this.logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it is not supported in your environment.'`);\r\n return new Error(`'${HttpTransportType[transport]}' is not supported in your environment.`);\r\n } else {\r\n this.logger.log(LogLevel.Debug, `Selecting transport '${HttpTransportType[transport]}'.`);\r\n try {\r\n return this.constructTransport(transport);\r\n } catch (ex) {\r\n return ex;\r\n }\r\n }\r\n } else {\r\n this.logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it does not support the requested transfer format '${TransferFormat[requestedTransferFormat]}'.`);\r\n return new Error(`'${HttpTransportType[transport]}' does not support ${TransferFormat[requestedTransferFormat]}.`);\r\n }\r\n } else {\r\n this.logger.log(LogLevel.Debug, `Skipping transport '${HttpTransportType[transport]}' because it was disabled by the client.`);\r\n return new Error(`'${HttpTransportType[transport]}' is disabled by the client.`);\r\n }\r\n }\r\n }\r\n\r\n private isITransport(transport: any): transport is ITransport {\r\n return transport && typeof (transport) === \"object\" && \"connect\" in transport;\r\n }\r\n\r\n private stopConnection(error?: Error): void {\r\n this.logger.log(LogLevel.Debug, `HttpConnection.stopConnection(${error}) called while in state ${this.connectionState}.`);\r\n\r\n this.transport = undefined;\r\n\r\n // If we have a stopError, it takes precedence over the error from the transport\r\n error = this.stopError || error;\r\n this.stopError = undefined;\r\n\r\n if (this.connectionState === ConnectionState.Disconnected) {\r\n this.logger.log(LogLevel.Debug, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection is already in the disconnected state.`);\r\n return;\r\n }\r\n\r\n if (this.connectionState === ConnectionState.Connecting) {\r\n this.logger.log(LogLevel.Warning, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection hasn't yet left the in the connecting state.`);\r\n return;\r\n }\r\n\r\n if (this.connectionState === ConnectionState.Disconnecting) {\r\n // A call to stop() induced this call to stopConnection and needs to be completed.\r\n // Any stop() awaiters will be scheduled to continue after the onclose callback fires.\r\n this.stopPromiseResolver();\r\n }\r\n\r\n if (error) {\r\n this.logger.log(LogLevel.Error, `Connection disconnected with error '${error}'.`);\r\n } else {\r\n this.logger.log(LogLevel.Information, \"Connection disconnected.\");\r\n }\r\n\r\n this.connectionId = undefined;\r\n this.connectionState = ConnectionState.Disconnected;\r\n\r\n if (this.onclose && this.connectionStarted) {\r\n this.connectionStarted = false;\r\n\r\n try {\r\n this.onclose(error);\r\n } catch (e) {\r\n this.logger.log(LogLevel.Error, `HttpConnection.onclose(${error}) threw error '${e}'.`);\r\n }\r\n }\r\n }\r\n\r\n private resolveUrl(url: string): string {\r\n // startsWith is not supported in IE\r\n if (url.lastIndexOf(\"https://\", 0) === 0 || url.lastIndexOf(\"http://\", 0) === 0) {\r\n return url;\r\n }\r\n\r\n if (!Platform.isBrowser || !window.document) {\r\n throw new Error(`Cannot resolve '${url}'.`);\r\n }\r\n\r\n // Setting the url to the href propery of an anchor tag handles normalization\r\n // for us. There are 3 main cases.\r\n // 1. Relative path normalization e.g \"b\" -> \"http://localhost:5000/a/b\"\r\n // 2. Absolute path normalization e.g \"/a/b\" -> \"http://localhost:5000/a/b\"\r\n // 3. Networkpath reference normalization e.g \"//localhost:5000/a/b\" -> \"http://localhost:5000/a/b\"\r\n const aTag = window.document.createElement(\"a\");\r\n aTag.href = url;\r\n\r\n this.logger.log(LogLevel.Information, `Normalizing '${url}' to '${aTag.href}'.`);\r\n return aTag.href;\r\n }\r\n\r\n private resolveNegotiateUrl(url: string): string {\r\n const index = url.indexOf(\"?\");\r\n let negotiateUrl = url.substring(0, index === -1 ? url.length : index);\r\n if (negotiateUrl[negotiateUrl.length - 1] !== \"/\") {\r\n negotiateUrl += \"/\";\r\n }\r\n negotiateUrl += \"negotiate\";\r\n negotiateUrl += index === -1 ? \"\" : url.substring(index);\r\n\r\n if (negotiateUrl.indexOf(\"negotiateVersion\") === -1) {\r\n negotiateUrl += index === -1 ? \"?\" : \"&\";\r\n negotiateUrl += \"negotiateVersion=\" + this.negotiateVersion;\r\n }\r\n return negotiateUrl;\r\n }\r\n}\r\n\r\nfunction transportMatches(requestedTransport: HttpTransportType | undefined, actualTransport: HttpTransportType) {\r\n return !requestedTransport || ((actualTransport & requestedTransport) !== 0);\r\n}\r\n\r\n/** @private */\r\nexport class TransportSendQueue {\r\n private buffer: any[] = [];\r\n private sendBufferedData: PromiseSource;\r\n private executing: boolean = true;\r\n private transportResult?: PromiseSource;\r\n private sendLoopPromise: Promise;\r\n\r\n constructor(private readonly transport: ITransport) {\r\n this.sendBufferedData = new PromiseSource();\r\n this.transportResult = new PromiseSource();\r\n\r\n this.sendLoopPromise = this.sendLoop();\r\n }\r\n\r\n public send(data: string | ArrayBuffer): Promise {\r\n this.bufferData(data);\r\n if (!this.transportResult) {\r\n this.transportResult = new PromiseSource();\r\n }\r\n return this.transportResult.promise;\r\n }\r\n\r\n public stop(): Promise {\r\n this.executing = false;\r\n this.sendBufferedData.resolve();\r\n return this.sendLoopPromise;\r\n }\r\n\r\n private bufferData(data: string | ArrayBuffer): void {\r\n if (this.buffer.length && typeof(this.buffer[0]) !== typeof(data)) {\r\n throw new Error(`Expected data to be of type ${typeof(this.buffer)} but was of type ${typeof(data)}`);\r\n }\r\n\r\n this.buffer.push(data);\r\n this.sendBufferedData.resolve();\r\n }\r\n\r\n private async sendLoop(): Promise {\r\n while (true) {\r\n await this.sendBufferedData.promise;\r\n\r\n if (!this.executing) {\r\n if (this.transportResult) {\r\n this.transportResult.reject(\"Connection stopped.\");\r\n }\r\n\r\n break;\r\n }\r\n\r\n this.sendBufferedData = new PromiseSource();\r\n\r\n const transportResult = this.transportResult!;\r\n this.transportResult = undefined;\r\n\r\n const data = typeof(this.buffer[0]) === \"string\" ?\r\n this.buffer.join(\"\") :\r\n TransportSendQueue.concatBuffers(this.buffer);\r\n\r\n this.buffer.length = 0;\r\n\r\n try {\r\n await this.transport.send(data);\r\n transportResult.resolve();\r\n } catch (error) {\r\n transportResult.reject(error);\r\n }\r\n }\r\n }\r\n\r\n private static concatBuffers(arrayBuffers: ArrayBuffer[]): ArrayBuffer {\r\n const totalLength = arrayBuffers.map((b) => b.byteLength).reduce((a, b) => a + b);\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const item of arrayBuffers) {\r\n result.set(new Uint8Array(item), offset);\r\n offset += item.byteLength;\r\n }\r\n\r\n return result;\r\n }\r\n}\r\n\r\nclass PromiseSource {\r\n private resolver?: () => void;\r\n private rejecter!: (reason?: any) => void;\r\n public promise: Promise;\r\n\r\n constructor() {\r\n this.promise = new Promise((resolve, reject) => [this.resolver, this.rejecter] = [resolve, reject]);\r\n }\r\n\r\n public resolve(): void {\r\n this.resolver!();\r\n }\r\n\r\n public reject(reason?: any): void {\r\n this.rejecter!(reason);\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\n// This will be treated as a bit flag in the future, so we keep it using power-of-two values.\r\n/** Specifies a specific HTTP transport type. */\r\nexport enum HttpTransportType {\r\n /** Specifies no transport preference. */\r\n None = 0,\r\n /** Specifies the WebSockets transport. */\r\n WebSockets = 1,\r\n /** Specifies the Server-Sent Events transport. */\r\n ServerSentEvents = 2,\r\n /** Specifies the Long Polling transport. */\r\n LongPolling = 4,\r\n}\r\n\r\n/** Specifies the transfer format for a connection. */\r\nexport enum TransferFormat {\r\n /** Specifies that only text data will be transmitted over the connection. */\r\n Text = 1,\r\n /** Specifies that binary data will be transmitted over the connection. */\r\n Binary = 2,\r\n}\r\n\r\n/** An abstraction over the behavior of transports. This is designed to support the framework and not intended for use by applications. */\r\nexport interface ITransport {\r\n connect(url: string, transferFormat: TransferFormat): Promise;\r\n send(data: any): Promise;\r\n stop(): Promise;\r\n onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n onclose: ((error?: Error) => void) | null;\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { AbortController } from \"./AbortController\";\r\nimport { HttpError, TimeoutError } from \"./Errors\";\r\nimport { HttpClient, HttpRequest } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { ITransport, TransferFormat } from \"./ITransport\";\r\nimport { Arg, getDataDetail, sendMessage } from \"./Utils\";\r\n\r\n// Not exported from 'index', this type is internal.\r\n/** @private */\r\nexport class LongPollingTransport implements ITransport {\r\n private readonly httpClient: HttpClient;\r\n private readonly accessTokenFactory: (() => string | Promise) | undefined;\r\n private readonly logger: ILogger;\r\n private readonly logMessageContent: boolean;\r\n private readonly pollAbort: AbortController;\r\n\r\n private url?: string;\r\n private running: boolean;\r\n private receiving?: Promise;\r\n private closeError?: Error;\r\n\r\n public onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n public onclose: ((error?: Error) => void) | null;\r\n\r\n // This is an internal type, not exported from 'index' so this is really just internal.\r\n public get pollAborted() {\r\n return this.pollAbort.aborted;\r\n }\r\n\r\n constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise) | undefined, logger: ILogger, logMessageContent: boolean) {\r\n this.httpClient = httpClient;\r\n this.accessTokenFactory = accessTokenFactory;\r\n this.logger = logger;\r\n this.pollAbort = new AbortController();\r\n this.logMessageContent = logMessageContent;\r\n\r\n this.running = false;\r\n\r\n this.onreceive = null;\r\n this.onclose = null;\r\n }\r\n\r\n public async connect(url: string, transferFormat: TransferFormat): Promise {\r\n Arg.isRequired(url, \"url\");\r\n Arg.isRequired(transferFormat, \"transferFormat\");\r\n Arg.isIn(transferFormat, TransferFormat, \"transferFormat\");\r\n\r\n this.url = url;\r\n\r\n this.logger.log(LogLevel.Trace, \"(LongPolling transport) Connecting.\");\r\n\r\n // Allow binary format on Node and Browsers that support binary content (indicated by the presence of responseType property)\r\n if (transferFormat === TransferFormat.Binary &&\r\n (typeof XMLHttpRequest !== \"undefined\" && typeof new XMLHttpRequest().responseType !== \"string\")) {\r\n throw new Error(\"Binary protocols over XmlHttpRequest not implementing advanced features are not supported.\");\r\n }\r\n\r\n const pollOptions: HttpRequest = {\r\n abortSignal: this.pollAbort.signal,\r\n headers: {},\r\n timeout: 100000,\r\n };\r\n\r\n if (transferFormat === TransferFormat.Binary) {\r\n pollOptions.responseType = \"arraybuffer\";\r\n }\r\n\r\n const token = await this.getAccessToken();\r\n this.updateHeaderToken(pollOptions, token);\r\n\r\n // Make initial long polling request\r\n // Server uses first long polling request to finish initializing connection and it returns without data\r\n const pollUrl = `${url}&_=${Date.now()}`;\r\n this.logger.log(LogLevel.Trace, `(LongPolling transport) polling: ${pollUrl}.`);\r\n const response = await this.httpClient.get(pollUrl, pollOptions);\r\n if (response.statusCode !== 200) {\r\n this.logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}.`);\r\n\r\n // Mark running as false so that the poll immediately ends and runs the close logic\r\n this.closeError = new HttpError(response.statusText || \"\", response.statusCode);\r\n this.running = false;\r\n } else {\r\n this.running = true;\r\n }\r\n\r\n this.receiving = this.poll(this.url, pollOptions);\r\n }\r\n\r\n private async getAccessToken(): Promise {\r\n if (this.accessTokenFactory) {\r\n return await this.accessTokenFactory();\r\n }\r\n\r\n return null;\r\n }\r\n\r\n private updateHeaderToken(request: HttpRequest, token: string | null) {\r\n if (!request.headers) {\r\n request.headers = {};\r\n }\r\n if (token) {\r\n // tslint:disable-next-line:no-string-literal\r\n request.headers[\"Authorization\"] = `Bearer ${token}`;\r\n return;\r\n }\r\n // tslint:disable-next-line:no-string-literal\r\n if (request.headers[\"Authorization\"]) {\r\n // tslint:disable-next-line:no-string-literal\r\n delete request.headers[\"Authorization\"];\r\n }\r\n }\r\n\r\n private async poll(url: string, pollOptions: HttpRequest): Promise {\r\n try {\r\n while (this.running) {\r\n // We have to get the access token on each poll, in case it changes\r\n const token = await this.getAccessToken();\r\n this.updateHeaderToken(pollOptions, token);\r\n\r\n try {\r\n const pollUrl = `${url}&_=${Date.now()}`;\r\n this.logger.log(LogLevel.Trace, `(LongPolling transport) polling: ${pollUrl}.`);\r\n const response = await this.httpClient.get(pollUrl, pollOptions);\r\n\r\n if (response.statusCode === 204) {\r\n this.logger.log(LogLevel.Information, \"(LongPolling transport) Poll terminated by server.\");\r\n\r\n this.running = false;\r\n } else if (response.statusCode !== 200) {\r\n this.logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}.`);\r\n\r\n // Unexpected status code\r\n this.closeError = new HttpError(response.statusText || \"\", response.statusCode);\r\n this.running = false;\r\n } else {\r\n // Process the response\r\n if (response.content) {\r\n this.logger.log(LogLevel.Trace, `(LongPolling transport) data received. ${getDataDetail(response.content, this.logMessageContent)}.`);\r\n if (this.onreceive) {\r\n this.onreceive(response.content);\r\n }\r\n } else {\r\n // This is another way timeout manifest.\r\n this.logger.log(LogLevel.Trace, \"(LongPolling transport) Poll timed out, reissuing.\");\r\n }\r\n }\r\n } catch (e) {\r\n if (!this.running) {\r\n // Log but disregard errors that occur after stopping\r\n this.logger.log(LogLevel.Trace, `(LongPolling transport) Poll errored after shutdown: ${e.message}`);\r\n } else {\r\n if (e instanceof TimeoutError) {\r\n // Ignore timeouts and reissue the poll.\r\n this.logger.log(LogLevel.Trace, \"(LongPolling transport) Poll timed out, reissuing.\");\r\n } else {\r\n // Close the connection with the error as the result.\r\n this.closeError = e;\r\n this.running = false;\r\n }\r\n }\r\n }\r\n }\r\n } finally {\r\n this.logger.log(LogLevel.Trace, \"(LongPolling transport) Polling complete.\");\r\n\r\n // We will reach here with pollAborted==false when the server returned a response causing the transport to stop.\r\n // If pollAborted==true then client initiated the stop and the stop method will raise the close event after DELETE is sent.\r\n if (!this.pollAborted) {\r\n this.raiseOnClose();\r\n }\r\n }\r\n }\r\n\r\n public async send(data: any): Promise {\r\n if (!this.running) {\r\n return Promise.reject(new Error(\"Cannot send until the transport is connected\"));\r\n }\r\n return sendMessage(this.logger, \"LongPolling\", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent);\r\n }\r\n\r\n public async stop(): Promise {\r\n this.logger.log(LogLevel.Trace, \"(LongPolling transport) Stopping polling.\");\r\n\r\n // Tell receiving loop to stop, abort any current request, and then wait for it to finish\r\n this.running = false;\r\n this.pollAbort.abort();\r\n\r\n try {\r\n await this.receiving;\r\n\r\n // Send DELETE to clean up long polling on the server\r\n this.logger.log(LogLevel.Trace, `(LongPolling transport) sending DELETE request to ${this.url}.`);\r\n\r\n const deleteOptions: HttpRequest = {\r\n headers: {},\r\n };\r\n const token = await this.getAccessToken();\r\n this.updateHeaderToken(deleteOptions, token);\r\n await this.httpClient.delete(this.url!, deleteOptions);\r\n\r\n this.logger.log(LogLevel.Trace, \"(LongPolling transport) DELETE request sent.\");\r\n } finally {\r\n this.logger.log(LogLevel.Trace, \"(LongPolling transport) Stop finished.\");\r\n\r\n // Raise close event here instead of in polling\r\n // It needs to happen after the DELETE request is sent\r\n this.raiseOnClose();\r\n }\r\n }\r\n\r\n private raiseOnClose() {\r\n if (this.onclose) {\r\n let logMessage = \"(LongPolling transport) Firing onclose event.\";\r\n if (this.closeError) {\r\n logMessage += \" Error: \" + this.closeError;\r\n }\r\n this.logger.log(LogLevel.Trace, logMessage);\r\n this.onclose(this.closeError);\r\n }\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\n// Rough polyfill of https://developer.mozilla.org/en-US/docs/Web/API/AbortController\r\n// We don't actually ever use the API being polyfilled, we always use the polyfill because\r\n// it's a very new API right now.\r\n\r\n// Not exported from index.\r\n/** @private */\r\nexport class AbortController implements AbortSignal {\r\n private isAborted: boolean = false;\r\n public onabort: (() => void) | null = null;\r\n\r\n public abort() {\r\n if (!this.isAborted) {\r\n this.isAborted = true;\r\n if (this.onabort) {\r\n this.onabort();\r\n }\r\n }\r\n }\r\n\r\n get signal(): AbortSignal {\r\n return this;\r\n }\r\n\r\n get aborted(): boolean {\r\n return this.isAborted;\r\n }\r\n}\r\n\r\n/** Represents a signal that can be monitored to determine if a request has been aborted. */\r\nexport interface AbortSignal {\r\n /** Indicates if the request has been aborted. */\r\n aborted: boolean;\r\n /** Set this to a handler that will be invoked when the request is aborted. */\r\n onabort: (() => void) | null;\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { HttpClient } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { ITransport, TransferFormat } from \"./ITransport\";\r\nimport { EventSourceConstructor } from \"./Polyfills\";\r\nimport { Arg, getDataDetail, Platform, sendMessage } from \"./Utils\";\r\n\r\n/** @private */\r\nexport class ServerSentEventsTransport implements ITransport {\r\n private readonly httpClient: HttpClient;\r\n private readonly accessTokenFactory: (() => string | Promise) | undefined;\r\n private readonly logger: ILogger;\r\n private readonly logMessageContent: boolean;\r\n private readonly eventSourceConstructor: EventSourceConstructor;\r\n private eventSource?: EventSource;\r\n private url?: string;\r\n\r\n public onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n public onclose: ((error?: Error) => void) | null;\r\n\r\n constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise) | undefined, logger: ILogger,\r\n logMessageContent: boolean, eventSourceConstructor: EventSourceConstructor) {\r\n this.httpClient = httpClient;\r\n this.accessTokenFactory = accessTokenFactory;\r\n this.logger = logger;\r\n this.logMessageContent = logMessageContent;\r\n this.eventSourceConstructor = eventSourceConstructor;\r\n\r\n this.onreceive = null;\r\n this.onclose = null;\r\n }\r\n\r\n public async connect(url: string, transferFormat: TransferFormat): Promise {\r\n Arg.isRequired(url, \"url\");\r\n Arg.isRequired(transferFormat, \"transferFormat\");\r\n Arg.isIn(transferFormat, TransferFormat, \"transferFormat\");\r\n\r\n this.logger.log(LogLevel.Trace, \"(SSE transport) Connecting.\");\r\n\r\n // set url before accessTokenFactory because this.url is only for send and we set the auth header instead of the query string for send\r\n this.url = url;\r\n\r\n if (this.accessTokenFactory) {\r\n const token = await this.accessTokenFactory();\r\n if (token) {\r\n url += (url.indexOf(\"?\") < 0 ? \"?\" : \"&\") + `access_token=${encodeURIComponent(token)}`;\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n let opened = false;\r\n if (transferFormat !== TransferFormat.Text) {\r\n reject(new Error(\"The Server-Sent Events transport only supports the 'Text' transfer format\"));\r\n return;\r\n }\r\n\r\n let eventSource: EventSource;\r\n if (Platform.isBrowser || Platform.isWebWorker) {\r\n eventSource = new this.eventSourceConstructor(url, { withCredentials: true });\r\n } else {\r\n // Non-browser passes cookies via the dictionary\r\n const cookies = this.httpClient.getCookieString(url);\r\n eventSource = new this.eventSourceConstructor(url, { withCredentials: true, headers: { Cookie: cookies } } as EventSourceInit);\r\n }\r\n\r\n try {\r\n eventSource.onmessage = (e: MessageEvent) => {\r\n if (this.onreceive) {\r\n try {\r\n this.logger.log(LogLevel.Trace, `(SSE transport) data received. ${getDataDetail(e.data, this.logMessageContent)}.`);\r\n this.onreceive(e.data);\r\n } catch (error) {\r\n this.close(error);\r\n return;\r\n }\r\n }\r\n };\r\n\r\n eventSource.onerror = (e: MessageEvent) => {\r\n const error = new Error(e.data || \"Error occurred\");\r\n if (opened) {\r\n this.close(error);\r\n } else {\r\n reject(error);\r\n }\r\n };\r\n\r\n eventSource.onopen = () => {\r\n this.logger.log(LogLevel.Information, `SSE connected to ${this.url}`);\r\n this.eventSource = eventSource;\r\n opened = true;\r\n resolve();\r\n };\r\n } catch (e) {\r\n reject(e);\r\n return;\r\n }\r\n });\r\n }\r\n\r\n public async send(data: any): Promise {\r\n if (!this.eventSource) {\r\n return Promise.reject(new Error(\"Cannot send until the transport is connected\"));\r\n }\r\n return sendMessage(this.logger, \"SSE\", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent);\r\n }\r\n\r\n public stop(): Promise {\r\n this.close();\r\n return Promise.resolve();\r\n }\r\n\r\n private close(e?: Error) {\r\n if (this.eventSource) {\r\n this.eventSource.close();\r\n this.eventSource = undefined;\r\n\r\n if (this.onclose) {\r\n this.onclose(e);\r\n }\r\n }\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { HttpClient } from \"./HttpClient\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { ITransport, TransferFormat } from \"./ITransport\";\r\nimport { WebSocketConstructor } from \"./Polyfills\";\r\nimport { Arg, getDataDetail, Platform } from \"./Utils\";\r\n\r\n/** @private */\r\nexport class WebSocketTransport implements ITransport {\r\n private readonly logger: ILogger;\r\n private readonly accessTokenFactory: (() => string | Promise) | undefined;\r\n private readonly logMessageContent: boolean;\r\n private readonly webSocketConstructor: WebSocketConstructor;\r\n private readonly httpClient: HttpClient;\r\n private webSocket?: WebSocket;\r\n\r\n public onreceive: ((data: string | ArrayBuffer) => void) | null;\r\n public onclose: ((error?: Error) => void) | null;\r\n\r\n constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise) | undefined, logger: ILogger,\r\n logMessageContent: boolean, webSocketConstructor: WebSocketConstructor) {\r\n this.logger = logger;\r\n this.accessTokenFactory = accessTokenFactory;\r\n this.logMessageContent = logMessageContent;\r\n this.webSocketConstructor = webSocketConstructor;\r\n this.httpClient = httpClient;\r\n\r\n this.onreceive = null;\r\n this.onclose = null;\r\n }\r\n\r\n public async connect(url: string, transferFormat: TransferFormat): Promise {\r\n Arg.isRequired(url, \"url\");\r\n Arg.isRequired(transferFormat, \"transferFormat\");\r\n Arg.isIn(transferFormat, TransferFormat, \"transferFormat\");\r\n\r\n this.logger.log(LogLevel.Trace, \"(WebSockets transport) Connecting.\");\r\n\r\n if (this.accessTokenFactory) {\r\n const token = await this.accessTokenFactory();\r\n if (token) {\r\n url += (url.indexOf(\"?\") < 0 ? \"?\" : \"&\") + `access_token=${encodeURIComponent(token)}`;\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n url = url.replace(/^http/, \"ws\");\r\n let webSocket: WebSocket | undefined;\r\n const cookies = this.httpClient.getCookieString(url);\r\n let opened = false;\r\n\r\n if (Platform.isNode && cookies) {\r\n // Only pass cookies when in non-browser environments\r\n webSocket = new this.webSocketConstructor(url, undefined, {\r\n headers: {\r\n Cookie: `${cookies}`,\r\n },\r\n });\r\n }\r\n\r\n if (!webSocket) {\r\n // Chrome is not happy with passing 'undefined' as protocol\r\n webSocket = new this.webSocketConstructor(url);\r\n }\r\n\r\n if (transferFormat === TransferFormat.Binary) {\r\n webSocket.binaryType = \"arraybuffer\";\r\n }\r\n\r\n // tslint:disable-next-line:variable-name\r\n webSocket.onopen = (_event: Event) => {\r\n this.logger.log(LogLevel.Information, `WebSocket connected to ${url}.`);\r\n this.webSocket = webSocket;\r\n opened = true;\r\n resolve();\r\n };\r\n\r\n webSocket.onerror = (event: Event) => {\r\n let error: any = null;\r\n // ErrorEvent is a browser only type we need to check if the type exists before using it\r\n if (typeof ErrorEvent !== \"undefined\" && event instanceof ErrorEvent) {\r\n error = event.error;\r\n } else {\r\n error = new Error(\"There was an error with the transport.\");\r\n }\r\n\r\n reject(error);\r\n };\r\n\r\n webSocket.onmessage = (message: MessageEvent) => {\r\n this.logger.log(LogLevel.Trace, `(WebSockets transport) data received. ${getDataDetail(message.data, this.logMessageContent)}.`);\r\n if (this.onreceive) {\r\n this.onreceive(message.data);\r\n }\r\n };\r\n\r\n webSocket.onclose = (event: CloseEvent) => {\r\n // Don't call close handler if connection was never established\r\n // We'll reject the connect call instead\r\n if (opened) {\r\n this.close(event);\r\n } else {\r\n let error: any = null;\r\n // ErrorEvent is a browser only type we need to check if the type exists before using it\r\n if (typeof ErrorEvent !== \"undefined\" && event instanceof ErrorEvent) {\r\n error = event.error;\r\n } else {\r\n error = new Error(\"There was an error with the transport.\");\r\n }\r\n\r\n reject(error);\r\n }\r\n };\r\n });\r\n }\r\n\r\n public send(data: any): Promise {\r\n if (this.webSocket && this.webSocket.readyState === this.webSocketConstructor.OPEN) {\r\n this.logger.log(LogLevel.Trace, `(WebSockets transport) sending data. ${getDataDetail(data, this.logMessageContent)}.`);\r\n this.webSocket.send(data);\r\n return Promise.resolve();\r\n }\r\n\r\n return Promise.reject(\"WebSocket is not in the OPEN state\");\r\n }\r\n\r\n public stop(): Promise {\r\n if (this.webSocket) {\r\n // Clear websocket handlers because we are considering the socket closed now\r\n this.webSocket.onclose = () => {};\r\n this.webSocket.onmessage = () => {};\r\n this.webSocket.onerror = () => {};\r\n this.webSocket.close();\r\n this.webSocket = undefined;\r\n\r\n // Manually invoke onclose callback inline so we know the HttpConnection was closed properly before returning\r\n // This also solves an issue where websocket.onclose could take 18+ seconds to trigger during network disconnects\r\n this.close(undefined);\r\n }\r\n\r\n return Promise.resolve();\r\n }\r\n\r\n private close(event?: CloseEvent): void {\r\n // webSocket will be null if the transport did not start successfully\r\n this.logger.log(LogLevel.Trace, \"(WebSockets transport) socket closed.\");\r\n if (this.onclose) {\r\n if (event && (event.wasClean === false || event.code !== 1000)) {\r\n this.onclose(new Error(`WebSocket closed with status code: ${event.code} (${event.reason}).`));\r\n } else {\r\n this.onclose();\r\n }\r\n }\r\n }\r\n}\r\n","// Copyright (c) .NET Foundation. All rights reserved.\r\n// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\r\n\r\nimport { CompletionMessage, HubMessage, IHubProtocol, InvocationMessage, MessageType, StreamItemMessage } from \"./IHubProtocol\";\r\nimport { ILogger, LogLevel } from \"./ILogger\";\r\nimport { TransferFormat } from \"./ITransport\";\r\nimport { NullLogger } from \"./Loggers\";\r\nimport { TextMessageFormat } from \"./TextMessageFormat\";\r\n\r\nconst JSON_HUB_PROTOCOL_NAME: string = \"json\";\r\n\r\n/** Implements the JSON Hub Protocol. */\r\nexport class JsonHubProtocol implements IHubProtocol {\r\n\r\n /** @inheritDoc */\r\n public readonly name: string = JSON_HUB_PROTOCOL_NAME;\r\n /** @inheritDoc */\r\n public readonly version: number = 1;\r\n\r\n /** @inheritDoc */\r\n public readonly transferFormat: TransferFormat = TransferFormat.Text;\r\n\r\n /** Creates an array of {@link @microsoft/signalr.HubMessage} objects from the specified serialized representation.\r\n *\r\n * @param {string} input A string containing the serialized representation.\r\n * @param {ILogger} logger A logger that will be used to log messages that occur during parsing.\r\n */\r\n public parseMessages(input: string, logger: ILogger): HubMessage[] {\r\n // The interface does allow \"ArrayBuffer\" to be passed in, but this implementation does not. So let's throw a useful error.\r\n if (typeof input !== \"string\") {\r\n throw new Error(\"Invalid input for JSON hub protocol. Expected a string.\");\r\n }\r\n\r\n if (!input) {\r\n return [];\r\n }\r\n\r\n if (logger === null) {\r\n logger = NullLogger.instance;\r\n }\r\n\r\n // Parse the messages\r\n const messages = TextMessageFormat.parse(input);\r\n\r\n const hubMessages = [];\r\n for (const message of messages) {\r\n const parsedMessage = JSON.parse(message) as HubMessage;\r\n if (typeof parsedMessage.type !== \"number\") {\r\n throw new Error(\"Invalid payload.\");\r\n }\r\n switch (parsedMessage.type) {\r\n case MessageType.Invocation:\r\n this.isInvocationMessage(parsedMessage);\r\n break;\r\n case MessageType.StreamItem:\r\n this.isStreamItemMessage(parsedMessage);\r\n break;\r\n case MessageType.Completion:\r\n this.isCompletionMessage(parsedMessage);\r\n break;\r\n case MessageType.Ping:\r\n // Single value, no need to validate\r\n break;\r\n case MessageType.Close:\r\n // All optional values, no need to validate\r\n break;\r\n default:\r\n // Future protocol changes can add message types, old clients can ignore them\r\n logger.log(LogLevel.Information, \"Unknown message type '\" + parsedMessage.type + \"' ignored.\");\r\n continue;\r\n }\r\n hubMessages.push(parsedMessage);\r\n }\r\n\r\n return hubMessages;\r\n }\r\n\r\n /** Writes the specified {@link @microsoft/signalr.HubMessage} to a string and returns it.\r\n *\r\n * @param {HubMessage} message The message to write.\r\n * @returns {string} A string containing the serialized representation of the message.\r\n */\r\n public writeMessage(message: HubMessage): string {\r\n return TextMessageFormat.write(JSON.stringify(message));\r\n }\r\n\r\n private isInvocationMessage(message: InvocationMessage): void {\r\n this.assertNotEmptyString(message.target, \"Invalid payload for Invocation message.\");\r\n\r\n if (message.invocationId !== undefined) {\r\n this.assertNotEmptyString(message.invocationId, \"Invalid payload for Invocation message.\");\r\n }\r\n }\r\n\r\n private isStreamItemMessage(message: StreamItemMessage): void {\r\n this.assertNotEmptyString(message.invocationId, \"Invalid payload for StreamItem message.\");\r\n\r\n if (message.item === undefined) {\r\n throw new Error(\"Invalid payload for StreamItem message.\");\r\n }\r\n }\r\n\r\n private isCompletionMessage(message: CompletionMessage): void {\r\n if (message.result && message.error) {\r\n throw new Error(\"Invalid payload for Completion message.\");\r\n }\r\n\r\n if (!message.result && message.error) {\r\n this.assertNotEmptyString(message.error, \"Invalid payload for Completion message.\");\r\n }\r\n\r\n this.assertNotEmptyString(message.invocationId, \"Invalid payload for Completion message.\");\r\n }\r\n\r\n private assertNotEmptyString(value: any, errorMessage: string): void {\r\n if (typeof value !== \"string\" || value === \"\") {\r\n throw new Error(errorMessage);\r\n }\r\n }\r\n}\r\n"],"sourceRoot":""} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.min.js b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.min.js new file mode 100644 index 000000000..0f69eb928 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/signalr/signalr.min.js @@ -0,0 +1,17 @@ +(function webpackUniversalModuleDefinition(root,factory){if(typeof exports==="object"&&typeof module==="object")module.exports=factory();else if(typeof define==="function"&&define.amd)define([],factory);else if(typeof exports==="object")exports["signalR"]=factory();else root["signalR"]=factory()})(window,function(){return function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!=="undefined"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"})}Object.defineProperty(exports,"__esModule",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value==="object"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,"default",{enumerable:true,value:value});if(mode&2&&typeof value!="string")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module["default"]}:function getModuleExports(){return module};__webpack_require__.d(getter,"a",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p="";return __webpack_require__(__webpack_require__.s=0)}([function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);var es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(1);var es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(es6_promise_dist_es6_promise_auto_js__WEBPACK_IMPORTED_MODULE_0__);var _index__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(3);__webpack_require__.d(__webpack_exports__,"VERSION",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["VERSION"]});__webpack_require__.d(__webpack_exports__,"AbortError",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["AbortError"]});__webpack_require__.d(__webpack_exports__,"HttpError",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HttpError"]});__webpack_require__.d(__webpack_exports__,"TimeoutError",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["TimeoutError"]});__webpack_require__.d(__webpack_exports__,"HttpClient",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HttpClient"]});__webpack_require__.d(__webpack_exports__,"HttpResponse",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HttpResponse"]});__webpack_require__.d(__webpack_exports__,"DefaultHttpClient",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["DefaultHttpClient"]});__webpack_require__.d(__webpack_exports__,"HubConnection",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnection"]});__webpack_require__.d(__webpack_exports__,"HubConnectionState",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnectionState"]});__webpack_require__.d(__webpack_exports__,"HubConnectionBuilder",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HubConnectionBuilder"]});__webpack_require__.d(__webpack_exports__,"MessageType",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["MessageType"]});__webpack_require__.d(__webpack_exports__,"LogLevel",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["LogLevel"]});__webpack_require__.d(__webpack_exports__,"HttpTransportType",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["HttpTransportType"]});__webpack_require__.d(__webpack_exports__,"TransferFormat",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["TransferFormat"]});__webpack_require__.d(__webpack_exports__,"NullLogger",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["NullLogger"]});__webpack_require__.d(__webpack_exports__,"JsonHubProtocol",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["JsonHubProtocol"]});__webpack_require__.d(__webpack_exports__,"Subject",function(){return _index__WEBPACK_IMPORTED_MODULE_1__["Subject"]});if(!Uint8Array.prototype.indexOf){Object.defineProperty(Uint8Array.prototype,"indexOf",{value:Array.prototype.indexOf,writable:true})}if(!Uint8Array.prototype.slice){Object.defineProperty(Uint8Array.prototype,"slice",{value:function(start,end){return new Uint8Array(Array.prototype.slice.call(this,start,end))},writable:true})}if(!Uint8Array.prototype.forEach){Object.defineProperty(Uint8Array.prototype,"forEach",{value:Array.prototype.forEach,writable:true})}},function(module,exports,__webpack_require__){(function(global){var require; +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version v4.2.2+97478eb6 + */ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version v4.2.2+97478eb6 + */ +(function(global,factory){true?module.exports=factory():undefined})(this,function(){"use strict";function objectOrFunction(x){var type=typeof x;return x!==null&&(type==="object"||type==="function")}function isFunction(x){return typeof x==="function"}var _isArray=void 0;if(Array.isArray){_isArray=Array.isArray}else{_isArray=function(x){return Object.prototype.toString.call(x)==="[object Array]"}}var isArray=_isArray;var len=0;var vertxNext=void 0;var customSchedulerFn=void 0;var asap=function asap(callback,arg){queue[len]=callback;queue[len+1]=arg;len+=2;if(len===2){if(customSchedulerFn){customSchedulerFn(flush)}else{scheduleFlush()}}};function setScheduler(scheduleFn){customSchedulerFn=scheduleFn}function setAsap(asapFn){asap=asapFn}var browserWindow=typeof window!=="undefined"?window:undefined;var browserGlobal=browserWindow||{};var BrowserMutationObserver=browserGlobal.MutationObserver||browserGlobal.WebKitMutationObserver;var isNode=typeof self==="undefined"&&typeof process!=="undefined"&&{}.toString.call(process)==="[object process]";var isWorker=typeof Uint8ClampedArray!=="undefined"&&typeof importScripts!=="undefined"&&typeof MessageChannel!=="undefined";function useNextTick(){return function(){return process.nextTick(flush)}}function useVertxTimer(){if(typeof vertxNext!=="undefined"){return function(){vertxNext(flush)}}return useSetTimeout()}function useMutationObserver(){var iterations=0;var observer=new BrowserMutationObserver(flush);var node=document.createTextNode("");observer.observe(node,{characterData:true});return function(){node.data=iterations=++iterations%2}}function useMessageChannel(){var channel=new MessageChannel;channel.port1.onmessage=flush;return function(){return channel.port2.postMessage(0)}}function useSetTimeout(){var globalSetTimeout=setTimeout;return function(){return globalSetTimeout(flush,1)}}var queue=new Array(1e3);function flush(){for(var i=0;i=200&&xhr.status<300){resolve(new _HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpResponse"](xhr.status,xhr.statusText,xhr.response||xhr.responseText))}else{reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["HttpError"](xhr.statusText,xhr.status))}};xhr.onerror=function(){_this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Warning,"Error from HTTP request. "+xhr.status+": "+xhr.statusText+".");reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["HttpError"](xhr.statusText,xhr.status))};xhr.ontimeout=function(){_this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Warning,"Timeout from HTTP request.");reject(new _Errors__WEBPACK_IMPORTED_MODULE_0__["TimeoutError"])};xhr.send(request.content||"")})};return XhrHttpClient}(_HttpClient__WEBPACK_IMPORTED_MODULE_1__["HttpClient"])},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"LogLevel",function(){return LogLevel});var LogLevel;(function(LogLevel){LogLevel[LogLevel["Trace"]=0]="Trace";LogLevel[LogLevel["Debug"]=1]="Debug";LogLevel[LogLevel["Information"]=2]="Information";LogLevel[LogLevel["Warning"]=3]="Warning";LogLevel[LogLevel["Error"]=4]="Error";LogLevel[LogLevel["Critical"]=5]="Critical";LogLevel[LogLevel["None"]=6]="None"})(LogLevel||(LogLevel={}))},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"HubConnectionState",function(){return HubConnectionState});__webpack_require__.d(__webpack_exports__,"HubConnection",function(){return HubConnection});var _HandshakeProtocol__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(11);var _IHubProtocol__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(15);var _ILogger__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(9);var _Subject__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(16);var _Utils__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(13);var __awaiter=undefined&&undefined.__awaiter||function(thisArg,_arguments,P,generator){return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):new P(function(resolve){resolve(result.value)}).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};var __generator=undefined&&undefined.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]responseLength?binaryData.slice(responseLength).buffer:null}else{var textData=data;var separatorIndex=textData.indexOf(_TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__["TextMessageFormat"].RecordSeparator);if(separatorIndex===-1){throw new Error("Message is incomplete.")}var responseLength=separatorIndex+1;messageData=textData.substring(0,responseLength);remainingData=textData.length>responseLength?textData.substring(responseLength):null}var messages=_TextMessageFormat__WEBPACK_IMPORTED_MODULE_0__["TextMessageFormat"].parse(messageData);var response=JSON.parse(messages[0]);if(response.type){throw new Error("Expected a handshake response from the server.")}responseMessage=response;return[remainingData,responseMessage]};return HandshakeProtocol}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"TextMessageFormat",function(){return TextMessageFormat});var TextMessageFormat=function(){function TextMessageFormat(){}TextMessageFormat.write=function(output){return""+output+TextMessageFormat.RecordSeparator};TextMessageFormat.parse=function(input){if(input[input.length-1]!==TextMessageFormat.RecordSeparator){throw new Error("Message is incomplete.")}var messages=input.split(TextMessageFormat.RecordSeparator);messages.pop();return messages};TextMessageFormat.RecordSeparatorCode=30;TextMessageFormat.RecordSeparator=String.fromCharCode(TextMessageFormat.RecordSeparatorCode);return TextMessageFormat}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"Arg",function(){return Arg});__webpack_require__.d(__webpack_exports__,"Platform",function(){return Platform});__webpack_require__.d(__webpack_exports__,"getDataDetail",function(){return getDataDetail});__webpack_require__.d(__webpack_exports__,"formatArrayBuffer",function(){return formatArrayBuffer});__webpack_require__.d(__webpack_exports__,"isArrayBuffer",function(){return isArrayBuffer});__webpack_require__.d(__webpack_exports__,"sendMessage",function(){return sendMessage});__webpack_require__.d(__webpack_exports__,"createLogger",function(){return createLogger});__webpack_require__.d(__webpack_exports__,"SubjectSubscription",function(){return SubjectSubscription});__webpack_require__.d(__webpack_exports__,"ConsoleLogger",function(){return ConsoleLogger});var _ILogger__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(9);var _Loggers__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(14);var __awaiter=undefined&&undefined.__awaiter||function(thisArg,_arguments,P,generator){return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator["throw"](value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):new P(function(resolve){resolve(result.value)}).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};var __generator=undefined&&undefined.__generator||function(thisArg,body){var _={label:0,sent:function(){if(t[0]&1)throw t[1];return t[1]},trys:[],ops:[]},f,y,t,g;return g={next:verb(0),throw:verb(1),return:verb(2)},typeof Symbol==="function"&&(g[Symbol.iterator]=function(){return this}),g;function verb(n){return function(v){return step([n,v])}}function step(op){if(f)throw new TypeError("Generator is already executing.");while(_)try{if(f=1,y&&(t=op[0]&2?y["return"]:op[0]?y["throw"]||((t=y["return"])&&t.call(y),0):y.next)&&!(t=t.call(y,op[1])).done)return t;if(y=0,t)op=[op[0]&2,t.value];switch(op[0]){case 0:case 1:t=op;break;case 4:_.label++;return{value:op[1],done:false};case 5:_.label++;y=op[1];op=[0];continue;case 7:op=_.ops.pop();_.trys.pop();continue;default:if(!(t=_.trys,t=t.length>0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]-1){this.subject.observers.splice(index,1)}if(this.subject.observers.length===0&&this.subject.cancelCallback){this.subject.cancelCallback().catch(function(_){})}};return SubjectSubscription}();var ConsoleLogger=function(){function ConsoleLogger(minimumLogLevel){this.minimumLogLevel=minimumLogLevel;this.outputConsole=console}ConsoleLogger.prototype.log=function(logLevel,message){if(logLevel>=this.minimumLogLevel){switch(logLevel){case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Critical:case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Error:this.outputConsole.error("["+(new Date).toISOString()+"] "+_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel]+": "+message);break;case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Warning:this.outputConsole.warn("["+(new Date).toISOString()+"] "+_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel]+": "+message);break;case _ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"].Information:this.outputConsole.info("["+(new Date).toISOString()+"] "+_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel]+": "+message);break;default:this.outputConsole.log("["+(new Date).toISOString()+"] "+_ILogger__WEBPACK_IMPORTED_MODULE_0__["LogLevel"][logLevel]+": "+message);break}}};return ConsoleLogger}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"NullLogger",function(){return NullLogger});var NullLogger=function(){function NullLogger(){}NullLogger.prototype.log=function(_logLevel,_message){};NullLogger.instance=new NullLogger;return NullLogger}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"MessageType",function(){return MessageType});var MessageType;(function(MessageType){MessageType[MessageType["Invocation"]=1]="Invocation";MessageType[MessageType["StreamItem"]=2]="StreamItem";MessageType[MessageType["Completion"]=3]="Completion";MessageType[MessageType["StreamInvocation"]=4]="StreamInvocation";MessageType[MessageType["CancelInvocation"]=5]="CancelInvocation";MessageType[MessageType["Ping"]=6]="Ping";MessageType[MessageType["Close"]=7]="Close"})(MessageType||(MessageType={}))},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"Subject",function(){return Subject});var _Utils__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(13);var Subject=function(){function Subject(){this.observers=[]}Subject.prototype.next=function(item){for(var _i=0,_a=this.observers;_i<_a.length;_i++){var observer=_a[_i];observer.next(item)}};Subject.prototype.error=function(err){for(var _i=0,_a=this.observers;_i<_a.length;_i++){var observer=_a[_i];if(observer.error){observer.error(err)}}};Subject.prototype.complete=function(){for(var _i=0,_a=this.observers;_i<_a.length;_i++){var observer=_a[_i];if(observer.complete){observer.complete()}}};Subject.prototype.subscribe=function(observer){this.observers.push(observer);return new _Utils__WEBPACK_IMPORTED_MODULE_0__["SubjectSubscription"](this,observer)};return Subject}()},function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,"HubConnectionBuilder",function(){return HubConnectionBuilder});var _DefaultReconnectPolicy__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(18);var _HttpConnection__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(19);var _HubConnection__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(10);var _ILogger__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(9);var _JsonHubProtocol__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(25);var _Loggers__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(14);var _Utils__WEBPACK_IMPORTED_MODULE_6__=__webpack_require__(13);var __assign=undefined&&undefined.__assign||Object.assign||function(t){for(var s,i=1,n=arguments.length;i0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0){return[2,Promise.reject(new Error("Unable to connect to the server with any of the available transports. "+transportExceptions.join(" ")))]}return[2,Promise.reject(new Error("None of the transports supported by the client are supported by the server."))]}})})};HttpConnection.prototype.constructTransport=function(transport){switch(transport){case _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].WebSockets:if(!this.options.WebSocket){throw new Error("'WebSocket' is not supported in your environment.")}return new _WebSocketTransport__WEBPACK_IMPORTED_MODULE_6__["WebSocketTransport"](this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||false,this.options.WebSocket);case _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].ServerSentEvents:if(!this.options.EventSource){throw new Error("'EventSource' is not supported in your environment.")}return new _ServerSentEventsTransport__WEBPACK_IMPORTED_MODULE_4__["ServerSentEventsTransport"](this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||false,this.options.EventSource);case _ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].LongPolling:return new _LongPollingTransport__WEBPACK_IMPORTED_MODULE_3__["LongPollingTransport"](this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||false);default:throw new Error("Unknown transport: "+transport+".")}};HttpConnection.prototype.startTransport=function(url,transferFormat){var _this=this;this.transport.onreceive=this.onreceive;this.transport.onclose=function(e){return _this.stopConnection(e)};return this.transport.connect(url,transferFormat)};HttpConnection.prototype.resolveTransportOrError=function(endpoint,requestedTransport,requestedTransferFormat){var transport=_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][endpoint.transport];if(transport===null||transport===undefined){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Skipping transport '"+endpoint.transport+"' because it is not supported by this client.");return new Error("Skipping transport '"+endpoint.transport+"' because it is not supported by this client.")}else{if(transportMatches(requestedTransport,transport)){var transferFormats=endpoint.transferFormats.map(function(s){return _ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"][s]});if(transferFormats.indexOf(requestedTransferFormat)>=0){if(transport===_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].WebSockets&&!this.options.WebSocket||transport===_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"].ServerSentEvents&&!this.options.EventSource){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Skipping transport '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' because it is not supported in your environment.'");return new Error("'"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' is not supported in your environment.")}else{this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Selecting transport '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"'.");try{return this.constructTransport(transport)}catch(ex){return ex}}}else{this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Skipping transport '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' because it does not support the requested transfer format '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"][requestedTransferFormat]+"'.");return new Error("'"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' does not support "+_ITransport__WEBPACK_IMPORTED_MODULE_2__["TransferFormat"][requestedTransferFormat]+".")}}else{this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Skipping transport '"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' because it was disabled by the client.");return new Error("'"+_ITransport__WEBPACK_IMPORTED_MODULE_2__["HttpTransportType"][transport]+"' is disabled by the client.")}}};HttpConnection.prototype.isITransport=function(transport){return transport&&typeof transport==="object"&&"connect"in transport};HttpConnection.prototype.stopConnection=function(error){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"HttpConnection.stopConnection("+error+") called while in state "+this.connectionState+".");this.transport=undefined;error=this.stopError||error;this.stopError=undefined;if(this.connectionState==="Disconnected"){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Debug,"Call to HttpConnection.stopConnection("+error+") was ignored because the connection is already in the disconnected state.");return}if(this.connectionState==="Connecting "){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Warning,"Call to HttpConnection.stopConnection("+error+") was ignored because the connection hasn't yet left the in the connecting state.");return}if(this.connectionState==="Disconnecting"){this.stopPromiseResolver()}if(error){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error,"Connection disconnected with error '"+error+"'.")}else{this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Information,"Connection disconnected.")}this.connectionId=undefined;this.connectionState="Disconnected";if(this.onclose&&this.connectionStarted){this.connectionStarted=false;try{this.onclose(error)}catch(e){this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Error,"HttpConnection.onclose("+error+") threw error '"+e+"'.")}}};HttpConnection.prototype.resolveUrl=function(url){if(url.lastIndexOf("https://",0)===0||url.lastIndexOf("http://",0)===0){return url}if(!_Utils__WEBPACK_IMPORTED_MODULE_5__["Platform"].isBrowser||!window.document){throw new Error("Cannot resolve '"+url+"'.")}var aTag=window.document.createElement("a");aTag.href=url;this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_1__["LogLevel"].Information,"Normalizing '"+url+"' to '"+aTag.href+"'.");return aTag.href};HttpConnection.prototype.resolveNegotiateUrl=function(url){var index=url.indexOf("?");var negotiateUrl=url.substring(0,index===-1?url.length:index);if(negotiateUrl[negotiateUrl.length-1]!=="/"){negotiateUrl+="/"}negotiateUrl+="negotiate";negotiateUrl+=index===-1?"":url.substring(index);if(negotiateUrl.indexOf("negotiateVersion")===-1){negotiateUrl+=index===-1?"?":"&";negotiateUrl+="negotiateVersion="+this.negotiateVersion}return negotiateUrl};return HttpConnection}();function transportMatches(requestedTransport,actualTransport){return!requestedTransport||(actualTransport&requestedTransport)!==0}var TransportSendQueue=function(){function TransportSendQueue(transport){this.transport=transport;this.buffer=[];this.executing=true;this.sendBufferedData=new PromiseSource;this.transportResult=new PromiseSource;this.sendLoopPromise=this.sendLoop()}TransportSendQueue.prototype.send=function(data){this.bufferData(data);if(!this.transportResult){this.transportResult=new PromiseSource}return this.transportResult.promise};TransportSendQueue.prototype.stop=function(){this.executing=false;this.sendBufferedData.resolve();return this.sendLoopPromise};TransportSendQueue.prototype.bufferData=function(data){if(this.buffer.length&&typeof this.buffer[0]!==typeof data){throw new Error("Expected data to be of type "+typeof this.buffer+" but was of type "+typeof data)}this.buffer.push(data);this.sendBufferedData.resolve()};TransportSendQueue.prototype.sendLoop=function(){return __awaiter(this,void 0,void 0,function(){var transportResult,data,error_1;return __generator(this,function(_a){switch(_a.label){case 0:if(false){}return[4,this.sendBufferedData.promise];case 1:_a.sent();if(!this.executing){if(this.transportResult){this.transportResult.reject("Connection stopped.")}return[3,6]}this.sendBufferedData=new PromiseSource;transportResult=this.transportResult;this.transportResult=undefined;data=typeof this.buffer[0]==="string"?this.buffer.join(""):TransportSendQueue.concatBuffers(this.buffer);this.buffer.length=0;_a.label=2;case 2:_a.trys.push([2,4,,5]);return[4,this.transport.send(data)];case 3:_a.sent();transportResult.resolve();return[3,5];case 4:error_1=_a.sent();transportResult.reject(error_1);return[3,5];case 5:return[3,0];case 6:return[2]}})})};TransportSendQueue.concatBuffers=function(arrayBuffers){var totalLength=arrayBuffers.map(function(b){return b.byteLength}).reduce(function(a,b){return a+b});var result=new Uint8Array(totalLength);var offset=0;for(var _i=0,arrayBuffers_1=arrayBuffers;_i0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#FFF;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51A351}.toast-error{background-color:#BD362F}.toast-info{background-color:#2F96B4}.toast-warning{background-color:#F89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/toastr/toastr.min.js b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/toastr/toastr.min.js new file mode 100644 index 000000000..4b5f34a05 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/wwwroot/lib/toastr/toastr.min.js @@ -0,0 +1,7 @@ +/* + * Note that this is toastr v2.1.3, the "latest" version in url has no more maintenance, + * please go to https://cdnjs.com/libraries/toastr.js and pick a certain version you want to use, + * make sure you copy the url from the website since the url may change between versions. + * */ +!function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return g({type:O.error,iconClass:m().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=m()),v=e("#"+t.containerId),v.length?v:(n&&(v=d(t)),v)}function o(e,t,n){return g({type:O.info,iconClass:m().iconClasses.info,message:e,optionsOverride:n,title:t})}function s(e){C=e}function i(e,t,n){return g({type:O.success,iconClass:m().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return g({type:O.warning,iconClass:m().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e,t){var o=m();v||n(o),u(e,o,t)||l(o)}function c(t){var o=m();return v||n(o),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function l(t){for(var n=v.children(),o=n.length-1;o>=0;o--)u(e(n[o]),t)}function u(t,n,o){var s=!(!o||!o.force)&&o.force;return!(!t||!s&&0!==e(":focus",t).length)&&(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0)}function d(t){return v=e("
").attr("id",t.containerId).addClass(t.positionClass),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,closeMethod:!1,closeDuration:!1,closeEasing:!1,closeOnHover:!0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",escapeHtml:!1,target:"body",closeHtml:'',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1}}function f(e){C&&C(e)}function g(t){function o(e){return null==e&&(e=""),e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function s(){c(),u(),d(),p(),g(),C(),l(),i()}function i(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}I.attr("aria-live",e)}function a(){E.closeOnHover&&I.hover(H,D),!E.onclick&&E.tapToDismiss&&I.click(b),E.closeButton&&j&&j.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),E.onCloseClick&&E.onCloseClick(e),b(!0)}),E.onclick&&I.click(function(e){E.onclick(e),b()})}function r(){I.hide(),I[E.showMethod]({duration:E.showDuration,easing:E.showEasing,complete:E.onShown}),E.timeOut>0&&(k=setTimeout(b,E.timeOut),F.maxHideTime=parseFloat(E.timeOut),F.hideEta=(new Date).getTime()+F.maxHideTime,E.progressBar&&(F.intervalId=setInterval(x,10)))}function c(){t.iconClass&&I.addClass(E.toastClass).addClass(y)}function l(){E.newestOnTop?v.prepend(I):v.append(I)}function u(){if(t.title){var e=t.title;E.escapeHtml&&(e=o(t.title)),M.append(e).addClass(E.titleClass),I.append(M)}}function d(){if(t.message){var e=t.message;E.escapeHtml&&(e=o(t.message)),B.append(e).addClass(E.messageClass),I.append(B)}}function p(){E.closeButton&&(j.addClass(E.closeClass).attr("role","button"),I.prepend(j))}function g(){E.progressBar&&(q.addClass(E.progressClass),I.prepend(q))}function C(){E.rtl&&I.addClass("rtl")}function O(e,t){if(e.preventDuplicates){if(t.message===w)return!0;w=t.message}return!1}function b(t){var n=t&&E.closeMethod!==!1?E.closeMethod:E.hideMethod,o=t&&E.closeDuration!==!1?E.closeDuration:E.hideDuration,s=t&&E.closeEasing!==!1?E.closeEasing:E.hideEasing;if(!e(":focus",I).length||t)return clearTimeout(F.intervalId),I[n]({duration:o,easing:s,complete:function(){h(I),clearTimeout(k),E.onHidden&&"hidden"!==P.state&&E.onHidden(),P.state="hidden",P.endTime=new Date,f(P)}})}function D(){(E.timeOut>0||E.extendedTimeOut>0)&&(k=setTimeout(b,E.extendedTimeOut),F.maxHideTime=parseFloat(E.extendedTimeOut),F.hideEta=(new Date).getTime()+F.maxHideTime)}function H(){clearTimeout(k),F.hideEta=0,I.stop(!0,!0)[E.showMethod]({duration:E.showDuration,easing:E.showEasing})}function x(){var e=(F.hideEta-(new Date).getTime())/F.maxHideTime*100;q.width(e+"%")}var E=m(),y=t.iconClass||E.iconClass;if("undefined"!=typeof t.optionsOverride&&(E=e.extend(E,t.optionsOverride),y=t.optionsOverride.iconClass||y),!O(E,t)){T++,v=n(E,!0);var k=null,I=e("
"),M=e("
"),B=e("
"),q=e("
"),j=e(E.closeHtml),F={intervalId:null,hideEta:null,maxHideTime:null},P={toastId:T,state:"visible",startTime:new Date,options:E,map:t};return s(),r(),a(),f(P),E.debug&&console&&console.log(P),I}}function m(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),w=void 0))}var v,C,w,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:c,error:t,getContainer:n,info:o,options:{},subscribe:s,success:i,version:"2.1.3",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)}); +//# sourceMappingURL=toastr.js.map diff --git a/src/ClassifiedAds.Projects/Dockerfile b/src/ClassifiedAds.Projects/Dockerfile index 10687ade4..57ced7cc8 100644 --- a/src/ClassifiedAds.Projects/Dockerfile +++ b/src/ClassifiedAds.Projects/Dockerfile @@ -15,7 +15,6 @@ COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ COPY ./ClassifiedAds.NotificationServer/*.csproj ./ClassifiedAds.NotificationServer/ -COPY ./ClassifiedAds.NotificationTestClient/*.csproj ./ClassifiedAds.NotificationTestClient/ COPY ./ClassifiedAds.Ocelot/*.csproj ./ClassifiedAds.Ocelot/ COPY ./ClassifiedAds.Ocelot.IntegrationTests/*.csproj ./ClassifiedAds.Ocelot.IntegrationTests/ COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ diff --git a/src/ClassifiedAds.Projects/docker-compose.yml b/src/ClassifiedAds.Projects/docker-compose.yml index 84008b8b1..aa6bace69 100644 --- a/src/ClassifiedAds.Projects/docker-compose.yml +++ b/src/ClassifiedAds.Projects/docker-compose.yml @@ -50,6 +50,7 @@ services: OpenIdConnect__Authority: "http://host.docker.internal:9000" OpenIdConnect__RequireHttpsMetadata: "false" ResourceServer__Endpoint: "http://webapi" + NotificationServer__Endpoint: "http://host.docker.internal:9001" CurrentUrl: "http://localhost:80" Storage__Provider: "Local" Storage__Local__Path: "/files" From 0faf10855d29ff96952cb41fc9d9116ce43de66b Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 27 Jan 2020 20:38:00 +0700 Subject: [PATCH 04/46] add health check for notification server --- docs/imgs/code-solution-structure.png | Bin 23653 -> 22707 bytes .../Hubs/HealthCheckHub.cs | 8 ++++++++ .../Startup.cs | 1 + .../ClassifiedAds.WebMVC.csproj | 1 + .../ClassifiedAds.WebMVC/Startup.cs | 3 +++ 5 files changed, 13 insertions(+) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Hubs/HealthCheckHub.cs diff --git a/docs/imgs/code-solution-structure.png b/docs/imgs/code-solution-structure.png index e771df34a5a9d206596b74c237c6d16ee8dc1853..fbfa7bbec35163b77c91765ce17d2c55f47b2890 100644 GIT binary patch literal 22707 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 KO8G_Wu>S*>DPPC{ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Hubs/HealthCheckHub.cs b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Hubs/HealthCheckHub.cs new file mode 100644 index 000000000..db73cf04f --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Hubs/HealthCheckHub.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.SignalR; + +namespace ClassifiedAds.NotificationServer.Hubs +{ + public class HealthCheckHub : Hub + { + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Startup.cs index 823d826da..3908239de 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Startup.cs @@ -51,6 +51,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseEndpoints(endpoints => { + endpoints.MapHub("/HealthCheckHub"); endpoints.MapHub("/SimulatedLongRunningTaskHub"); }); } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj index c531d7249..538a63c83 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj @@ -7,6 +7,7 @@ + diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index a586d502c..83422a9fd 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -150,6 +150,9 @@ public void ConfigureServices(IServiceCollection services) failureStatus: HealthStatus.Degraded) .AddUrlGroup(new Uri(appSettings.ResourceServer.Endpoint), name: "Resource (Web API) Server", + failureStatus: HealthStatus.Degraded) + .AddSignalRHub(appSettings.NotificationServer.Endpoint + "/HealthCheckHub", + name: "Notification (SignalR) Server", failureStatus: HealthStatus.Degraded); services.AddHealthChecksUI(setupSettings: setup => From 826cccc1422f7eb5b225ed05f96eed2428e9aa01 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 27 Jan 2020 22:50:42 +0700 Subject: [PATCH 05/46] update application urls --- README.md | 15 ++++++++++ docs/imgs/startup-projects.png | Bin 0 -> 31764 bytes .../Properties/launchSettings.json | 3 +- .../appsettings.json | 2 +- .../Properties/launchSettings.json | 3 +- .../Properties/launchSettings.json | 1 - .../Properties/launchSettings.json | 13 ++++----- .../Properties/launchSettings.json | 1 - .../Properties/launchSettings.json | 3 +- .../MessageBroker/KafkaOptions.cs | 1 + .../MessageBroker/RabbitMQOptions.cs | 2 ++ .../ClassifiedAds.WebMVC/Startup.cs | 26 +++++++++--------- .../ClassifiedAds.WebMVC/appsettings.json | 9 ++++-- src/ClassifiedAds.Projects/docker-compose.yml | 3 ++ 14 files changed, 50 insertions(+), 32 deletions(-) create mode 100644 docs/imgs/startup-projects.png diff --git a/README.md b/README.md index 44f336bdc..ad3e2e261 100644 --- a/README.md +++ b/README.md @@ -193,8 +193,23 @@ } ``` +## Set Startup Projects +![alt text](/docs/imgs/startup-projects.png) + ## How to Login on Identity Server: - Option 1: Use default created account: + User Name: phong@gmail.com + Password: v*7Un8b4rcN@<-RN - Option 2: Register new account at https://localhost:44367/Account/Register + +## Application URLs: +| Project | Launch URL | Docker Container URL| Docker Container URL| +| -------- | ---------- | ------------------- | ------------------- | +| BackgroundServices | [https://localhost:44318](https://localhost:44318/hangfire/) | [http://localhost:9004](http://localhost:9004/hangfire/) | [http://host.docker.internal:9004](http://host.docker.internal:9004/hangfire/) | +| GRPC | https://localhost:5001 | https://localhost:9005 | https://host.docker.internal:9005 | +| IdentityServer | https://localhost:44367 | http://localhost:9000 | http://host.docker.internal:9000 | +| NotificationServer | https://localhost:44390 | http://localhost:9001 | http://host.docker.internal:9001 | +| WebAPI | https://localhost:44312 | http://localhost:9002 | http://host.docker.internal:9002 | +| WebMVC | https://localhost:44364 | http://localhost:9003 | http://host.docker.internal:9003 | +| GraphQL | [https://localhost:44392](https://localhost:44392/ui/playground) | [http://localhost:9006](http://localhost:9006/ui/playground) | [http://host.docker.internal:9006](http://host.docker.internal:9006/ui/playground) | +| Ocelot | [https://localhost:44340](https://localhost:44340/ocelot/products) | [http://localhost:9007](http://localhost:9007/ocelot/products) | [http://host.docker.internal:9007](http://host.docker.internal:9007/ocelot/products) | diff --git a/docs/imgs/startup-projects.png b/docs/imgs/startup-projects.png new file mode 100644 index 0000000000000000000000000000000000000000..7cadf07a7569d830fdf53b28e7d4f5beb9bf89ab GIT binary patch literal 31764 zcmeFYbzD?$yDkm_(hXA5F{E@29Rfo)($a#IbR$R$5<^G~NOwyMNJ$DffTVy(OSj~2 zfnVSKefRsGz0W@9{Q3LC&kVDc>#6&`uKT*4XGN;3%41`YV;~?PU_%sSH4zYyun`dM zQKH=ge%YhD{}%W_bk&rXMyMEmxB>iuY$K&2g@8~Ui+O2=0{o5ctf23TfPm9=`wy|# zsmKxm!M7M9E2ZsavfVlxYdq<`I_M)Lv=)TeOg)Xbbd40DilFo$HCz?k8%K)>b47ql zf;qE|lA)@pqnM2E*)_wK5rXqyCL0z?Zb%V8&!%*LcE{9nB(o+cYNW3_QZ!e=Jmzv_ zHAobDn$?GTYFaitR{Sj9aIca7Y@Rf!J4sr=#e5zsyh>^@T=#rLOroN-v~i6ZjWz^N zT7h^R0(_v+PN;+MfsYVv8JO(t6-{h+g1Z~||C<|pek}F8zB$Ue9uo^%xct2E!RNTd zJsLCm;$V=|unQ5?T=pgHJcKt3a}rJ5^OKPk-qBCocC#3!d9SlcLpfdNRo5o%Vyn09 z$9x!P`WweN6CNc!Qu&hGTKvvo;16)?qmLDtI%?m^?!#7jr3)Oyf!>yQ@Ar_jpY?D` zQ(avI-t6utUnN!g^?*P1405(#wqNbupbme<(FV_|=Rz%!-uPZq;7S;=C8jmp1TDQ| zbRCdAGnwh6Q{{&cuD|LHZSln% zwxNbCr;p6e3bRI89%bo=7zm0?j(~2DBedCEr-`#d6B(HC_#2h!a^@t5!WP$wrsNzG zFZ_1QkgcHmKQP;1(^b)lWW57dFQW)ay?z1n>(K*M6OTN7l2I;(Zi0hhD@cdV=a=1o zlF%66muy*ow2qVLQ8wUEHmt7?@-Ys??=JKazLZovTKq!7##bwAnmu4EXtY>;@B4IQ z>vZ=eQEBk^(zZr>c60$P)@~Plev_V-%AffN^!MTpBk_r$M7&Qz!NbwD zE(HmpNt{E+omH~vgBjC!=1C=l(r6ngfk&~|U(VW7wHH_Lf96R}IasRU)6W_%$^AGj zY$WCvK^=hVR2c9z4>)Gn*b zUn-yz=O5ciYLb2jzM4}4Rqk5E?_YA zDCbFj!z#YUu7}KHkd%cd-S_C%5=pyI@dboauQ*51h9a%4*I~-~nTQ&V!z0I2MY605 zBbPKa*2cb7uy9KhyBM(oOEu?qod6QvqukLUf~$AKCl>k9lILkvBianbOhVq|ptwGf z^sI&h?b7?Ta@B>(0ng}n%Tm?OgFSScg&K?~)0Lu=t$XqgWGJdDQgn53z9cRD!0EA2 zUQxIWU)*`K*oVUAG5f*UrWJ4*=bSx? zK2qTNIK<)m49%Ei`su!Y$j{fxNDf~~OiNuqeWV7vYZ^8R^zn_Z`ujcjeohD6bKEnz znZdmmt_mA}P=U>Z{ob28>v3ZnaF4bBejV5b>oQvlR2TaE74Vq(UK{)hPT(GWQ@`?I zvj4IvA48ZM*nFE7(gb6BZMW`L#Wzv(MGj+y1;

Zo-#S0neNdp{mUhoSy2*>H2xN*$lPI#x^Imb?a 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 ol#M3fOA^-pY literal 0 HcmV?d00001 diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Properties/launchSettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Properties/launchSettings.json index 6a71008bd..e41907d1d 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Properties/launchSettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Properties/launchSettings.json @@ -3,14 +3,13 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:63199", + "applicationUrl": "https://localhost:44318/", "sslPort": 44318 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, "launchUrl": "hangfire", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/appsettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/appsettings.json index 440a3eb76..30198f1d7 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/appsettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/appsettings.json @@ -9,6 +9,6 @@ }, "AllowedHosts": "*", "NotificationServer": { - "Endpoint": "http://localhost:62710" + "Endpoint": "https://localhost:44390" } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Properties/launchSettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Properties/launchSettings.json index 52ec4aa24..ff545c24e 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Properties/launchSettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:58068", + "applicationUrl": "https://localhost:44392/", "sslPort": 44392 } }, @@ -11,7 +11,6 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, "launchUrl": "ui/playground", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Properties/launchSettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Properties/launchSettings.json index bdc0a62cd..cf5c38e1a 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Properties/launchSettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Properties/launchSettings.json @@ -10,7 +10,6 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Properties/launchSettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Properties/launchSettings.json index 4e05a3ca5..250d91dfa 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Properties/launchSettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/Properties/launchSettings.json @@ -1,16 +1,15 @@ -{ +{ "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, + "windowsAuthentication": false, + "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:62710", + "applicationUrl": "https://localhost:44390/", "sslPort": 44390 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -18,10 +17,10 @@ "ClassifiedAds.NotificationServer": { "commandName": "Project", "launchBrowser": true, - "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" } } } \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Ocelot/Properties/launchSettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.Ocelot/Properties/launchSettings.json index 966830dcc..04ea3d1e4 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Ocelot/Properties/launchSettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Ocelot/Properties/launchSettings.json @@ -11,7 +11,6 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, "launchUrl": "ocelot/products", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Properties/launchSettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Properties/launchSettings.json index e32cb7f8f..4c1281c8e 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Properties/launchSettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:59157", + "applicationUrl": "https://localhost:44312/", "sslPort": 44312 } }, @@ -11,7 +11,6 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, "launchUrl": "api/products", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/KafkaOptions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/KafkaOptions.cs index 1a6d4ba29..fdcc01467 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/KafkaOptions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/KafkaOptions.cs @@ -5,5 +5,6 @@ public class KafkaOptions public string BootstrapServers { get; set; } public string Topic_FileUploaded { get; set; } public string Topic_FileDeleted { get; set; } + public string GroupId { get; set; } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/RabbitMQOptions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/RabbitMQOptions.cs index 46debdd8b..85936fd22 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/RabbitMQOptions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/RabbitMQOptions.cs @@ -8,5 +8,7 @@ public class RabbitMQOptions public string ExchangeName { get; set; } public string RoutingKey_FileUploaded { get; set; } public string RoutingKey_FileDeleted { get; set; } + public string QueueName_FileUploaded { get; set; } + public string QueueName_FileDeleted { get; set; } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index 83422a9fd..632a45b9a 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -297,7 +297,7 @@ private void TestMessageBrokerReceivers() HostName = AppSettings.MessageBroker.RabbitMQ.HostName, UserName = AppSettings.MessageBroker.RabbitMQ.UserName, Password = AppSettings.MessageBroker.RabbitMQ.Password, - QueueName = "classifiedadds_fileuploaded", + QueueName = AppSettings.MessageBroker.RabbitMQ.QueueName_FileUploaded, }); fileDeletedMessageQueueReceiver = new RabbitMQReceiver(new RabbitMQReceiverOptions @@ -305,28 +305,28 @@ private void TestMessageBrokerReceivers() HostName = AppSettings.MessageBroker.RabbitMQ.HostName, UserName = AppSettings.MessageBroker.RabbitMQ.UserName, Password = AppSettings.MessageBroker.RabbitMQ.Password, - QueueName = "classifiedadds_filedeleted", + QueueName = AppSettings.MessageBroker.RabbitMQ.QueueName_FileDeleted, }); } if (AppSettings.MessageBroker.UsedKafka()) { fileUploadedMessageQueueReceiver = new KafkaReceiver( - AppSettings.MessageBroker.Kafka.BootstrapServers, - AppSettings.MessageBroker.Kafka.Topic_FileUploaded, - "classified"); + AppSettings.MessageBroker.Kafka.BootstrapServers, + AppSettings.MessageBroker.Kafka.Topic_FileUploaded, + AppSettings.MessageBroker.Kafka.GroupId); fileDeletedMessageQueueReceiver = new KafkaReceiver( AppSettings.MessageBroker.Kafka.BootstrapServers, AppSettings.MessageBroker.Kafka.Topic_FileDeleted, - "classified"); + AppSettings.MessageBroker.Kafka.GroupId); } if (AppSettings.MessageBroker.UsedAzureQueue()) { fileUploadedMessageQueueReceiver = new AzureQueueReceiver( - AppSettings.MessageBroker.AzureQueue.ConnectionString, - AppSettings.MessageBroker.AzureQueue.QueueName_FileUploaded); + AppSettings.MessageBroker.AzureQueue.ConnectionString, + AppSettings.MessageBroker.AzureQueue.QueueName_FileUploaded); fileDeletedMessageQueueReceiver = new AzureQueueReceiver( AppSettings.MessageBroker.AzureQueue.ConnectionString, @@ -336,8 +336,8 @@ private void TestMessageBrokerReceivers() if (AppSettings.MessageBroker.UsedAzureServiceBus()) { fileUploadedMessageQueueReceiver = new AzureServiceBusReceiver( - AppSettings.MessageBroker.AzureServiceBus.ConnectionString, - AppSettings.MessageBroker.AzureServiceBus.QueueName_FileUploaded); + AppSettings.MessageBroker.AzureServiceBus.ConnectionString, + AppSettings.MessageBroker.AzureServiceBus.QueueName_FileUploaded); fileDeletedMessageQueueReceiver = new AzureServiceBusReceiver( AppSettings.MessageBroker.AzureServiceBus.ConnectionString, @@ -345,9 +345,9 @@ private void TestMessageBrokerReceivers() } var connection = new HubConnectionBuilder() - .WithUrl($"{AppSettings.NotificationServer.Endpoint}/SimulatedLongRunningTaskHub") - .AddMessagePackProtocol() - .Build(); + .WithUrl($"{AppSettings.NotificationServer.Endpoint}/SimulatedLongRunningTaskHub") + .AddMessagePackProtocol() + .Build(); fileUploadedMessageQueueReceiver?.Receive(data => { diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json index d82bb8628..9a65cc68b 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json @@ -11,7 +11,7 @@ "Endpoint": "https://localhost:44312" }, "NotificationServer": { - "Endpoint": "http://localhost:62710" + "Endpoint": "https://localhost:44390" }, "Logging": { "LogLevel": { @@ -45,12 +45,15 @@ "Password": "guest", "ExchangeName": "amq.direct", "RoutingKey_FileUploaded": "classifiedadds_fileuploaded", - "RoutingKey_FileDeleted": "classifiedadds_filedeleted" + "RoutingKey_FileDeleted": "classifiedadds_filedeleted", + "QueueName_FileUploaded": "classifiedadds_fileuploaded", + "QueueName_FileDeleted": "classifiedadds_filedeleted" }, "Kafka": { "BootstrapServers": "localhost:9092", "Topic_FileUploaded": "classifiedadds_fileuploaded", - "Topic_FileDeleted": "classifiedadds_filedeleted" + "Topic_FileDeleted": "classifiedadds_filedeleted", + "GroupId": "classified" }, "AzureQueue": { "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=xxx;EndpointSuffix=core.windows.net", diff --git a/src/ClassifiedAds.Projects/docker-compose.yml b/src/ClassifiedAds.Projects/docker-compose.yml index aa6bace69..5e3d0f11e 100644 --- a/src/ClassifiedAds.Projects/docker-compose.yml +++ b/src/ClassifiedAds.Projects/docker-compose.yml @@ -67,9 +67,12 @@ services: MessageBroker__RabbitMQ__ExchangeName: "amq.direct" MessageBroker__RabbitMQ__RoutingKey_FileUploaded: "classifiedadds_fileuploaded" MessageBroker__RabbitMQ__RoutingKey_FileDeleted: "classifiedadds_filedeleted" + MessageBroker__RabbitMQ__QueueName_FileUploaded: "classifiedadds_fileuploaded" + MessageBroker__RabbitMQ__QueueName_FileDeleted: "classifiedadds_filedeleted" MessageBroker__Kafka__BootstrapServers: "host.docker.internal:9092" MessageBroker__Kafka__Topic_FileUploaded: "classifiedadds_fileuploaded" MessageBroker__Kafka__Topic_FileDeleted: "classifiedadds_filedeleted" + MessageBroker__Kafka__GroupId: "classified" MessageBroker__AzureQueue__ConnectionString: "DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=xxx;EndpointSuffix=core.windows.net" MessageBroker__AzureQueue__QueueName_FileUploaded: "classifiedadds-fileuploaded" MessageBroker__AzureQueue__QueueName_FileDeleted: "classifiedadds-filedeleted" From b3a487d2552d188fe4c2685f56dee0e592aa003c Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Tue, 28 Jan 2020 08:55:22 +0700 Subject: [PATCH 06/46] add more screenshots --- README.md | 13 ++++++- docs/imgs/health-checks-ui.png | Bin 0 -> 25758 bytes docs/imgs/web-mvc-home-page.png | Bin 0 -> 22997 bytes .../Startup.cs | 8 ++--- .../ClassifiedAds.GraphQL/Startup.cs | 2 -- .../ClassifiedAds.WebAPI/Startup.cs | 32 +++++++----------- 6 files changed, 28 insertions(+), 27 deletions(-) create mode 100644 docs/imgs/health-checks-ui.png create mode 100644 docs/imgs/web-mvc-home-page.png diff --git a/README.md b/README.md index ad3e2e261..1c9e3202d 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,9 @@ "Password": "guest", "ExchangeName": "amq.direct", "RoutingKey_FileUploaded": "classifiedadds_fileuploaded", - "RoutingKey_FileDeleted": "classifiedadds_filedeleted" + "RoutingKey_FileDeleted": "classifiedadds_filedeleted", + "QueueName_FileUploaded": "classifiedadds_fileuploaded", + "QueueName_FileDeleted": "classifiedadds_filedeleted" }, } ``` @@ -196,6 +198,15 @@ ## Set Startup Projects ![alt text](/docs/imgs/startup-projects.png) +## Run or Debug the Solution +- Web MVC Home Page: https://localhost:44364/ + +![alt text](/docs/imgs/web-mvc-home-page.png) + +- Navigate to Health Checks UI https://localhost:44364/healthchecks-ui#/healthchecks and make sure everything is green. + +![alt text](/docs/imgs/health-checks-ui.png) + ## How to Login on Identity Server: - Option 1: Use default created account: + User Name: phong@gmail.com diff --git a/docs/imgs/health-checks-ui.png b/docs/imgs/health-checks-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..aeaf96e14fd9ed27255af3a69bc25a4e1a5df0a0 GIT binary patch literal 25758 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 literal 0 HcmV?d00001 diff --git a/docs/imgs/web-mvc-home-page.png b/docs/imgs/web-mvc-home-page.png new file mode 100644 index 0000000000000000000000000000000000000000..6ecfbf2f5a05e71204828ca07966e71b7a54e310 GIT binary patch literal 22997 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

5LkwlS$%Eyd7owokBmuR-J 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 { - var options = new Hangfire.SqlServer.SqlServerStorageOptions + var options = new SqlServerStorageOptions { - PrepareSchemaIfNecessary = true + PrepareSchemaIfNecessary = true, }; x.UseSqlServerStorage(Configuration.GetConnectionString("ClassifiedAds"), options); }); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Startup.cs index e6a0a0957..60b1c5165 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.GraphQL/Startup.cs @@ -67,8 +67,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - app.UseHttpsRedirection(); - // add http for Schema at default url /graphql app.UseGraphQL(); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs index d1e1419ae..d23e9beea 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs @@ -86,17 +86,17 @@ public void ConfigureServices(IServiceCollection services) Title = "ClassifiedAds API", Version = "1", Description = "ClassifiedAds API Specification.", - Contact = new OpenApiContact() + Contact = new OpenApiContact { Email = "abc.xyz@gmail.com", Name = "Phong Nguyen", - Url = new Uri("https://github.com/phongnguyend") + Url = new Uri("https://github.com/phongnguyend"), }, - License = new OpenApiLicense() + License = new OpenApiLicense { Name = "MIT License", - Url = new Uri("https://opensource.org/licenses/MIT") - } + Url = new Uri("https://opensource.org/licenses/MIT"), + }, }); setupAction.AddSecurityDefinition("bearer", new OpenApiSecurityScheme() @@ -104,7 +104,7 @@ public void ConfigureServices(IServiceCollection services) Type = SecuritySchemeType.Http, Scheme = "bearer", BearerFormat = "JWT", - Description = "Input your Bearer token to access this API" + Description = "Input your Bearer token to access this API", }); setupAction.AddSecurityRequirement(new OpenApiSecurityRequirement @@ -115,17 +115,17 @@ public void ConfigureServices(IServiceCollection services) Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, - Id = "bearer" - } + Id = "bearer", + }, }, new List() - } + }, }); }); services.AddMemoryCache() .AddMiniProfiler(options => { - options.RouteBasePath = "/profiler";// access /profiler/results to see last profile check + options.RouteBasePath = "/profiler"; // access /profiler/results to see last profile check options.PopupRenderPosition = StackExchange.Profiling.RenderPosition.BottomLeft; options.PopupShowTimeWithChildren = true; }) @@ -143,18 +143,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.MigrateDb(); app.UseDeveloperExceptionPage(); } - else - { - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } app.UseRouting(); app.UseCors(AppSettings.CORS.AllowAnyOrigin ? "AllowAnyOrigin" : "AllowedOrigins"); - app.UseHttpsRedirection(); - app.UseSwagger(); app.UseSwaggerUI(setupAction => @@ -163,7 +156,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) "/swagger/ClassifiedAds/swagger.json", "ClassifiedAds API"); - setupAction.RoutePrefix = ""; + setupAction.RoutePrefix = string.Empty; setupAction.DefaultModelExpandDepth(2); setupAction.DefaultModelRendering(ModelRendering.Model); @@ -177,7 +170,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseMiniProfiler(); - app.UseEndpoints(endpoints => { + app.UseEndpoints(endpoints => + { endpoints.MapControllers(); }); } From 7e3e8348d26ef6b8cc1a39d0725f3bdf67aaec67 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Tue, 28 Jan 2020 11:00:38 +0700 Subject: [PATCH 07/46] Add Health Checks for Storage Options (#26) --- docs/imgs/health-checks-ui.png | Bin 25758 -> 36568 bytes .../ClassifiedAds.Infrastructure.csproj | 4 ++ .../FilePathHealthCheckBuilderExtensions.cs | 24 +++++++ .../HealthChecks/FilePathWriteHealthCheck.cs | 43 ++++++++++++ .../ClassifiedAds.WebMVC.csproj | 5 ++ .../ClassifiedAds.WebMVC/Startup.cs | 62 +++++++++++++++++- 6 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/HealthChecks/FilePathHealthCheckBuilderExtensions.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/HealthChecks/FilePathWriteHealthCheck.cs diff --git a/docs/imgs/health-checks-ui.png b/docs/imgs/health-checks-ui.png index aeaf96e14fd9ed27255af3a69bc25a4e1a5df0a0..2414674892336975f313357336d7746d4452ef4b 100644 GIT binary patch literal 36568 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 literal 25758 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 diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj b/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj index 0db440a9f..18d83a724 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj @@ -28,4 +28,8 @@ + + + + diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/HealthChecks/FilePathHealthCheckBuilderExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/HealthChecks/FilePathHealthCheckBuilderExtensions.cs new file mode 100644 index 000000000..48e0dcace --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/HealthChecks/FilePathHealthCheckBuilderExtensions.cs @@ -0,0 +1,24 @@ +using ClassifiedAds.Infrastructure.HealthChecks; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class FilePathHealthCheckBuilderExtensions + { + public static IHealthChecksBuilder AddFilePathWrite(this IHealthChecksBuilder builder, string path, string name, HealthStatus failureStatus, IEnumerable tags = default) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + return builder.Add(new HealthCheckRegistration( + name, + new FilePathWriteHealthCheck(path), + failureStatus, + tags)); + } + } +} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/HealthChecks/FilePathWriteHealthCheck.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/HealthChecks/FilePathWriteHealthCheck.cs new file mode 100644 index 000000000..dd7db9fbd --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/HealthChecks/FilePathWriteHealthCheck.cs @@ -0,0 +1,43 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.HealthChecks +{ + public class FilePathWriteHealthCheck : IHealthCheck + { + private string _path; + + public FilePathWriteHealthCheck(string path) + { + _path = path; + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var testFile = $"{_path}\\HealthCheck_{Guid.NewGuid()}.txt"; + var fs = File.Create(testFile); + fs.Close(); + File.Delete(testFile); + + return Task.FromResult(HealthCheckResult.Healthy($"Application has read and write permissions to {_path}")); + } + catch (Exception e) + { + if (context.Registration.FailureStatus == HealthStatus.Unhealthy) + { + return Task.FromResult(HealthCheckResult.Unhealthy(e.Message)); + } + else + { + return Task.FromResult(HealthCheckResult.Degraded(e.Message)); + } + } + + } + } +} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj index 538a63c83..35528047c 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj @@ -7,6 +7,11 @@ + + + + + diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index 632a45b9a..a6e095022 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -1,4 +1,6 @@ -using ClassifiedAds.DomainServices.DomainEvents; +using Amazon; +using Amazon.S3; +using ClassifiedAds.DomainServices.DomainEvents; using ClassifiedAds.DomainServices.Identity; using ClassifiedAds.DomainServices.Infrastructure.MessageBrokers; using ClassifiedAds.DomainServices.Infrastructure.Storages; @@ -140,7 +142,7 @@ public void ConfigureServices(IServiceCollection services) }) .AddEntityFramework(); - services.AddHealthChecks() + var healthChecksBuilder = services.AddHealthChecks() .AddSqlServer(connectionString: appSettings.ConnectionStrings.ClassifiedAds, healthQuery: "SELECT 1;", name: "Sql Server", @@ -166,14 +168,44 @@ public void ConfigureServices(IServiceCollection services) if (appSettings.Storage.UsedAzure()) { services.AddSingleton(new AzureBlobStorageManager(appSettings.Storage.Azure.ConnectionString, appSettings.Storage.Azure.Container)); + + healthChecksBuilder.AddAzureBlobStorage( + appSettings.Storage.Azure.ConnectionString, + containerName: appSettings.Storage.Azure.Container, + name: "Storage (Azure Blob)", + failureStatus: HealthStatus.Degraded); } else if (appSettings.Storage.UsedAmazon()) { - services.AddSingleton(new AmazonS3StorageManager(appSettings.Storage.Amazon.AccessKeyID, appSettings.Storage.Amazon.SecretAccessKey, appSettings.Storage.Amazon.BucketName, appSettings.Storage.Amazon.RegionEndpoint)); + services.AddSingleton( + new AmazonS3StorageManager( + appSettings.Storage.Amazon.AccessKeyID, + appSettings.Storage.Amazon.SecretAccessKey, + appSettings.Storage.Amazon.BucketName, + appSettings.Storage.Amazon.RegionEndpoint)); + + healthChecksBuilder.AddS3( + s3 => + { + s3.AccessKey = appSettings.Storage.Amazon.AccessKeyID; + s3.SecretKey = appSettings.Storage.Amazon.SecretAccessKey; + s3.BucketName = appSettings.Storage.Amazon.BucketName; + s3.S3Config = new AmazonS3Config + { + RegionEndpoint = RegionEndpoint.GetBySystemName(appSettings.Storage.Amazon.RegionEndpoint), + }; + }, + name: "Storage (Amazon S3)", + failureStatus: HealthStatus.Degraded); } else { services.AddSingleton(new LocalFileStorageManager(appSettings.Storage.Local.Path)); + + healthChecksBuilder.AddFilePathWrite( + appSettings.Storage.Local.Path, + name: "Storage (Local Directory)", + failureStatus: HealthStatus.Degraded); } if (appSettings.MessageBroker.UsedRabbitMQ()) @@ -215,6 +247,18 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton>(new AzureQueueSender( connectionString: appSettings.MessageBroker.AzureQueue.ConnectionString, queueName: appSettings.MessageBroker.AzureQueue.QueueName_FileDeleted)); + + healthChecksBuilder.AddAzureQueueStorage( + connectionString: appSettings.MessageBroker.AzureQueue.ConnectionString, + queueName: appSettings.MessageBroker.AzureQueue.QueueName_FileUploaded, + name: "Message Broker (Azure Queue) File Uploaded", + failureStatus: HealthStatus.Degraded); + + healthChecksBuilder.AddAzureQueueStorage( + connectionString: appSettings.MessageBroker.AzureQueue.ConnectionString, + queueName: appSettings.MessageBroker.AzureQueue.QueueName_FileDeleted, + name: "Message Broker (Azure Queue) File Deleted", + failureStatus: HealthStatus.Degraded); } else if (appSettings.MessageBroker.UsedAzureServiceBus()) { @@ -225,6 +269,18 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton>(new AzureServiceBusSender( connectionString: appSettings.MessageBroker.AzureServiceBus.ConnectionString, queueName: appSettings.MessageBroker.AzureServiceBus.QueueName_FileDeleted)); + + healthChecksBuilder.AddAzureServiceBusQueue( + connectionString: appSettings.MessageBroker.AzureServiceBus.ConnectionString, + queueName: appSettings.MessageBroker.AzureServiceBus.QueueName_FileUploaded, + name: "Message Broker (Azure Service Bus) File Uploaded", + failureStatus: HealthStatus.Degraded); + + healthChecksBuilder.AddAzureServiceBusQueue( + connectionString: appSettings.MessageBroker.AzureServiceBus.ConnectionString, + queueName: appSettings.MessageBroker.AzureServiceBus.QueueName_FileDeleted, + name: "Message Broker (Azure Service Bus) File Deleted", + failureStatus: HealthStatus.Degraded); } } From 41649b93734666accf7aa0a1d3ab0887f5b86f93 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Tue, 28 Jan 2020 20:08:27 +0700 Subject: [PATCH 08/46] Add Health Checks for Message Broker Options (#27) --- .../MessageBroker/RabbitMQOptions.cs | 8 ++++++++ .../ClassifiedAds.WebMVC/Startup.cs | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/RabbitMQOptions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/RabbitMQOptions.cs index 85936fd22..8c1a6aa03 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/RabbitMQOptions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/MessageBroker/RabbitMQOptions.cs @@ -10,5 +10,13 @@ public class RabbitMQOptions public string RoutingKey_FileDeleted { get; set; } public string QueueName_FileUploaded { get; set; } public string QueueName_FileDeleted { get; set; } + + public string ConnectionString + { + get + { + return $"amqp://{UserName}:{Password}@{HostName}/%2f"; + } + } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index a6e095022..253093099 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -227,6 +227,11 @@ public void ConfigureServices(IServiceCollection services) ExchangeName = appSettings.MessageBroker.RabbitMQ.ExchangeName, RoutingKey = appSettings.MessageBroker.RabbitMQ.RoutingKey_FileDeleted, })); + + healthChecksBuilder.AddRabbitMQ( + rabbitMQConnectionString: appSettings.MessageBroker.RabbitMQ.ConnectionString, + name: "Message Broker (RabbitMQ)", + failureStatus: HealthStatus.Degraded); } else if (appSettings.MessageBroker.UsedKafka()) { @@ -237,6 +242,15 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton>(new KafkaSender( appSettings.MessageBroker.Kafka.BootstrapServers, appSettings.MessageBroker.Kafka.Topic_FileDeleted)); + + healthChecksBuilder.AddKafka( + setup => + { + setup.BootstrapServers = appSettings.MessageBroker.Kafka.BootstrapServers; + setup.MessageTimeoutMs = 5000; + }, + name: "Message Broker (Kafka)", + failureStatus: HealthStatus.Degraded); } else if (appSettings.MessageBroker.UsedAzureQueue()) { From d2c5d695065c63551d2eae5568aed5dacf05f9de Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Wed, 29 Jan 2020 21:25:35 +0700 Subject: [PATCH 09/46] remove unnecessary migration steps --- .../ClassifiedAds.IdentityServer/Startup.cs | 1 - src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Startup.cs index 3dd90d59f..6c6202f95 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Startup.cs @@ -60,7 +60,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { - app.MigrateIdServerDb(); app.UseDeveloperExceptionPage(); } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs index d23e9beea..0eb640544 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebAPI/Startup.cs @@ -140,7 +140,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { - app.MigrateDb(); app.UseDeveloperExceptionPage(); } From d550a4a7d8b62ff7e7fedbfecad2b0c1994b58aa Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Thu, 30 Jan 2020 00:29:48 +0700 Subject: [PATCH 10/46] update urls --- .../appsettings.json | 2 +- .../IdServerPersistenceExtensions.cs | 11 +++++---- .../ConfigurationOptions/AppSettings.cs | 2 ++ .../ConfigurationOptions/BackgroundServer.cs | 7 ++++++ .../Middleware/CustomMiddleware.cs | 4 ++++ .../ClassifiedAds.WebMVC/Startup.cs | 4 ++++ .../Views/Shared/_Layout.cshtml | 2 +- .../ClassifiedAds.WebMVC/appsettings.json | 3 +++ .../ClassifiedAds.WebMVC/wwwroot/js/site.js | 2 ++ src/ClassifiedAds.Projects/docker-compose.yml | 23 ++++++++++--------- 10 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/BackgroundServer.cs diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/appsettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/appsettings.json index ec60d08ff..e3091d18f 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/appsettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.NotificationServer/appsettings.json @@ -6,6 +6,6 @@ }, "AllowedHosts": "*", "CORS": { - "AllowedOrigins": [ "https://localhost:44364", "http://localhost:9003" ] + "AllowedOrigins": [ "https://localhost:44364", "http://host.docker.internal:9003" ] } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs index 715dcbefd..c65e36679 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs @@ -58,7 +58,8 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) if (!context.Clients.Any()) { - var clients = new List { + var clients = new List + { new Client { ClientId = "ClassifiedAds.WebMVC", @@ -67,12 +68,12 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) RedirectUris = { "https://localhost:44364/signin-oidc", - "http://localhost:9003/signin-oidc", + "http://host.docker.internal:9003/signin-oidc", }, PostLogoutRedirectUris = { "https://localhost:44364/signout-callback-oidc", - "http://localhost:9003/signout-callback-oidc", + "http://host.docker.internal:9003/signout-callback", }, AllowedScopes = { @@ -116,7 +117,7 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) }, AllowOfflineAccess = true, }, - }; + }; foreach (var client in clients) { @@ -147,7 +148,7 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) var apiResources = new List { new ApiResource("ClassifiedAds.WebAPI", "ClassifiedAds Web API", - new List() {"role" } ), + new List() { "role" }), }; foreach (var resource in apiResources) { diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs index ec9a73983..a7e70e1fc 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs @@ -14,6 +14,8 @@ public class AppSettings public NotificationServer NotificationServer { get; set; } + public BackgroundServer BackgroundServer { get; set; } + public string AllowedHosts { get; set; } public string CurrentUrl { get; set; } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/BackgroundServer.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/BackgroundServer.cs new file mode 100644 index 000000000..5f068c63f --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/BackgroundServer.cs @@ -0,0 +1,7 @@ +namespace ClassifiedAds.WebMVC.ConfigurationOptions +{ + public class BackgroundServer + { + public string Endpoint { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Middleware/CustomMiddleware.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Middleware/CustomMiddleware.cs index 1b4c78e6a..4b5689a97 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Middleware/CustomMiddleware.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Middleware/CustomMiddleware.cs @@ -18,6 +18,10 @@ public async Task InvokeAsync(HttpContext context) var stopwatch = Stopwatch.StartNew(); await _next(context); var elapsedTime = stopwatch.Elapsed; + + if (context.Request.Path.HasValue && context.Request.Path.Value.Contains("oidc")) + { + } } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index 253093099..305ebe3fc 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -80,6 +80,7 @@ public void ConfigureServices(IServiceCollection services) // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; + options.Secure = CookieSecurePolicy.None; }); services.AddControllersWithViews(setupAction => @@ -301,6 +302,8 @@ public void ConfigureServices(IServiceCollection services) // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + app.UseMiddleware(); + if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); @@ -318,6 +321,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseIPFiltering(); app.UseHttpsRedirection(); + app.UseStaticFiles(); app.UseCookiePolicy(); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml index 1823330e8..292dc410c 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml @@ -64,7 +64,7 @@ Health Checks UI

diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json index 851eb461f..fec84d948 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json @@ -8,10 +8,12 @@ "RequireHttpsMetadata": "true" }, "ResourceServer": { - "Endpoint": "https://localhost:44312" + "Endpoint": "https://localhost:44312", + "PublicEndpoint": "https://localhost:44312" }, "NotificationServer": { - "Endpoint": "https://localhost:44390" + "Endpoint": "https://localhost:44390", + "PublicEndpoint": "https://localhost:44390" }, "BackgroundServer": { "Endpoint": "https://localhost:44318" diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/docker-entrypoint.sh b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/docker-entrypoint.sh index 7ad80ed84..92e6d75c5 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/docker-entrypoint.sh +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/docker-entrypoint.sh @@ -2,14 +2,14 @@ while ! nc -z migrator 80; do echo migrator is not ready, sleeping; - sleep 1; + sleep 5; done; echo migrator is ready!; while ! nc -z rabbitmq 15672; do echo rabbitmq is not ready, sleeping; - sleep 1; + sleep 5; done; echo rabbitmq is ready!; diff --git a/src/ClassifiedAds.Projects/docker-compose.yml b/src/ClassifiedAds.Projects/docker-compose.yml index dd2ecde1e..4c9e97d4e 100644 --- a/src/ClassifiedAds.Projects/docker-compose.yml +++ b/src/ClassifiedAds.Projects/docker-compose.yml @@ -108,8 +108,10 @@ services: ConnectionStrings__ClassifiedAds: "Server=db;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#;MultipleActiveResultSets=true" OpenIdConnect__Authority: "http://host.docker.internal:9000" OpenIdConnect__RequireHttpsMetadata: "false" - ResourceServer__Endpoint: "http://host.docker.internal:9002" - NotificationServer__Endpoint: "http://host.docker.internal:9001" + ResourceServer__Endpoint: "http://webapi" + ResourceServer__PublicEndpoint: "http://host.docker.internal:9002" + NotificationServer__Endpoint: "http://notificationserver" + NotificationServer__PublicEndpoint: "http://localhost:9001" BackgroundServer__Endpoint: "http://host.docker.internal:9004" CurrentUrl: "http://host.docker.internal:9003" Storage__Provider: "Local" From 28a6c01b463f2e946bfff4b70633a14a0eee5385 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Thu, 30 Jan 2020 20:59:51 +0700 Subject: [PATCH 13/46] configure docker-compose --- README.md | 29 ++++++++++++++++++ docs/imgs/health-checks-ui-container.png | Bin 0 -> 33263 bytes .../docker-entrypoint.sh | 4 +++ .../docker-entrypoint.sh | 6 +++- .../ClassifiedAds.WebMVC/Startup.cs | 4 +-- .../ClassifiedAds.WebMVC/docker-entrypoint.sh | 3 ++ 6 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 docs/imgs/health-checks-ui-container.png diff --git a/README.md b/README.md index 1c9e3202d..d3923b2de 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,35 @@ + Password: v*7Un8b4rcN@<-RN - Option 2: Register new account at https://localhost:44367/Account/Register +## How to Run on Containers: +- Add Migrations if you haven't done on previous steps: + + Install **dotnet-ef** cli: + ``` + dotnet tool install --global dotnet-ef --version="3.1" + ``` + + Navigate to [ClassifiedAds.Migrator](/src/ClassifiedAds.Projects/ClassifiedAds.Migrator/) and run these commands: + ``` + dotnet ef migrations add Init --context AdsDbContext -o Migrations/AdsDb + dotnet ef migrations add Init --context ConfigurationDbContext -o Migrations/ConfigurationDb + dotnet ef migrations add Init --context PersistedGrantDbContext -o Migrations/PersistedGrantDb + ``` +- Navigate to [ClassifiedAds.Projects](/src/ClassifiedAds.Projects/) and run: + ``` + docker-compose build + docker-compose up + ``` +- Open Web MVC Home Page at: http://host.docker.internal:9003 + +![alt text](/docs/imgs/web-mvc-home-page.png) + +- Navigate to Health Checks UI http://host.docker.internal:9003/healthchecks-ui#/healthchecks and make sure everything is green. + +![alt text](/docs/imgs/health-checks-ui-container.png) + +- Login on Identity Server: + + Use default created account: phong@gmail.com / v*7Un8b4rcN@<-RN + + Register new account at http://host.docker.internal:9000/Account/Register + ## Application URLs: | Project | Launch URL | Docker Container URL| Docker Container URL| | -------- | ---------- | ------------------- | ------------------- | diff --git a/docs/imgs/health-checks-ui-container.png b/docs/imgs/health-checks-ui-container.png new file mode 100644 index 0000000000000000000000000000000000000000..09b82f5e3ab8e43508a09c795a904c44f11e92d2 GIT binary patch literal 33263 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 b$#Ff#;}=lt#g!^vxS;$%{eHQ;Y0!TGo#!(b literal 0 HcmV?d00001 diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/docker-entrypoint.sh b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/docker-entrypoint.sh index 9061dd4c5..cacd1be0c 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/docker-entrypoint.sh +++ b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/docker-entrypoint.sh @@ -5,6 +5,10 @@ do sleep 5; done; echo migrator is ready!; + +echo sleep more 30s before starting!; +sleep 30; + cd /ClassifiedAds.Projects && dotnet ClassifiedAds.BackgroundServices.dll # Keep the line ending diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Migrator/docker-entrypoint.sh b/src/ClassifiedAds.Projects/ClassifiedAds.Migrator/docker-entrypoint.sh index 3e543e926..df38e91e3 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Migrator/docker-entrypoint.sh +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Migrator/docker-entrypoint.sh @@ -4,7 +4,11 @@ do echo db is not ready, sleeping; sleep 5; done; -echo Connected!; +echo db is ready!; + +echo sleep more 30s before starting!; +sleep 30; + cd /ClassifiedAds.Projects && dotnet ClassifiedAds.Migrator.dll # Keep the line ending diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index fc9f3190b..2c62a7eac 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -426,7 +426,7 @@ private void TestMessageBrokerReceivers() fileUploadedMessageQueueReceiver?.Receive(data => { - Thread.Sleep(2000); // Sleep few seconds to wait for the fontend to reload + Thread.Sleep(2000); // simulate long running task string message = data.FileEntry.Id.ToString(); @@ -439,7 +439,7 @@ private void TestMessageBrokerReceivers() fileDeletedMessageQueueReceiver?.Receive(data => { - Thread.Sleep(2000); // Sleep few seconds to wait for the fontend to reload + Thread.Sleep(2000); // simulate long running task string message = data.FileEntry.Id.ToString(); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/docker-entrypoint.sh b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/docker-entrypoint.sh index 92e6d75c5..3ef1fdf1e 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/docker-entrypoint.sh +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/docker-entrypoint.sh @@ -13,6 +13,9 @@ do done; echo rabbitmq is ready!; +echo sleep 30s before starting!; +sleep 30; + curl -i -u guest:guest -H "content-type:application/json" -XPUT -d '{"durable":true}' http://rabbitmq:15672/api/queues/%2f/classifiedadds_fileuploaded curl -i -u guest:guest -H "content-type:application/json" -XPUT -d '{"durable":true}' http://rabbitmq:15672/api/queues/%2f/classifiedadds_filedeleted curl -i -u guest:guest -H "content-type:application/json" -XPOST -d '{"routing_key": "classifiedadds_fileuploaded", "arguments": {}}' http://rabbitmq:15672/api/bindings/%2f/e/amq.direct/q/classifiedadds_fileuploaded From b1fbac69a98436c65063a4efb2f3ef3f0d78fdf5 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Thu, 30 Jan 2020 21:05:34 +0700 Subject: [PATCH 14/46] update alignment --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d3923b2de..035e421f8 100644 --- a/README.md +++ b/README.md @@ -201,11 +201,11 @@ ## Run or Debug the Solution - Web MVC Home Page: https://localhost:44364/ -![alt text](/docs/imgs/web-mvc-home-page.png) + ![alt text](/docs/imgs/web-mvc-home-page.png) - Navigate to Health Checks UI https://localhost:44364/healthchecks-ui#/healthchecks and make sure everything is green. -![alt text](/docs/imgs/health-checks-ui.png) + ![alt text](/docs/imgs/health-checks-ui.png) ## How to Login on Identity Server: - Option 1: Use default created account: @@ -232,11 +232,11 @@ ``` - Open Web MVC Home Page at: http://host.docker.internal:9003 -![alt text](/docs/imgs/web-mvc-home-page.png) + ![alt text](/docs/imgs/web-mvc-home-page.png) - Navigate to Health Checks UI http://host.docker.internal:9003/healthchecks-ui#/healthchecks and make sure everything is green. -![alt text](/docs/imgs/health-checks-ui-container.png) + ![alt text](/docs/imgs/health-checks-ui-container.png) - Login on Identity Server: + Use default created account: phong@gmail.com / v*7Un8b4rcN@<-RN From 715872ff7c1672998011e1207fab18f153730ade Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Thu, 30 Jan 2020 21:31:19 +0700 Subject: [PATCH 15/46] configure docker-compose --- .../IdServerPersistenceExtensions.cs | 2 +- src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs index c65e36679..a8bb78222 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs @@ -73,7 +73,7 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) PostLogoutRedirectUris = { "https://localhost:44364/signout-callback-oidc", - "http://host.docker.internal:9003/signout-callback", + "http://host.docker.internal:9003/signout-callback-oidc", }, AllowedScopes = { diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index 2c62a7eac..982194601 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -426,7 +426,7 @@ private void TestMessageBrokerReceivers() fileUploadedMessageQueueReceiver?.Receive(data => { - Thread.Sleep(2000); // simulate long running task + Thread.Sleep(5000); // simulate long running task string message = data.FileEntry.Id.ToString(); @@ -439,7 +439,7 @@ private void TestMessageBrokerReceivers() fileDeletedMessageQueueReceiver?.Receive(data => { - Thread.Sleep(2000); // simulate long running task + Thread.Sleep(5000); // simulate long running task string message = data.FileEntry.Id.ToString(); From 9de4fb2877b405b41decddee7344e2ade965f52f Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Thu, 30 Jan 2020 21:37:12 +0700 Subject: [PATCH 16/46] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 035e421f8..8cad1c77a 100644 --- a/README.md +++ b/README.md @@ -207,13 +207,13 @@ ![alt text](/docs/imgs/health-checks-ui.png) -## How to Login on Identity Server: -- Option 1: Use default created account: - + User Name: phong@gmail.com - + Password: v*7Un8b4rcN@<-RN -- Option 2: Register new account at https://localhost:44367/Account/Register +- Login on Identity Server: + + Option 1: Use default created account: + + User Name: phong@gmail.com + + Password: v*7Un8b4rcN@<-RN + + Option 2: Register new account at https://localhost:44367/Account/Register -## How to Run on Containers: +## How to Run on Docker Containers: - Add Migrations if you haven't done on previous steps: + Install **dotnet-ef** cli: ``` From d74369e432197197b6d8c0151625322f0708dbda Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Fri, 31 Jan 2020 13:14:24 +0700 Subject: [PATCH 17/46] view + refresh tokens --- .../ExtensionMethods/HttpClientExtensions.cs | 1 - .../ExtensionMethods/ObjectExtensions.cs | 13 ++ .../CustomClaimsTransformation.cs | 10 +- .../Controllers/HomeController.cs | 113 ++++++++++-------- .../Models/Home/AuthenticationModel.cs | 8 ++ .../Models/Home/CurrentUserModel.cs | 23 ++++ .../Models/Home/TokenModel.cs | 10 ++ .../Views/Home/AuthorizedAction.cshtml | 57 +++++++++ .../Views/Shared/_Layout.cshtml | 3 - 9 files changed, 181 insertions(+), 57 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ObjectExtensions.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/AuthenticationModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/CurrentUserModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/TokenModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Home/AuthorizedAction.cshtml diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpClientExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpClientExtensions.cs index 77a607203..baf05ee67 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpClientExtensions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpClientExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Text; diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ObjectExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ObjectExtensions.cs new file mode 100644 index 000000000..7edad2dc1 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ObjectExtensions.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class ObjectExtensions + { + public static string AsJsonString(this object obj) + { + var content = JsonConvert.SerializeObject(obj, Formatting.Indented); + return content; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClaimsTransformations/CustomClaimsTransformation.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClaimsTransformations/CustomClaimsTransformation.cs index edf62d1f7..1a57ad914 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClaimsTransformations/CustomClaimsTransformation.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClaimsTransformations/CustomClaimsTransformation.cs @@ -11,7 +11,6 @@ public class CustomClaimsTransformation : IClaimsTransformation { public CustomClaimsTransformation() { - } public Task TransformAsync(ClaimsPrincipal principal) @@ -22,9 +21,16 @@ public Task TransformAsync(ClaimsPrincipal principal) return Task.FromResult(principal); } - var claims = new List { + var claims = new List + { new Claim("now", DateTime.Now.ToString(), ClaimValueTypes.String, "CustomClaimsTransformation"), }; + + if (identity.HasClaim(c => c.Type == "now")) + { + identity.RemoveClaim(identity.FindFirst(c => c.Type == "now")); + } + claims.AddRange(identity.Claims); var newIdentity = new ClaimsIdentity(claims, identity.AuthenticationType); return Task.FromResult(new ClaimsPrincipal(newIdentity)); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs index a6a952fb1..23e127419 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs @@ -1,25 +1,26 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using ClassifiedAds.WebMVC.Models; -using Microsoft.AspNetCore.Authorization; -using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using Microsoft.AspNetCore.Authentication; -using ClassifiedAds.DomainServices; -using IdentityModel.Client; using System.Net.Http; -using ClassifiedAds.DomainServices.Entities; -using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using System.Threading.Tasks; using ClassifiedAds.ApplicationServices; using ClassifiedAds.ApplicationServices.Queries.Products; -using Microsoft.Extensions.Logging; -using Microsoft.AspNetCore.Authentication.Cookies; -using System.Globalization; +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using ClassifiedAds.DomainServices; +using ClassifiedAds.DomainServices.Entities; using ClassifiedAds.WebMVC.ConfigurationOptions; +using ClassifiedAds.WebMVC.Models; +using ClassifiedAds.WebMVC.Models.Home; +using IdentityModel.Client; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; namespace ClassifiedAds.WebMVC.Controllers { @@ -40,33 +41,29 @@ public HomeController(IProductService productService, IHttpClientFactory httpCli _appSettings = appSettings.Value; } - public IActionResult Index() + public async Task Index() { _logger.LogInformation("Getting all products"); - var products = _dispatcher.Dispatch(new GetProductsQuery()); - return View(); - } - public async Task Privacy() - { - var httpClient = _httpClientFactory.CreateClient(); + var products1 = _dispatcher.Dispatch(new GetProductsQuery()); + var products2 = _productService.GetProducts().ToList(); var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken); - var metaDataResponse = await httpClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest + if (User.Identity.IsAuthenticated && !string.IsNullOrEmpty(accessToken)) { - Address = _appSettings.OpenIdConnect.Authority, - Policy = { RequireHttps = _appSettings.OpenIdConnect.RequireHttpsMetadata }, - }); - - var response = await httpClient.GetUserInfoAsync(new UserInfoRequest { Address = metaDataResponse.UserInfoEndpoint, Token = accessToken }); + var httpClient = _httpClientFactory.CreateClient(); - var products = _productService.GetProducts().ToList(); + httpClient.SetBearerToken(accessToken); + var response = await httpClient.GetAsync($"{_appSettings.ResourceServer.Endpoint}/api/products"); + var product3 = await response.Content.ReadAs>(); + } - httpClient.SetBearerToken(accessToken); - var response2 = await httpClient.GetAsync($"{_appSettings.ResourceServer.Endpoint}/api/products"); - var products2 = await response2.Content.ReadAs>(); + return View(); + } + public IActionResult Privacy() + { return View(); } @@ -79,9 +76,39 @@ public IActionResult Error() [Authorize] public async Task AuthorizedAction() { - var identityToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken); - var claims = User.Claims.Select(x => new { x.Type, x.Value }); - return Json(claims); + var model = new AuthenticationModel + { + User = new CurrentUserModel + { + Identity = new CurrentUserIdentityModel + { + IsAuthenticated = User.Identity.IsAuthenticated, + Name = User.Identity.Name, + AuthenticationType = User.Identity.AuthenticationType, + }, + Claims = User.Claims.Select(x => new ClaimModel { Type = x.Type, Value = x.Value }).ToList(), + }, + Token = new TokenModel + { + IdentityToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken), + AccessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken), + RefreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken), + ExpiresAt = await HttpContext.GetTokenAsync("expires_at"), + }, + }; + + var httpClient = _httpClientFactory.CreateClient(); + var metaDataResponse = await httpClient.GetDiscoveryDocumentAsync(_appSettings.OpenIdConnect.Authority); + var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken); + + var response = await httpClient.GetUserInfoAsync(new UserInfoRequest { Address = metaDataResponse.UserInfoEndpoint, Token = accessToken }); + + if (!response.IsError) + { + model.User.Claims.AddRange(response.Claims.Select(x => new ClaimModel { Type = x.Type, Value = x.Value })); + } + + return View(model); } [Authorize] @@ -96,22 +123,6 @@ public async Task Logout() await HttpContext.SignOutAsync("oidc"); } - [Authorize] - public async Task UserInfoClient() - { - var httpClient = _httpClientFactory.CreateClient(); - var metaDataResponse = await httpClient.GetDiscoveryDocumentAsync(_appSettings.OpenIdConnect.Authority); - var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken); - var response = await httpClient.GetUserInfoAsync(new UserInfoRequest { Address = metaDataResponse.UserInfoEndpoint, Token = accessToken }); - - if (response.IsError) - { - throw new Exception("Problem accessing the UserInfo endpoint.", response.Exception); - } - - return Json(response.Claims); - } - [Authorize] public async Task RefreshToken() { @@ -128,7 +139,7 @@ public async Task RefreshToken() Address = metaDataResponse.TokenEndpoint, ClientId = "ClassifiedAds.WebMVC", ClientSecret = "secret", - RefreshToken = refreshToken + RefreshToken = refreshToken, }); if (response.IsError) @@ -143,7 +154,7 @@ public async Task RefreshToken() auth.Properties.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture)); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, auth.Principal, auth.Properties); - return Json(response); + return RedirectToAction(nameof(AuthorizedAction)); } public IActionResult TestException() diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/AuthenticationModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/AuthenticationModel.cs new file mode 100644 index 000000000..3325269f2 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/AuthenticationModel.cs @@ -0,0 +1,8 @@ +namespace ClassifiedAds.WebMVC.Models.Home +{ + public class AuthenticationModel + { + public CurrentUserModel User { get; set; } + public TokenModel Token { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/CurrentUserModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/CurrentUserModel.cs new file mode 100644 index 000000000..3b801cf45 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/CurrentUserModel.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.WebMVC.Models.Home +{ + public class CurrentUserModel + { + public CurrentUserIdentityModel Identity { get; set; } + public List Claims { get; set; } + } + + public class CurrentUserIdentityModel + { + public string AuthenticationType { get; set; } + public bool IsAuthenticated { get; set; } + public string Name { get; set; } + } + + public class ClaimModel + { + public string Type { get; set; } + public string Value { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/TokenModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/TokenModel.cs new file mode 100644 index 000000000..24a35edd7 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Models/Home/TokenModel.cs @@ -0,0 +1,10 @@ +namespace ClassifiedAds.WebMVC.Models.Home +{ + public class TokenModel + { + public string IdentityToken { get; set; } + public string AccessToken { get; set; } + public string RefreshToken { get; set; } + public string ExpiresAt { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Home/AuthorizedAction.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Home/AuthorizedAction.cshtml new file mode 100644 index 000000000..dcd691c70 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Home/AuthorizedAction.cshtml @@ -0,0 +1,57 @@ +@using ClassifiedAds.WebMVC.Models.Home +@model AuthenticationModel +@{ + ViewData["Title"] = "AuthorizedAction"; +} + +

Auths Information

+
Click to Refresh Token + + + + + + + + + + + + + + + + + + + +
Access Token@Model.Token.AccessToken
Refresh Token@Model.Token.RefreshToken
Expires At@Model.Token.ExpiresAt
Identity Token@Model.Token.IdentityToken
+

Current User Information

+ + + + + + + + + + + + + + + +
Is Authenticated@Model.User.Identity.IsAuthenticated
Name@Model.User.Identity.Name
Authentication Type@Model.User.Identity.AuthenticationType
+

Claims

+ + + @foreach (var claim in Model.User.Claims) + { + + + + + } + +
@claim.Type@claim.Value
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml index 285bc2c15..23073c5ef 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml @@ -47,9 +47,6 @@ @if (User.Identity.IsAuthenticated) { - From ed833039d447869aaae0702b2c5be36b1e73d0af Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Fri, 31 Jan 2020 13:28:13 +0700 Subject: [PATCH 18/46] strongly typed configuration --- .../ConfigurationOptions/OpenIdConnect.cs | 2 ++ .../ClassifiedAds.WebMVC/Controllers/HomeController.cs | 4 ++-- src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs | 2 +- .../ClassifiedAds.WebMVC/appsettings.json | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/OpenIdConnect.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/OpenIdConnect.cs index 7d1c19407..f497bbf3b 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/OpenIdConnect.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/OpenIdConnect.cs @@ -9,6 +9,8 @@ public class OpenIdConnect public string ClientId { get; set; } + public string ClientSecret { get; set; } + public bool RequireHttpsMetadata { get; set; } public ValidateOptionsResult Validate() diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs index 23e127419..608186466 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs @@ -137,8 +137,8 @@ public async Task RefreshToken() var response = await httpClient.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = metaDataResponse.TokenEndpoint, - ClientId = "ClassifiedAds.WebMVC", - ClientSecret = "secret", + ClientId = _appSettings.OpenIdConnect.ClientId, + ClientSecret = _appSettings.OpenIdConnect.ClientSecret, RefreshToken = refreshToken, }); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index 982194601..45a52de6c 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -111,13 +111,13 @@ public void ConfigureServices(IServiceCollection services) options.SignInScheme = "Cookies"; options.Authority = appSettings.OpenIdConnect.Authority; options.ClientId = appSettings.OpenIdConnect.ClientId; + options.ClientSecret = appSettings.OpenIdConnect.ClientSecret; options.ResponseType = "code id_token"; options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("ClassifiedAds.WebAPI"); options.Scope.Add("offline_access"); options.SaveTokens = true; - options.ClientSecret = "secret"; options.GetClaimsFromUserInfoEndpoint = true; options.RequireHttpsMetadata = appSettings.OpenIdConnect.RequireHttpsMetadata; }); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json index fec84d948..ac56f768f 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/appsettings.json @@ -5,6 +5,7 @@ "OpenIdConnect": { "Authority": "https://localhost:44367", "ClientId": "ClassifiedAds.WebMVC", + "ClientSecret": "secret", "RequireHttpsMetadata": "true" }, "ResourceServer": { From 1cf2530fc87c707fa177a0dfd1744c944d23bff1 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sat, 8 Feb 2020 11:47:52 +0700 Subject: [PATCH 19/46] Configuring Authentication & Authorization for Hangfire Dashboard (#30) --- README.md | 2 +- .../Properties/launchSettings.json | 1 - .../Startup.cs | 3 +- .../IdServerPersistenceExtensions.cs | 122 ++++++++---------- .../ClassifiedAds.WebMVC.csproj | 1 + .../ConfigurationOptions/AppSettings.cs | 2 - .../ConfigurationOptions/BackgroundServer.cs | 7 - .../Controllers/HomeController.cs | 5 +- .../Filters/DashboardAuthorizationFilter.cs | 18 +++ .../ClassifiedAds.WebMVC/Startup.cs | 24 +++- .../Views/Shared/_Layout.cshtml | 2 +- .../ClassifiedAds.WebMVC/appsettings.json | 3 - src/ClassifiedAds.Projects/docker-compose.yml | 1 - 13 files changed, 101 insertions(+), 90 deletions(-) delete mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/BackgroundServer.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Filters/DashboardAuthorizationFilter.cs diff --git a/README.md b/README.md index 8cad1c77a..e620594e4 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,7 @@ ## Application URLs: | Project | Launch URL | Docker Container URL| Docker Container URL| | -------- | ---------- | ------------------- | ------------------- | -| BackgroundServices | [https://localhost:44318](https://localhost:44318/hangfire/) | [http://localhost:9004](http://localhost:9004/hangfire/) | [http://host.docker.internal:9004](http://host.docker.internal:9004/hangfire/) | +| BackgroundServices | https://localhost:44318 | http://localhost:9004 | http://host.docker.internal:9004 | | GRPC | https://localhost:5001 | https://localhost:9005 | https://host.docker.internal:9005 | | IdentityServer | https://localhost:44367 | http://localhost:9000 | http://host.docker.internal:9000 | | NotificationServer | https://localhost:44390 | http://localhost:9001 | http://host.docker.internal:9001 | diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Properties/launchSettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Properties/launchSettings.json index e41907d1d..eb5ec2d28 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Properties/launchSettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Properties/launchSettings.json @@ -10,7 +10,6 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchUrl": "hangfire", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Startup.cs index 99544fac5..d2663dc8a 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.BackgroundServices/Startup.cs @@ -53,7 +53,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - app.UseHangfireDashboard(); app.UseHangfireServer(); RecurringJob.AddOrUpdate(job => job.Run(), Cron.Minutely); @@ -62,7 +61,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.Run(async (context) => { - await context.Response.WriteAsync("Hello World!"); + await context.Response.WriteAsync("Hello Hangfire Server!"); }); } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs index a8bb78222..a9232d311 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs @@ -60,69 +60,66 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) { var clients = new List { - new Client - { - ClientId = "ClassifiedAds.WebMVC", - ClientName = "ClassifiedAds Web MVC", - AllowedGrantTypes = GrantTypes.Hybrid.Combines(GrantTypes.ResourceOwnerPassword), - RedirectUris = - { - "https://localhost:44364/signin-oidc", - "http://host.docker.internal:9003/signin-oidc", - }, - PostLogoutRedirectUris = - { - "https://localhost:44364/signout-callback-oidc", - "http://host.docker.internal:9003/signout-callback-oidc", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = + new Client { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, - }, - new Client - { - ClientId = "spa-client", - ClientName = "SPA Client", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - RedirectUris = - { - "http://localhost:4200/assets/oidc-login-redirect.html", - }, - PostLogoutRedirectUris = - { - "http://localhost:4200/?postLogout=true", - }, - AllowedCorsOrigins = - { - "http://localhost:4200/", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", + ClientId = "ClassifiedAds.WebMVC", + ClientName = "ClassifiedAds Web MVC", + AllowedGrantTypes = GrantTypes.Hybrid.Combines(GrantTypes.ResourceOwnerPassword), + RedirectUris = + { + "https://localhost:44364/signin-oidc", + "http://host.docker.internal:9003/signin-oidc", + }, + PostLogoutRedirectUris = + { + "https://localhost:44364/signout-callback-oidc", + "http://host.docker.internal:9003/signout-callback-oidc", + }, + AllowedScopes = + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "ClassifiedAds.WebAPI", + }, + ClientSecrets = + { + new Secret("secret".Sha256()), + }, + AllowOfflineAccess = true, }, - ClientSecrets = + new Client { - new Secret("secret".Sha256()), + ClientId = "spa-client", + ClientName = "SPA Client", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + RedirectUris = + { + "http://localhost:4200/assets/oidc-login-redirect.html", + }, + PostLogoutRedirectUris = + { + "http://localhost:4200/?postLogout=true", + }, + AllowedCorsOrigins = + { + "http://localhost:4200/", + }, + AllowedScopes = + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "ClassifiedAds.WebAPI", + }, + ClientSecrets = + { + new Secret("secret".Sha256()), + }, + AllowOfflineAccess = true, }, - AllowOfflineAccess = true, - }, }; - foreach (var client in clients) - { - context.Clients.Add(client.ToEntity()); - } + context.Clients.AddRange(clients.Select(x => x.ToEntity())); context.SaveChanges(); } @@ -135,10 +132,7 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) new IdentityResources.Profile(), }; - foreach (var resource in identityResources) - { - context.IdentityResources.Add(resource.ToEntity()); - } + context.IdentityResources.AddRange(identityResources.Select(x => x.ToEntity())); context.SaveChanges(); } @@ -150,10 +144,8 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) new ApiResource("ClassifiedAds.WebAPI", "ClassifiedAds Web API", new List() { "role" }), }; - foreach (var resource in apiResources) - { - context.ApiResources.Add(resource.ToEntity()); - } + + context.ApiResources.AddRange(apiResources.Select(x => x.ToEntity())); context.SaveChanges(); } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj index 22c7a3b9c..147d54dde 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ClassifiedAds.WebMVC.csproj @@ -16,6 +16,7 @@ + diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs index a7e70e1fc..ec9a73983 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/AppSettings.cs @@ -14,8 +14,6 @@ public class AppSettings public NotificationServer NotificationServer { get; set; } - public BackgroundServer BackgroundServer { get; set; } - public string AllowedHosts { get; set; } public string CurrentUrl { get; set; } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/BackgroundServer.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/BackgroundServer.cs deleted file mode 100644 index 5f068c63f..000000000 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/ConfigurationOptions/BackgroundServer.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ClassifiedAds.WebMVC.ConfigurationOptions -{ - public class BackgroundServer - { - public string Endpoint { get; set; } - } -} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs index 608186466..b6bc09b4a 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Controllers/HomeController.cs @@ -16,6 +16,7 @@ using IdentityModel.Client; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -119,8 +120,8 @@ public IActionResult Login() public async Task Logout() { - await HttpContext.SignOutAsync("Cookies"); - await HttpContext.SignOutAsync("oidc"); + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); } [Authorize] diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Filters/DashboardAuthorizationFilter.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Filters/DashboardAuthorizationFilter.cs new file mode 100644 index 000000000..25ac41e0a --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Filters/DashboardAuthorizationFilter.cs @@ -0,0 +1,18 @@ +using Hangfire.Dashboard; + +namespace ClassifiedAds.WebMVC.Filters +{ + public class DashboardAuthorizationFilter : IDashboardAuthorizationFilter + { + public bool Authorize(DashboardContext context) + { + var httpContext = context.GetHttpContext(); + if (!httpContext.User.Identity.IsAuthenticated) + { + httpContext.Response.Redirect("/home/login"); + } + + return true; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs index 45a52de6c..cae1b85ea 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Startup.cs @@ -18,8 +18,11 @@ using ClassifiedAds.WebMVC.Filters; using ClassifiedAds.WebMVC.HttpHandlers; using ClassifiedAds.WebMVC.Middleware; +using Hangfire; using HealthChecks.UI.Client; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; @@ -99,16 +102,16 @@ public void ConfigureServices(IServiceCollection services) services.AddAuthentication(options => { - options.DefaultScheme = "Cookies"; - options.DefaultChallengeScheme = "oidc"; + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) - .AddCookie("Cookies", options => + .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.AccessDeniedPath = "/Authorization/AccessDenied"; }) - .AddOpenIdConnect("oidc", options => + .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { - options.SignInScheme = "Cookies"; + options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.Authority = appSettings.OpenIdConnect.Authority; options.ClientId = appSettings.OpenIdConnect.ClientId; options.ClientSecret = appSettings.OpenIdConnect.ClientSecret; @@ -164,6 +167,11 @@ public void ConfigureServices(IServiceCollection services) setup.AddHealthCheckEndpoint("Basic Health Check", $"{appSettings.CurrentUrl}/healthcheck"); }); + services.AddHangfire(x => + { + x.UseSqlServerStorage(appSettings.ConnectionStrings.ClassifiedAds); + }); + services.AddSingleton(); services.AddScoped(); @@ -344,8 +352,14 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable, }, }); + app.UseHealthChecksUI(); // /healthchecks-ui#/healthchecks + app.UseHangfireDashboard("/hangfire", new DashboardOptions + { + Authorization = new[] { new DashboardAuthorizationFilter() }, + }); + app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml index 23073c5ef..76633ff0d 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.WebMVC/Views/Shared/_Layout.cshtml @@ -61,7 +61,7 @@ Health Checks UI
+
+
+
+ \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml new file mode 100644 index 000000000..50e5ea716 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml @@ -0,0 +1,165 @@ +@model ClassifiedAds.IdentityServer.Models.IdentityResourceModels.IdentityResourceModel + +
+
+ +
+ +
+

Identity Resource

+
+
+ +
+ +
+ + + + +
+
Name
+
+ +
+ +
+ + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + +
+
+ + @if (Model.Id != 0) + { + +
+ + +
+ } + + +
+ +
+ + @if (Model.Id != 0) + { + Delete + } +
+
+
+
+ +
+ +@section scripts + { + +} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Index.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Index.cshtml new file mode 100644 index 000000000..867e9b87d --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Index.cshtml @@ -0,0 +1,49 @@ +@model List + +
+
+

Identity Resources

+
+ + + +
+ @*@await Html.PartialAsync("Common/Search", new Search { Action = "IdentityResources", Controller = "Configuration" })*@ +
+
+ +
+
+
+ + + + + + + + + + @foreach (var identity in Model) + { + + + + + + } + +
Name
Edit@identity.Name + +
+
+
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "IdentityResources", PageSize = Model.PageSize, TotalCount = Model.TotalCount, EnableSearch = true, Search = ViewBag.Search })*@ +
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/PropertiesList.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/PropertiesList.cshtml new file mode 100644 index 000000000..1f81fe9fc --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/PropertiesList.cshtml @@ -0,0 +1,104 @@ +@model PropertiesListModel +@using ClassifiedAds.IdentityServer.Models.IdentityResourceModels + +
+
+ +
+ +
+ +
+ +

Properties

+ +
+ + + + + + + +
+
Add Property
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+
+
+
+
+
+ +
+
+
+
Properties
+
+
+ + + + + + + + + + @foreach (var identityResourceProperty in Model.Properties) + { + + + + + + } + +
KeyValue
@identityResourceProperty.Key@identityResourceProperty.ValueRemove
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "IdentityResourceProperties", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ +
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/_Label.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/_Label.cshtml new file mode 100644 index 000000000..dcfaabab2 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/_Label.cshtml @@ -0,0 +1,4 @@ +@model string + +@Model + \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml new file mode 100644 index 000000000..15ac2f2d0 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml @@ -0,0 +1,7 @@ + +@{ + ViewData["Title"] = "Index"; +} + +

Index

+ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml index f85021dee..f05812337 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml @@ -17,36 +17,51 @@ + + - diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml index 04ed626fe..44ad863e1 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml @@ -1,4 +1,51 @@ - -@* - For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 -*@ +@model IdentityServer4.EntityFramework.Entities.Client + + +
+
Consent
+
+ +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml index 04ed626fe..c454f2c33 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml @@ -1,4 +1,27 @@ - -@* - For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 -*@ +@model IdentityServer4.EntityFramework.Entities.Client + + +
+
Device Flow
+
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml index 04ed626fe..4da4f16f9 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml @@ -1,4 +1,184 @@ - -@* - For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 -*@ +@model IdentityServer4.EntityFramework.Entities.Client + + +
+
Token
+
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ + +
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml index f05812337..30c8cda80 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml @@ -15,8 +15,6 @@ IdentityServer4 - - @@ -67,8 +65,6 @@ @RenderBody() - - @RenderSection("scripts", required: false) From 72a141ddf197b61cc1d2215f748875d2a9d986e9 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sun, 1 Mar 2020 13:41:46 +0700 Subject: [PATCH 37/46] Adding Identity Server 4 Admin UI (#31) --- .../ClassifiedAds.IdentityServer.csproj | 4 - .../Controllers/ClientController.cs | 161 +++++++++++++++- .../Models/ApiResourceModels/SecretsModel.cs | 16 +- .../Models/ClientModels/ClaimModel.cs | 23 +++ .../Models/ClientModels/ClaimsModel.cs | 22 +++ .../Models/ClientModels/ClientModel.cs | 23 +++ .../Models/ClientModels/PropertiesModel.cs | 28 +++ .../Models/ClientModels/PropertyModel.cs | 27 +++ .../Models/ClientModels/SecretModel.cs | 31 ++++ .../Models/ClientModels/SecretsModel.cs | 57 ++++++ .../Views/Client/Claims.cshtml | 127 +++++++++++++ .../Views/Client/Delete.cshtml | 7 + .../Views/Client/DeleteClaim.cshtml | 62 +++++++ .../Views/Client/DeleteProperty.cshtml | 60 ++++++ .../Views/Client/DeleteSecret.cshtml | 63 +++++++ .../Views/Client/Edit.cshtml | 94 ++++------ .../Views/Client/Properties.cshtml | 101 ++++++++++ .../Views/Client/Secrets.cshtml | 174 ++++++++++++++++++ .../Views/Client/_Actions.cshtml | 11 ++ .../Views/Client/_Basics.cshtml | 4 +- .../Views/Client/_Token.cshtml | 2 +- .../Views/IdentityResource/Edit.cshtml | 17 +- 22 files changed, 1028 insertions(+), 86 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj index 627441249..61aec6fee 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj @@ -30,8 +30,4 @@ - - - - diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs index 1892ada46..489a0b93f 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using ClassifiedAds.IdentityServer.Models.ClientModels; using IdentityServer4.EntityFramework.DbContexts; +using IdentityServer4.EntityFramework.Entities; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -45,6 +47,12 @@ public IActionResult Index() return View(clients); } + public IActionResult Add() + { + var client = new Client(); + return View(nameof(Edit), client); + } + public IActionResult Edit(int id) { var client = _configurationDbContext.Clients @@ -86,10 +94,161 @@ public IActionResult GetGrantTypes() "authorization_code", "hybrid", "password", - "urn:ietf:params:oauth:grant-type:device_code" + "urn:ietf:params:oauth:grant-type:device_code", }; return Ok(allowedGrantypes); } + + public IActionResult Properties(int id) + { + var client = _configurationDbContext.Clients + .Include(x => x.Properties) + .FirstOrDefault(x => x.Id == id); + return View(PropertiesModel.FromEntity(client)); + } + + [HttpPost] + public IActionResult AddProperty(PropertiesModel model) + { + var client = _configurationDbContext.Clients + .Include(x => x.Properties) + .FirstOrDefault(x => x.Id == model.Client.Id); + + client.Properties.Add(new ClientProperty + { + Key = model.Key, + Value = model.Value, + }); + + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Properties), new { id = client.Id }); + } + + [HttpGet] + public IActionResult DeleteProperty(int id) + { + var prop = _configurationDbContext.Set() + .Include(x => x.Client) + .FirstOrDefault(x => x.Id == id); + return View(PropertyModel.FromEntity(prop)); + } + + [HttpPost] + public IActionResult DeleteProperty(PropertyModel model) + { + var client = _configurationDbContext.Clients + .Include(x => x.Properties) + .FirstOrDefault(x => x.Id == model.Client.Id); + var prop = client.Properties.FirstOrDefault(x => x.Id == model.Id); + client.Properties.Remove(prop); + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Properties), new { id = client.Id }); + } + + public IActionResult Secrets(int id) + { + var client = _configurationDbContext.Clients + .Include(x => x.ClientSecrets) + .FirstOrDefault(x => x.Id == id); + return View(SecretsModel.FromEntity(client)); + } + + [HttpPost] + public IActionResult Secrets(SecretsModel model) + { + var client = _configurationDbContext.Clients + .Include(x => x.ClientSecrets) + .FirstOrDefault(x => x.Id == model.Client.Id); + + var secret = new ClientSecret + { + Created = DateTime.Now, + }; + + model.HashSecret(); + model.UpdateEntity(secret); + client.ClientSecrets.Add(secret); + + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Secrets), new { id = client.Id }); + } + + [HttpGet] + public IActionResult DeleteSecret(int id) + { + var secret = _configurationDbContext.Set() + .Include(x => x.Client) + .FirstOrDefault(x => x.Id == id); + return View(SecretModel.FromEntity(secret)); + } + + [HttpPost] + public IActionResult DeleteSecret(SecretModel model) + { + var client = _configurationDbContext.Clients + .Include(x => x.ClientSecrets) + .FirstOrDefault(x => x.Id == model.Client.Id); + var secret = client.ClientSecrets.FirstOrDefault(x => x.Id == model.Id); + client.ClientSecrets.Remove(secret); + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Secrets), new { id = client.Id }); + } + + public IActionResult Claims(int id) + { + var client = _configurationDbContext.Clients + .Include(x => x.Claims) + .FirstOrDefault(x => x.Id == id); + + return View(ClaimsModel.FromEntity(client)); + } + + [HttpPost] + public IActionResult Claims(ClaimModel model) + { + var client = _configurationDbContext.Clients + .Include(x => x.Claims) + .FirstOrDefault(x => x.Id == model.Client.Id); + + client.Claims.Add(new ClientClaim + { + Type = model.Type, + Value = model.Value, + }); + + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Claims), new { id = client.Id }); + } + + public IActionResult DeleteClaim(int id) + { + var claim = _configurationDbContext.Set() + .Include(x => x.Client) + .FirstOrDefault(x => x.Id == id); + + return View(ClaimModel.FromEntity(claim)); + } + + [HttpPost] + public IActionResult DeleteClaim(ClaimModel model) + { + var client = _configurationDbContext.Clients + .Include(x => x.Claims) + .FirstOrDefault(x => x.Id == model.Client.Id); + + var claim = client.Claims.FirstOrDefault(x => x.Id == model.Id); + + client.Claims.Remove(claim); + + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Claims), new { id = client.Id }); + } } } \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs index cec75a61e..e66ae3d6e 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs @@ -7,12 +7,6 @@ namespace ClassifiedAds.IdentityServer.Models.ApiResourceModels { - public enum HashType - { - Sha256, - Sha512, - } - public class SecretsModel : SecretModel { public List Secrets { get; set; } @@ -60,15 +54,7 @@ public static SecretsModel FromEntity(IdentityServer4.EntityFramework.Entities.A { ApiResourceId = apiResource.Id, ApiResourceName = apiResource.Name, - Secrets = apiResource.Secrets?.Select(x => new SecretModel - { - Id = x.Id, - Description = x.Description, - Value = x.Value, - Expiration = x.Expiration, - Type = x.Type, - Created = x.Created, - })?.ToList(), + Secrets = apiResource.Secrets?.Select(x => FromEntity(x))?.ToList(), }; } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs new file mode 100644 index 000000000..117ae9f53 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs @@ -0,0 +1,23 @@ +using IdentityServer4.EntityFramework.Entities; + +namespace ClassifiedAds.IdentityServer.Models.ClientModels +{ + public class ClaimModel + { + public int Id { get; set; } + public string Type { get; set; } + public string Value { get; set; } + public ClientModel Client { get; set; } + + public static ClaimModel FromEntity(ClientClaim claim) + { + return new ClaimModel + { + Id = claim.Id, + Type = claim.Type, + Value = claim.Value, + Client = ClientModel.FromEntity(claim.Client), + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs new file mode 100644 index 000000000..e18b56af4 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs @@ -0,0 +1,22 @@ +using IdentityServer4.EntityFramework.Entities; +using System.Collections.Generic; +using System.Linq; + +namespace ClassifiedAds.IdentityServer.Models.ClientModels +{ + public class ClaimsModel : ClaimModel + { + public List Claims { get; set; } + + public static ClaimsModel FromEntity(Client client) + { + var clientModel = ClientModel.FromEntity(client); + + return new ClaimsModel + { + Client = clientModel, + Claims = client.Claims?.Select(x => FromEntity(x))?.ToList(), + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs new file mode 100644 index 000000000..f91cef79f --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs @@ -0,0 +1,23 @@ +using IdentityServer4.EntityFramework.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ClassifiedAds.IdentityServer.Models.ClientModels +{ + public class ClientModel : IdentityServer4.Models.Client + { + public int Id { get; set; } + + public static ClientModel FromEntity(Client client) + { + return new ClientModel + { + Id = client.Id, + ClientId = client.ClientId, + ClientName = client.ClientName, + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs new file mode 100644 index 000000000..37baca777 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs @@ -0,0 +1,28 @@ +using IdentityServer4.EntityFramework.Entities; +using IdentityServer4.EntityFramework.Mappers; +using System.Collections.Generic; +using System.Linq; + +namespace ClassifiedAds.IdentityServer.Models.ClientModels +{ + public class PropertiesModel : PropertyModel + { + public List Properties { get; set; } + + public static PropertiesModel FromEntity(Client client) + { + var clientModel = ClientModel.FromEntity(client); + return new PropertiesModel + { + Client = clientModel, + Properties = client.Properties?.Select(x => new PropertyModel + { + Id = x.Id, + Key = x.Key, + Value = x.Value, + Client = clientModel, + })?.ToList(), + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs new file mode 100644 index 000000000..fbdc7ae14 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs @@ -0,0 +1,27 @@ +using IdentityServer4.EntityFramework.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ClassifiedAds.IdentityServer.Models.ClientModels +{ + public class PropertyModel + { + public int Id { get; set; } + public string Key { get; set; } + public string Value { get; set; } + public ClientModel Client { get; set; } + + public static PropertyModel FromEntity(ClientProperty prop) + { + return new PropertyModel + { + Id = prop.Id, + Key = prop.Key, + Value = prop.Value, + Client = ClientModel.FromEntity(prop.Client), + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs new file mode 100644 index 000000000..9e581ad29 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs @@ -0,0 +1,31 @@ +using IdentityServer4.EntityFramework.Entities; +using System; + +namespace ClassifiedAds.IdentityServer.Models.ClientModels +{ + public class SecretModel + { + public int Id { get; set; } + public string Description { get; set; } + public string Value { get; set; } + public DateTime? Expiration { get; set; } + public string Type { get; set; } + public string HashType { get; set; } + public DateTime Created { get; set; } + public ClientModel Client { get; set; } + + public static SecretModel FromEntity(ClientSecret secret) + { + return new SecretModel + { + Id = secret.Id, + Description = secret.Description, + Value = secret.Value, + Expiration = secret.Expiration, + Type = secret.Type, + Created = secret.Created, + Client = ClientModel.FromEntity(secret.Client), + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs new file mode 100644 index 000000000..07df9f198 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs @@ -0,0 +1,57 @@ +using IdentityServer4.Models; +using System.Collections.Generic; +using System.Linq; + +namespace ClassifiedAds.IdentityServer.Models.ClientModels +{ + public class SecretsModel : SecretModel + { + public List Secrets { get; set; } + public List TypeList { get; } = new List + { + "SharedSecret", + "X509Thumbprint", + "X509Name", + "X509CertificateBase64", + }; + public List HashTypes { get; } = new List + { + "Sha256", + "Sha512", + }; + + public void HashSecret() + { + if (Type != "SharedSecret") + { + return; + } + + if (HashType == "Sha256") + { + Value = Value.Sha256(); + } + else if (HashType == "Sha512") + { + Value = Value.Sha512(); + } + } + + public void UpdateEntity(IdentityServer4.EntityFramework.Entities.Secret entity) + { + entity.Description = Description; + entity.Value = Value; + entity.Expiration = Expiration; + entity.Type = Type; + } + + public static SecretsModel FromEntity(IdentityServer4.EntityFramework.Entities.Client client) + { + return new SecretsModel + { + Client = ClientModel.FromEntity(client), + Secrets = client.ClientSecrets?.Select(x => FromEntity(x))?.ToList(), + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml new file mode 100644 index 000000000..4f7c2c7f3 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml @@ -0,0 +1,127 @@ +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClaimsModel + + +
+ +
+ +
+ +
+

Claims

+ +
+ + + + +
+
Add Claim
+
+ + +
+ +
+ + + + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+
+
+
+
+ + +
+
+
+
Claims
+
+ +
+ + + + + + + + + + + @foreach (var clientClaim in Model.Claims) + { + + + + + + } + +
TypeValue
@clientClaim.Type@clientClaim.ValueRemove
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ClientClaims", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ +
+
+
+
+
+
+ +@section scripts + { + +} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml new file mode 100644 index 000000000..7bfbf4d37 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml @@ -0,0 +1,7 @@ + +@{ + ViewData["Title"] = "Delete"; +} + +

Delete

+ diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml new file mode 100644 index 000000000..f5ff01d66 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml @@ -0,0 +1,62 @@ +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClaimModel + +
+
+ +
+ +
+ +
+

Delete Claim

+ + + + + + +
+
Delete Claim
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml new file mode 100644 index 000000000..a6b4080bf --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml @@ -0,0 +1,60 @@ +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model PropertyModel + +
+
+ +
+ +
+ +
+

Delete Property

+ + + + + +
+
Delete Property
+
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml new file mode 100644 index 000000000..c314f4bc5 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml @@ -0,0 +1,63 @@ +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model SecretModel + +
+
+ +
+ +
+ +
+

Delete Secret

+ + + + + + + +
+
Delete Secret
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml index df01fd178..7baa7d264 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml @@ -27,71 +27,55 @@ @if (Model.Id != 0) {
-
- - - @if (Model.Id != 0) - { - Clone - Delete - } -
+ @await Html.PartialAsync("_Actions")
} -
-
-

Settings

- -
-
-
- - - @if (Model.Id != 0) - { - Clone - Delete - } -
+ @await Html.PartialAsync("_Actions")
diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml new file mode 100644 index 000000000..45491a159 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml @@ -0,0 +1,101 @@ +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model PropertiesModel + +
+
+ +
+ +
+ +
+ +

Properties

+ +
+ + + + +
+
Add Property
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+
+
+
+
+
+ +
+
+
+
Properties
+
+
+ + + + + + + + + + @foreach (var ApiResourceProperty in Model.Properties) + { + + + + + + } + +
KeyValue
@ApiResourceProperty.Key@ApiResourceProperty.ValueRemove
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ApiResourceProperties", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ +
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml new file mode 100644 index 000000000..b72a29a27 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml @@ -0,0 +1,174 @@ +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model SecretsModel + +
+
+ +
+ +
+ +
+

Client Secrets

+
+ +
+
+
+ +
+
+
Add Client Secrets
+
+ + + + + +
+ +
+ +
+
+ + +
+ +
+ +
+ +
+ +
+
+ +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+
+ +
+ +
+
+
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+
Client Secrets
+
+ +
+ + + + + + + + + + + + + + @foreach (var secret in Model.Secrets) + { + + + + + + + + + } + +
TypeValueDescriptionExpirationCreated
Remove@secret.Type@secret.Description@(secret.Expiration.HasValue ? secret.Expiration.Value.Date.ToShortDateString() : string.Empty)@secret.Created.Date.ToShortDateString()
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ApiSecrets", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ +
+
+ + +
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml new file mode 100644 index 000000000..34d751f41 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml @@ -0,0 +1,11 @@ +@model IdentityServer4.EntityFramework.Entities.Client + +
+ + + @if (Model.Id != 0) + { + Clone + Delete + } +
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml index 9ce52e744..b98ba4a18 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml @@ -168,7 +168,7 @@ @await Html.PartialAsync("_Label", "Client Secrets") @@ -179,7 +179,7 @@ @await Html.PartialAsync("_Label", "Properties") diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml index 4da4f16f9..3a8908e85 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml @@ -177,7 +177,7 @@ @await Html.PartialAsync("_Label", "Claims") diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml index 381f14598..68ac23024 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml @@ -111,14 +111,15 @@
+ selected-items="@Model.UserClaims" + url="@Url.Action("GetClaims","IdentityResource")?claim" + search-input-placeholder="search" + selected-items-title="selected items" + search-result-title="search result" + suggested-items-title="suggested items" + no-item-selected-title="no item selected" + show-all-items-title="show all items" + item-already-selected-title="item already selected">
From e3f6c520905681064ad349993b73863aff52c811 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sun, 1 Mar 2020 18:51:09 +0700 Subject: [PATCH 38/46] Adding Identity Server 4 Admin UI (#31) --- .../ExtensionMethods/TypeExtensions.cs | 35 +++++ .../ClassifiedAds.IdentityServer.csproj | 1 + .../Controllers/ClientController.cs | 55 +++++-- .../Models/ClientModels/ClientModel.cs | 146 ++++++++++++++++-- .../Views/Client/Delete.cshtml | 9 +- .../Views/Client/Edit.cshtml | 5 +- .../Views/Client/Index.cshtml | 3 +- .../Views/Client/_Actions.cshtml | 3 +- .../Views/Client/_Authentication.cshtml | 8 +- .../Views/Client/_Basics.cshtml | 10 +- .../Views/Client/_Consent.cshtml | 4 +- .../Views/Client/_DeviceFlow.cshtml | 4 +- .../Views/Client/_Name.cshtml | 122 +++++++-------- .../Views/Client/_Token.cshtml | 6 +- 14 files changed, 301 insertions(+), 110 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/TypeExtensions.cs diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/TypeExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/TypeExtensions.cs new file mode 100644 index 000000000..8a7663391 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/TypeExtensions.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using System.Text; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class TypeExtensions + { + public static string GenerateMappingCode(this Type type) + { + var names = type.GetProperties().Select(x => x.Name); + + var text1 = new StringBuilder(); + var text2 = new StringBuilder(); + var text3 = new StringBuilder(); + var text4 = new StringBuilder(); + + foreach (var name in names) + { + text1.Append($"a.{name} = {name};{Environment.NewLine}"); + text2.Append($"{name} = b.{name};{Environment.NewLine}"); + text3.Append($"{name} = b.{name},{Environment.NewLine}"); + text4.Append($"a.{name} = b.{name};{Environment.NewLine}"); + } + + return text1.ToString() + + "--------------------------------------" + Environment.NewLine + + text2.ToString() + + "--------------------------------------" + Environment.NewLine + + text3.ToString() + + "--------------------------------------" + Environment.NewLine + + text4.ToString(); + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj index 61aec6fee..75310768f 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj @@ -6,6 +6,7 @@ + diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs index 489a0b93f..d04cbd125 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs @@ -10,16 +10,6 @@ namespace ClassifiedAds.IdentityServer.Controllers { - public enum ClientType - { - Empty = 0, - WebHybrid = 1, - Spa = 2, - Native = 3, - Machine = 4, - Device = 5, - } - public class ClientController : Controller { private readonly ConfigurationDbContext _configurationDbContext; @@ -44,12 +34,14 @@ public IActionResult Index() .AsNoTracking() .ToList(); - return View(clients); + var models = clients.Select(x => ClientModel.FromEntity(x)).ToList(); + + return View(models); } public IActionResult Add() { - var client = new Client(); + var client = new ClientModel(); return View(nameof(Edit), client); } @@ -69,7 +61,44 @@ public IActionResult Edit(int id) .AsNoTracking() .FirstOrDefault(); - return View(client); + var model = ClientModel.FromEntity(client); + + return View(model); + } + + [HttpPost] + public IActionResult Edit(ClientModel model) + { + Client client; + if (model.Id == 0) + { + model.SetDefaultValues(); + client = new Client(); + _configurationDbContext.Clients.Add(client); + } + else + { + model.ConvertItemsToList(); + + client = _configurationDbContext.Clients + .Include(x => x.AllowedGrantTypes) + .Include(x => x.RedirectUris) + .Include(x => x.PostLogoutRedirectUris) + .Include(x => x.AllowedScopes) + .Include(x => x.ClientSecrets) + .Include(x => x.Claims) + .Include(x => x.IdentityProviderRestrictions) + .Include(x => x.AllowedCorsOrigins) + .Include(x => x.Properties) + .Where(x => x.Id == model.Id) + .FirstOrDefault(); + client.Updated = DateTime.Now; + } + + model.UpdateEntity(client); + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Edit), new { id = client.Id }); } public IActionResult GetScopes() diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs index f91cef79f..5c39916e9 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs @@ -1,23 +1,151 @@ -using IdentityServer4.EntityFramework.Entities; -using System; +using AutoMapper; +using IdentityServer4.EntityFramework.Entities; +using IdentityServer4.EntityFramework.Mappers; +using IdentityServer4.Models; +using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace ClassifiedAds.IdentityServer.Models.ClientModels { + public enum ClientType + { + Empty = 0, + WebHybrid = 1, + Spa = 2, + Native = 3, + Machine = 4, + Device = 5, + } + public class ClientModel : IdentityServer4.Models.Client { public int Id { get; set; } + public ClientType ClientType { get; set; } + public string AllowedGrantTypesItems { get; set; } + public string RedirectUrisItems { get; set; } + public string PostLogoutRedirectUrisItems { get; set; } + public string AllowedScopesItems { get; set; } + public string IdentityProviderRestrictionsItems { get; set; } + public string AllowedCorsOriginsItems { get; set; } + + public static ClientModel FromEntity(IdentityServer4.EntityFramework.Entities.Client client) + { + var config = new MapperConfiguration(cfg => cfg.CreateMap()); + var mapper = config.CreateMapper(); + var model = mapper.Map(client.ToModel()); + model.Id = client.Id; + return model; + } - public static ClientModel FromEntity(Client client) + public void SetDefaultValues() { - return new ClientModel + switch (ClientType) + { + case ClientType.Empty: + break; + case ClientType.WebHybrid: + AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.Hybrid).ToList(); + break; + case ClientType.Spa: + AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.Code).ToList(); + RequirePkce = true; + RequireClientSecret = false; + break; + case ClientType.Native: + AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.Hybrid).ToList(); + break; + case ClientType.Machine: + AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.ResourceOwnerPasswordAndClientCredentials).ToList(); + break; + case ClientType.Device: + AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.DeviceFlow).ToList(); + RequireClientSecret = false; + AllowOfflineAccess = true; + break; + } + } + + public void ConvertItemsToList() + { + AllowedGrantTypes = ConvertItems(AllowedGrantTypesItems); + RedirectUris = ConvertItems(RedirectUrisItems); + PostLogoutRedirectUris = ConvertItems(PostLogoutRedirectUrisItems); + AllowedScopes = ConvertItems(AllowedScopesItems); + IdentityProviderRestrictions = ConvertItems(IdentityProviderRestrictionsItems); + AllowedCorsOrigins = ConvertItems(AllowedCorsOriginsItems); + } + + private List ConvertItems(string items) + { + return string.IsNullOrWhiteSpace(items) + ? new List() + : JsonConvert.DeserializeObject>(items); + } + + public void UpdateEntity(IdentityServer4.EntityFramework.Entities.Client entity) + { + entity.Enabled = Enabled; + entity.ClientId = ClientId; + entity.ProtocolType = ProtocolType; + entity.RequireClientSecret = RequireClientSecret; + entity.ClientName = ClientName; + entity.Description = Description; + entity.ClientUri = ClientUri; + entity.LogoUri = LogoUri; + entity.RequireConsent = RequireConsent; + entity.AllowRememberConsent = AllowRememberConsent; + entity.AlwaysIncludeUserClaimsInIdToken = AlwaysIncludeUserClaimsInIdToken; + entity.AllowedGrantTypes = AllowedGrantTypes.Select(x => new ClientGrantType + { + GrantType = x, + }).ToList(); + entity.RequirePkce = RequirePkce; + entity.AllowPlainTextPkce = AllowPlainTextPkce; + entity.AllowAccessTokensViaBrowser = AllowAccessTokensViaBrowser; + entity.RedirectUris = RedirectUris.Select(x => new ClientRedirectUri + { + RedirectUri = x, + }).ToList(); + entity.PostLogoutRedirectUris = PostLogoutRedirectUris.Select(x => new ClientPostLogoutRedirectUri + { + PostLogoutRedirectUri = x, + }).ToList(); + entity.FrontChannelLogoutUri = FrontChannelLogoutUri; + entity.FrontChannelLogoutSessionRequired = FrontChannelLogoutSessionRequired; + entity.BackChannelLogoutUri = BackChannelLogoutUri; + entity.BackChannelLogoutSessionRequired = BackChannelLogoutSessionRequired; + entity.AllowOfflineAccess = AllowOfflineAccess; + entity.AllowedScopes = AllowedScopes.Select(x => new ClientScope + { + Scope = x, + }).ToList(); + entity.IdentityTokenLifetime = IdentityTokenLifetime; + entity.AccessTokenLifetime = AccessTokenLifetime; + entity.AuthorizationCodeLifetime = AuthorizationCodeLifetime; + entity.ConsentLifetime = ConsentLifetime; + entity.AbsoluteRefreshTokenLifetime = AbsoluteRefreshTokenLifetime; + entity.SlidingRefreshTokenLifetime = SlidingRefreshTokenLifetime; + entity.RefreshTokenUsage = (int)RefreshTokenUsage; + entity.UpdateAccessTokenClaimsOnRefresh = UpdateAccessTokenClaimsOnRefresh; + entity.RefreshTokenExpiration = (int)RefreshTokenExpiration; + entity.AccessTokenType = (int)AccessTokenType; + entity.EnableLocalLogin = EnableLocalLogin; + entity.IdentityProviderRestrictions = IdentityProviderRestrictions.Select(x => new ClientIdPRestriction + { + Provider = x, + }).ToList(); + entity.IncludeJwtId = IncludeJwtId; + entity.AlwaysSendClientClaims = AlwaysSendClientClaims; + entity.ClientClaimsPrefix = ClientClaimsPrefix; + entity.PairWiseSubjectSalt = PairWiseSubjectSalt; + entity.AllowedCorsOrigins = AllowedCorsOrigins.Select(x => new ClientCorsOrigin { - Id = client.Id, - ClientId = client.ClientId, - ClientName = client.ClientName, - }; + Origin = x, + }).ToList(); + entity.UserSsoLifetime = UserSsoLifetime; + entity.UserCodeType = UserCodeType; + entity.DeviceCodeLifetime = DeviceCodeLifetime; } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml index 7bfbf4d37..310ef4fbd 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml @@ -1,7 +1,2 @@ - -@{ - ViewData["Title"] = "Delete"; -} - -

Delete

- +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClientModel diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml index 7baa7d264..a0fcf8e7f 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml @@ -1,6 +1,7 @@ -@model IdentityServer4.EntityFramework.Entities.Client +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClientModel -
+
diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml index a637d7b3a..85806dd20 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml @@ -1,4 +1,5 @@ -@model List +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model List
diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml index 34d751f41..7afe7203c 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml @@ -1,4 +1,5 @@ -@model IdentityServer4.EntityFramework.Entities.Client +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClientModel
diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml index 1ce14db8a..120638006 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml @@ -1,5 +1,5 @@ -@model IdentityServer4.EntityFramework.Entities.Client - +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClientModel
Authentication
@@ -67,7 +67,7 @@
> { new KeyValuePair("oidc", "OpenID Connect") }; } @@ -109,7 +109,7 @@
Consent
diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml index c454f2c33..42e8085db 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml @@ -1,5 +1,5 @@ -@model IdentityServer4.EntityFramework.Entities.Client - +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClientModel
Device Flow
diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml index c5e890982..58a62ad0e 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml @@ -1,5 +1,5 @@ -@model IdentityServer4.EntityFramework.Entities.Client -@using ClassifiedAds.IdentityServer.Controllers +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClientModel
Name
@@ -29,74 +29,74 @@ @if (Model.Id == 0) { -
-
-
- +
+
+
+ +
-
-
-
-
- +
+
+
+ +
-
-
-
-
- +
+
+
+ +
-
-
-
-
- +
+
+
+ +
-
-
-
-
- +
+
+
+ +
-
-
-
-
- +
+
+
+ +
-
}
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml index 3a8908e85..099f3d24f 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml @@ -1,5 +1,5 @@ -@model IdentityServer4.EntityFramework.Entities.Client - +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClientModel
Token
@@ -91,7 +91,7 @@
Date: Sun, 1 Mar 2020 20:53:44 +0700 Subject: [PATCH 39/46] Adding Identity Server 4 Admin UI (#31) --- .../ClassifiedAds.Blazor/appsettings.json | 2 +- .../Controllers/ClientController.cs | 60 +++++++++++++ .../Models/ClientModels/ClientModel.cs | 2 + .../Views/Client/Clone.cshtml | 88 +++++++++++++++++++ .../Views/Client/Delete.cshtml | 54 ++++++++++++ .../Views/Client/_Actions.cshtml | 2 +- .../IdServerPersistenceExtensions.cs | 4 +- 7 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Blazor/appsettings.json b/src/ClassifiedAds.Projects/ClassifiedAds.Blazor/appsettings.json index cd61338e8..d03e33a3f 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Blazor/appsettings.json +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Blazor/appsettings.json @@ -9,7 +9,7 @@ "AllowedHosts": "*", "OpenIdConnect": { "Authority": "https://localhost:44367", - "ClientId": "ClassifiedAds.BlazorServerSide", + "ClientId": "ClassifiedAds.Blazor", "ClientSecret": "secret", "RequireHttpsMetadata": "true" }, diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs index d04cbd125..221d33e4f 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/ClientController.cs @@ -101,6 +101,66 @@ public IActionResult Edit(ClientModel model) return RedirectToAction(nameof(Edit), new { id = client.Id }); } + public IActionResult Clone(int id) + { + var client = _configurationDbContext.Clients + .Include(x => x.AllowedGrantTypes) + .Include(x => x.RedirectUris) + .Include(x => x.PostLogoutRedirectUris) + .Include(x => x.AllowedScopes) + .Include(x => x.ClientSecrets) + .Include(x => x.Claims) + .Include(x => x.IdentityProviderRestrictions) + .Include(x => x.AllowedCorsOrigins) + .Include(x => x.Properties) + .Where(x => x.Id == id) + .AsNoTracking() + .FirstOrDefault(); + + var model = ClientModel.FromEntity(client); + model.OriginalClientId = model.ClientId; + model.ClientId = $"Clone_From_{model.ClientId}_{DateTime.Now.ToString("yyyyMMddhhmmssfff")}"; + + return View(model); + } + + [HttpPost] + public IActionResult Clone(ClientModel model) + { + Client client = new Client(); + model.ConvertItemsToList(); + model.UpdateEntity(client); + + _configurationDbContext.Clients.Add(client); + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Edit), new { id = client.Id }); + } + + public IActionResult Delete(int id) + { + var client = _configurationDbContext.Clients + .Where(x => x.Id == id) + .AsNoTracking() + .FirstOrDefault(); + + var model = ClientModel.FromEntity(client); + + return View(model); + } + + [HttpPost] + public IActionResult Delete(ClientModel model) + { + var client = _configurationDbContext.Clients + .FirstOrDefault(x => x.Id == model.Id); + + _configurationDbContext.Clients.Remove(client); + _configurationDbContext.SaveChanges(); + + return RedirectToAction(nameof(Index)); + } + public IActionResult GetScopes() { var identityResources = _configurationDbContext.IdentityResources diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs index 5c39916e9..bc4bf4b9a 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs @@ -29,6 +29,8 @@ public class ClientModel : IdentityServer4.Models.Client public string IdentityProviderRestrictionsItems { get; set; } public string AllowedCorsOriginsItems { get; set; } + public string OriginalClientId { get; set; } + public static ClientModel FromEntity(IdentityServer4.EntityFramework.Entities.Client client) { var config = new MapperConfiguration(cfg => cfg.CreateMap()); diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml new file mode 100644 index 000000000..be4197994 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml @@ -0,0 +1,88 @@ +@using ClassifiedAds.IdentityServer.Models.ClientModels +@model ClientModel + + + +
+
+ +
+
+

Clone @Model.OriginalClientId

+
+
+
+
+
+ +
+
+ +
+
+ +
+
+

Settings

+ + +
+
+
+
+ +
+
+ + +@section scripts + { + +} + diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml index 310ef4fbd..408837e56 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml @@ -1,2 +1,56 @@ @using ClassifiedAds.IdentityServer.Models.ClientModels @model ClientModel + +
+
+ +
+
+ +
+ + + + +

Delete

+ +
+
Delete
+
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml index 7afe7203c..8091610d8 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml @@ -6,7 +6,7 @@ @if (Model.Id != 0) { - Clone + Clone Delete }
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs index 9c0341b50..2a97c1e8b 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs @@ -89,8 +89,8 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) }, new Client { - ClientId = "ClassifiedAds.BlazorServerSide", - ClientName = "ClassifiedAds Blazor Server Side", + ClientId = "ClassifiedAds.Blazor", + ClientName = "ClassifiedAds Blazor", AllowedGrantTypes = GrantTypes.Hybrid.Combines(GrantTypes.ResourceOwnerPassword), RedirectUris = { From d1dc33be14f7e03869d5d5864aacc1417c932c70 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sun, 1 Mar 2020 21:15:54 +0700 Subject: [PATCH 40/46] Adding Identity Server 4 Admin UI (#31) --- .../IdServerPersistenceExtensions.cs | 172 +++++++++--------- 1 file changed, 90 insertions(+), 82 deletions(-) diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs index 2a97c1e8b..d42fa2580 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs @@ -56,98 +56,106 @@ public static void MigrateIdServerDb(this IApplicationBuilder app) var context = serviceScope.ServiceProvider.GetRequiredService(); context.Database.Migrate(); - if (!context.Clients.Any()) + var clients = new List(); + if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.WebMVC")) { - var clients = new List + clients.Add(new Client { - new Client + ClientId = "ClassifiedAds.WebMVC", + ClientName = "ClassifiedAds Web MVC", + AllowedGrantTypes = GrantTypes.Hybrid.Combines(GrantTypes.ResourceOwnerPassword), + RedirectUris = { - ClientId = "ClassifiedAds.WebMVC", - ClientName = "ClassifiedAds Web MVC", - AllowedGrantTypes = GrantTypes.Hybrid.Combines(GrantTypes.ResourceOwnerPassword), - RedirectUris = - { - "https://localhost:44364/signin-oidc", - "http://host.docker.internal:9003/signin-oidc", - }, - PostLogoutRedirectUris = - { - "https://localhost:44364/signout-callback-oidc", - "http://host.docker.internal:9003/signout-callback-oidc", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, + "https://localhost:44364/signin-oidc", + "http://host.docker.internal:9003/signin-oidc", }, - new Client + PostLogoutRedirectUris = { - ClientId = "ClassifiedAds.Blazor", - ClientName = "ClassifiedAds Blazor", - AllowedGrantTypes = GrantTypes.Hybrid.Combines(GrantTypes.ResourceOwnerPassword), - RedirectUris = - { - "https://localhost:44331/signin-oidc", - "http://host.docker.internal:9008/signin-oidc", - }, - PostLogoutRedirectUris = - { - "https://localhost:44331/signout-callback-oidc", - "http://host.docker.internal:9008/signout-callback-oidc", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, + "https://localhost:44364/signout-callback-oidc", + "http://host.docker.internal:9003/signout-callback-oidc", }, - new Client + AllowedScopes = { - ClientId = "spa-client", - ClientName = "SPA Client", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - RedirectUris = - { - "http://localhost:4200/assets/oidc-login-redirect.html", - }, - PostLogoutRedirectUris = - { - "http://localhost:4200/?postLogout=true", - }, - AllowedCorsOrigins = - { - "http://localhost:4200", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "ClassifiedAds.WebAPI", }, - }; + ClientSecrets = + { + new Secret("secret".Sha256()), + }, + AllowOfflineAccess = true, + }); + } - context.Clients.AddRange(clients.Select(x => x.ToEntity())); + if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.Blazor")) + { + clients.Add(new Client + { + ClientId = "ClassifiedAds.Blazor", + ClientName = "ClassifiedAds Blazor", + AllowedGrantTypes = GrantTypes.Hybrid.Combines(GrantTypes.ResourceOwnerPassword), + RedirectUris = + { + "https://localhost:44331/signin-oidc", + "http://host.docker.internal:9008/signin-oidc", + }, + PostLogoutRedirectUris = + { + "https://localhost:44331/signout-callback-oidc", + "http://host.docker.internal:9008/signout-callback-oidc", + }, + AllowedScopes = + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "ClassifiedAds.WebAPI", + }, + ClientSecrets = + { + new Secret("secret".Sha256()), + }, + AllowOfflineAccess = true, + }); + } + + if (!context.Clients.Any(x => x.ClientId == "spa-client")) + { + clients.Add(new Client + { + ClientId = "spa-client", + ClientName = "SPA Client", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + RedirectUris = + { + "http://localhost:4200/assets/oidc-login-redirect.html", + }, + PostLogoutRedirectUris = + { + "http://localhost:4200/?postLogout=true", + }, + AllowedCorsOrigins = + { + "http://localhost:4200", + }, + AllowedScopes = + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "ClassifiedAds.WebAPI", + }, + ClientSecrets = + { + new Secret("secret".Sha256()), + }, + AllowOfflineAccess = true, + }); + } + if (clients.Any()) + { + context.Clients.AddRange(clients.Select(x => x.ToEntity())); context.SaveChanges(); } From e69550db513afe0de5ed596637b9749e8008a81e Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sun, 1 Mar 2020 21:39:01 +0700 Subject: [PATCH 41/46] Adding Identity Server 4 Admin UI (#31) --- .../TagHelpers/SwitchTagHelper.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/TagHelpers/SwitchTagHelper.cs diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/TagHelpers/SwitchTagHelper.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/TagHelpers/SwitchTagHelper.cs new file mode 100644 index 000000000..e1dc65e2b --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/TagHelpers/SwitchTagHelper.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace ClassifiedAds.IdentityServer.TagHelpers +{ + [HtmlTargetElement("toggle-button")] + public class SwitchTagHelper : TagHelper + { + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + var childContent = await output.GetChildContentAsync(); + + var divSlider = new TagBuilder("div"); + divSlider.AddCssClass("slider round"); + + output.TagName = "label"; + output.Attributes.Add("class", "switch"); + output.Content.AppendHtml(childContent); + output.Content.AppendHtml(divSlider); + output.TagMode = TagMode.StartTagAndEndTag; + } + } +} From cb5b22d0cbdee87e782aa7bcfe598507962e36f2 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sat, 7 Mar 2020 21:26:35 +0700 Subject: [PATCH 42/46] Adding Identity Server 4 Admin UI (#31) --- .../ClassifiedAds.Domain/Entities/Role.cs | 3 + .../Entities/RoleClaim.cs | 12 ++ .../ClassifiedAds.Domain/Entities/User.cs | 2 + .../Entities/UserClaim.cs | 12 ++ .../Controllers/RoleController.cs | 127 ++++++++++++- .../Controllers/UserController.cs | 125 ++++++++++++- .../Models/RoleModels/ClaimModel.cs | 24 +++ .../Models/RoleModels/ClaimsModel.cs | 20 ++ .../Models/RoleModels/RoleModel.cs | 11 ++ .../Models/UserModels/ChangePasswordModel.cs | 25 +++ .../Models/UserModels/ClaimModel.cs | 24 +++ .../Models/UserModels/ClaimsModel.cs | 20 ++ .../Models/UserModels/UserModel.cs | 11 ++ .../Views/Client/Index.cshtml | 2 +- .../Views/Role/Claims.cshtml | 127 +++++++++++++ .../Views/Role/Delete.cshtml | 46 +++++ .../Views/Role/DeleteClaim.cshtml | 62 +++++++ .../Views/Role/Edit.cshtml | 63 +++++++ .../Views/Role/Index.cshtml | 52 +++++- .../Views/Role/_Label.cshtml | 4 + .../Views/User/ChangePassword.cshtml | 74 ++++++++ .../Views/User/Claims.cshtml | 127 +++++++++++++ .../Views/User/Delete.cshtml | 46 +++++ .../Views/User/DeleteClaim.cshtml | 62 +++++++ .../Views/User/Index.cshtml | 58 +++++- .../Views/User/Profile.cshtml | 175 ++++++++++++++++++ .../Views/User/_Label.cshtml | 4 + .../RoleClaimConfiguration.cs | 15 ++ .../RoleConfiguration.cs | 3 + .../UserClaimConfiguration.cs | 15 ++ .../UserConfiguration.cs | 3 + 31 files changed, 1341 insertions(+), 13 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/RoleClaim.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserClaim.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleClaimConfiguration.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserClaimConfiguration.cs diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/Role.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/Role.cs index f84bcc726..f0d5c35d2 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/Role.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/Role.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace ClassifiedAds.Domain.Entities { @@ -9,5 +10,7 @@ public class Role : AggregateRoot public virtual string NormalizedName { get; set; } public virtual string ConcurrencyStamp { get; set; } + + public IList Claims { get; set; } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/RoleClaim.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/RoleClaim.cs new file mode 100644 index 000000000..aff2cb420 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/RoleClaim.cs @@ -0,0 +1,12 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class RoleClaim : Entity + { + public string Type { get; set; } + public string Value { get; set; } + + public Role Role { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/User.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/User.cs index 2a9618a5e..07647b27b 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/User.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/User.cs @@ -34,5 +34,7 @@ public class User : AggregateRoot public int AccessFailedCount { get; set; } public IList Tokens { get; set; } + + public IList Claims { get; set; } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserClaim.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserClaim.cs new file mode 100644 index 000000000..86f595b87 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserClaim.cs @@ -0,0 +1,12 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class UserClaim : Entity + { + public string Type { get; set; } + public string Value { get; set; } + + public User User { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/RoleController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/RoleController.cs index ecf901946..50f06eb99 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/RoleController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/RoleController.cs @@ -2,15 +2,140 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.IdentityServer.Models.RoleModels; +using ClassifiedAds.Persistence; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace ClassifiedAds.IdentityServer.Controllers { public class RoleController : Controller { + private readonly AdsDbContext _dbContext; + + public RoleController(AdsDbContext dbContext) + { + _dbContext = dbContext; + } + public IActionResult Index() { - return View(); + var roles = _dbContext.Set().AsNoTracking().ToList(); + return View(roles); + } + + public IActionResult Edit(Guid id) + { + Role role; + if (id == Guid.Empty) + { + role = new Role(); + } + else + { + role = _dbContext.Set().FirstOrDefault(x => x.Id == id); + } + + var model = role; + + return View(model); + } + + [HttpPost] + public IActionResult Edit(Role model) + { + Role role; + + if (model.Id == Guid.Empty) + { + role = new Role + { + Name = model.Name, + NormalizedName = model.Name.ToUpper(), + }; + + _dbContext.Set().Add(role); + } + else + { + role = _dbContext.Set().FirstOrDefault(x => x.Id == model.Id); + role.Name = model.Name; + role.NormalizedName = model.Name.ToUpper(); + } + + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Edit), new { role.Id }); + } + + public IActionResult Delete(Guid id) + { + var role = _dbContext.Set().FirstOrDefault(x => x.Id == id); + return View(role); + } + + [HttpPost] + public IActionResult Delete(Role model) + { + var role = _dbContext.Set().FirstOrDefault(x => x.Id == model.Id); + _dbContext.Set().Remove(role); + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Index)); + } + + public IActionResult Claims(Guid id) + { + var role = _dbContext.Set() + .Include(x => x.Claims) + .AsNoTracking() + .FirstOrDefault(x => x.Id == id); + + return View(ClaimsModel.FromEntity(role)); + } + + [HttpPost] + public IActionResult Claims(ClaimModel model) + { + var role = _dbContext.Set() + .Include(x => x.Claims) + .FirstOrDefault(x => x.Id == model.Role.Id); + + role.Claims.Add(new RoleClaim + { + Type = model.Type, + Value = model.Value, + }); + + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Claims), new { id = role.Id }); + } + + public IActionResult DeleteClaim(Guid id) + { + var claim = _dbContext.Set() + .Include(x => x.Role) + .FirstOrDefault(x => x.Id == id); + + return View(ClaimModel.FromEntity(claim)); + } + + [HttpPost] + public IActionResult DeleteClaim(ClaimModel model) + { + var role = _dbContext.Set() + .Include(x => x.Claims) + .FirstOrDefault(x => x.Id == model.Role.Id); + + var claim = role.Claims.FirstOrDefault(x => x.Id == model.Id); + + role.Claims.Remove(claim); + + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Claims), new { id = role.Id }); } } } \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/UserController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/UserController.cs index 2fe8fee62..6cb39832d 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/UserController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/UserController.cs @@ -2,15 +2,138 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.IdentityServer.Models.UserModels; +using ClassifiedAds.Persistence; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace ClassifiedAds.IdentityServer.Controllers { public class UserController : Controller { + private readonly AdsDbContext _dbContext; + private readonly UserManager _userManager; + + public UserController(AdsDbContext dbContext, UserManager userManager) + { + _dbContext = dbContext; + _userManager = userManager; + } + public IActionResult Index() { - return View(); + var users = _dbContext.Set().ToList(); + return View(users); + } + + public IActionResult Profile(Guid id) + { + var user = id != Guid.Empty + ? _dbContext.Set().FirstOrDefault(x => x.Id == id) + : new User(); + return View(user); + } + + public IActionResult ChangePassword(Guid id) + { + var user = _dbContext.Set().FirstOrDefault(x => x.Id == id); + return View(ChangePasswordModel.FromEntity(user)); + } + + public IActionResult Delete(Guid id) + { + var user = _dbContext.Set().FirstOrDefault(x => x.Id == id); + return View(user); + } + + [HttpPost] + public IActionResult Delete(User model) + { + var user = _dbContext.Set().FirstOrDefault(x => x.Id == model.Id); + _dbContext.Set().Remove(user); + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Index)); + } + + [HttpPost] + public async Task ChangePassword(ChangePasswordModel model) + { + if (!ModelState.IsValid) + { + return View(model); + } + + var user = await _userManager.FindByIdAsync(model.Id.ToString()); + var token = await _userManager.GeneratePasswordResetTokenAsync(user); + var rs = await _userManager.ResetPasswordAsync(user, token, model.ConfirmPassword); + + if (rs.Succeeded) + { + return RedirectToAction(nameof(Profile), new { model.Id }); + } + + foreach (var error in rs.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + + return View(ChangePasswordModel.FromEntity(user)); + } + + public IActionResult Claims(Guid id) + { + var user = _dbContext.Set() + .Include(x => x.Claims) + .AsNoTracking() + .FirstOrDefault(x => x.Id == id); + + return View(ClaimsModel.FromEntity(user)); + } + + [HttpPost] + public IActionResult Claims(ClaimModel model) + { + var user = _dbContext.Set() + .Include(x => x.Claims) + .FirstOrDefault(x => x.Id == model.User.Id); + + user.Claims.Add(new UserClaim + { + Type = model.Type, + Value = model.Value, + }); + + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Claims), new { id = user.Id }); + } + + public IActionResult DeleteClaim(Guid id) + { + var claim = _dbContext.Set() + .Include(x => x.User) + .FirstOrDefault(x => x.Id == id); + + return View(ClaimModel.FromEntity(claim)); + } + + [HttpPost] + public IActionResult DeleteClaim(ClaimModel model) + { + var user = _dbContext.Set() + .Include(x => x.Claims) + .FirstOrDefault(x => x.Id == model.User.Id); + + var claim = user.Claims.FirstOrDefault(x => x.Id == model.Id); + + user.Claims.Remove(claim); + + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Claims), new { id = user.Id }); } } } \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs new file mode 100644 index 000000000..bcbc570d5 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs @@ -0,0 +1,24 @@ +using ClassifiedAds.Domain.Entities; +using System; + +namespace ClassifiedAds.IdentityServer.Models.RoleModels +{ + public class ClaimModel + { + public Guid Id { get; set; } + public string Type { get; set; } + public string Value { get; set; } + public Role Role { get; set; } + + public static ClaimModel FromEntity(RoleClaim claim) + { + return new ClaimModel + { + Id = claim.Id, + Type = claim.Type, + Value = claim.Value, + Role = claim.Role, + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs new file mode 100644 index 000000000..2aef9b5d3 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs @@ -0,0 +1,20 @@ +using ClassifiedAds.Domain.Entities; +using System.Collections.Generic; +using System.Linq; + +namespace ClassifiedAds.IdentityServer.Models.RoleModels +{ + public class ClaimsModel : ClaimModel + { + public List Claims { get; set; } + + public static ClaimsModel FromEntity(Role role) + { + return new ClaimsModel + { + Role = role, + Claims = role.Claims?.Select(x => FromEntity(x))?.ToList(), + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs new file mode 100644 index 000000000..4ac80438e --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ClassifiedAds.IdentityServer.Models.RoleModels +{ + public class RoleModel + { + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs new file mode 100644 index 000000000..f68f14df4 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs @@ -0,0 +1,25 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.ComponentModel.DataAnnotations; + +namespace ClassifiedAds.IdentityServer.Models.UserModels +{ + public class ChangePasswordModel + { + public Guid Id { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + + [Compare(nameof(Password))] + public string ConfirmPassword { get; set; } + + public static ChangePasswordModel FromEntity(User user) + { + return new ChangePasswordModel + { + Id = user.Id, + UserName = user.UserName, + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs new file mode 100644 index 000000000..811594c6b --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs @@ -0,0 +1,24 @@ +using ClassifiedAds.Domain.Entities; +using System; + +namespace ClassifiedAds.IdentityServer.Models.UserModels +{ + public class ClaimModel + { + public Guid Id { get; set; } + public string Type { get; set; } + public string Value { get; set; } + public User User { get; set; } + + public static ClaimModel FromEntity(UserClaim claim) + { + return new ClaimModel + { + Id = claim.Id, + Type = claim.Type, + Value = claim.Value, + User = claim.User, + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs new file mode 100644 index 000000000..d4e5e8e66 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs @@ -0,0 +1,20 @@ +using ClassifiedAds.Domain.Entities; +using System.Collections.Generic; +using System.Linq; + +namespace ClassifiedAds.IdentityServer.Models.UserModels +{ + public class ClaimsModel : ClaimModel + { + public List Claims { get; set; } + + public static ClaimsModel FromEntity(User user) + { + return new ClaimsModel + { + User = user, + Claims = user.Claims?.Select(x => FromEntity(x))?.ToList(), + }; + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs new file mode 100644 index 000000000..4bd3ba2cf --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ClassifiedAds.IdentityServer.Models.UserModels +{ + public class UserModel + { + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml index 85806dd20..21e388224 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml @@ -7,7 +7,7 @@
diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml new file mode 100644 index 000000000..8332e70f0 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml @@ -0,0 +1,127 @@ +@using ClassifiedAds.IdentityServer.Models.RoleModels +@model ClaimsModel + +
+
+ +
+ +
+ +
+

Claims

+ +
+ + + + +
+
Add Claim
+
+ + +
+ +
+ + + + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+
+
+
+
+
+ +
+
+
+
Claims
+
+ +
+ + + + + + + + + + + @foreach (var claim in Model.Claims) + { + + + + + + } + +
TypeValue
@claim.Type@claim.ValueRemove
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "RoleClaims", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ +
+
+
+
+
+
+ +@section scripts + { + +} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml new file mode 100644 index 000000000..b66c2d97c --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml @@ -0,0 +1,46 @@ +@using ClassifiedAds.Domain.Entities; +@model Role + +
+
+ +
+
+ +
+ +

Delete

+ + + +
+
Delete
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml new file mode 100644 index 000000000..d8a441369 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml @@ -0,0 +1,62 @@ +@using ClassifiedAds.IdentityServer.Models.RoleModels +@model ClaimModel + +
+
+ +
+ +
+ +
+

Delete Claim

+ + + + + + +
+
Delete Claim
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml new file mode 100644 index 000000000..9ca423b25 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml @@ -0,0 +1,63 @@ +@using ClassifiedAds.Domain.Entities; +@model Role + +
+
+ +
+ +
+

Role

+
+
+ + +
+ +
+ + @if (Model.Id != Guid.Empty) + { + +
+ +
+ } + + + +
+
Role
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml index 15ac2f2d0..c1a09d20d 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml @@ -1,7 +1,49 @@ - -@{ - ViewData["Title"] = "Index"; -} +@using ClassifiedAds.Domain.Entities; +@model IEnumerable -

Index

+

Roles

+ Add Role + +
+
+ @*@await Html.PartialAsync("Common/Search", new Search { Action = "Search", Controller = "Role" })*@ +
+
+ +
+
+
+ + + + + + + + + + @foreach (var role in Model) + { + + + + + + } + +
Name
+ Edit + Users + @role.Name + +
+
+
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "Roles", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ +
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml new file mode 100644 index 000000000..dcfaabab2 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml @@ -0,0 +1,4 @@ +@model string + +@Model + \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml new file mode 100644 index 000000000..f272848fd --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml @@ -0,0 +1,74 @@ +@using ClassifiedAds.IdentityServer.Models.UserModels +@model ChangePasswordModel + +
+
+ +
+ +
+ +
+

Change Password

+ +
+ + + + +
+
Change Password
+
+ + +
+ +
+ +

@Model.UserName

+
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml new file mode 100644 index 000000000..8e30ba8ea --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml @@ -0,0 +1,127 @@ +@using ClassifiedAds.IdentityServer.Models.UserModels +@model ClaimsModel + +
+
+ +
+ +
+ +
+

Claims

+ +
+ + + + +
+
Add Claim
+
+ + +
+ +
+ + + + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+
+
+
+
+
+ +
+
+
+
Claims
+
+ +
+ + + + + + + + + + + @foreach (var claim in Model.Claims) + { + + + + + + } + +
TypeValue
@claim.Type@claim.ValueRemove
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "UserClaims", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ +
+
+
+
+
+
+ +@section scripts + { + +} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml new file mode 100644 index 000000000..a752d8936 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml @@ -0,0 +1,46 @@ +@using ClassifiedAds.Domain.Entities; +@model User + +
+
+ +
+
+ +
+ +

Delete

+ + + +
+
Delete
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml new file mode 100644 index 000000000..1d29a90fa --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml @@ -0,0 +1,62 @@ +@using ClassifiedAds.IdentityServer.Models.UserModels +@model ClaimModel + +
+
+ +
+ +
+ +
+

Delete Claim

+ + + + + + +
+
Delete Claim
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Index.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Index.cshtml index 15ac2f2d0..026b062fd 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Index.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Index.cshtml @@ -1,7 +1,55 @@ - -@{ - ViewData["Title"] = "Index"; -} +@using ClassifiedAds.Domain.Entities; +@model IEnumerable -

Index

+ Add User +
+
+ @*@await Html.PartialAsync("Common/Search", new Search() { Action = "Users", Controller = "Identity" })*@ +
+
+ +
+
+
+ + + + + + + + + + + + + + @foreach (var user in Model) + { + + + + + + + + + } + +
User IdUser NameEmail
+ Edit + + + @user.Id@user.UserName@user.Email + +
+
+
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "Users", PageSize = Model.PageSize, TotalCount = Model.TotalCount, EnableSearch = true, Search = ViewBag.Search })*@ +
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml new file mode 100644 index 000000000..02bcc187a --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml @@ -0,0 +1,175 @@ +@using ClassifiedAds.Domain.Entities; +@model User + +
+
+ +
+ +
+

User Profile

+
+
+ +
+ +
+ + @if (Model.Id != Guid.Empty) + { + + + } + + + + +
+
Profile
+
+ +
+
+ +
+
+ +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+
+ +
+
+
+
+
+
+ + +
+ +
+ +
+
+
+
+
+
+
+ +@section scripts + { + +} \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml new file mode 100644 index 000000000..dcfaabab2 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml @@ -0,0 +1,4 @@ +@model string + +@Model + \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleClaimConfiguration.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleClaimConfiguration.cs new file mode 100644 index 000000000..b59e5456a --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleClaimConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class RoleClaimConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("RoleClaims"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs index 6a51ae977..1b7e37e5a 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs @@ -10,6 +10,9 @@ public void Configure(EntityTypeBuilder builder) { builder.ToTable("Roles"); builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + builder.HasMany(x => x.Claims) + .WithOne(x => x.Role) + .OnDelete(DeleteBehavior.Cascade); } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserClaimConfiguration.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserClaimConfiguration.cs new file mode 100644 index 000000000..e54c53c82 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserClaimConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class UserClaimConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("UserClaims"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs index 406afc6e5..659e79b5e 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs @@ -12,6 +12,9 @@ public void Configure(EntityTypeBuilder builder) { builder.ToTable("Users"); builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + builder.HasMany(x => x.Claims) + .WithOne(x => x.User) + .OnDelete(DeleteBehavior.Cascade); // Seed builder.HasData(new List From 3b3cff9971caffb4e21da35803c0dff079447d7f Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sun, 8 Mar 2020 11:02:03 +0700 Subject: [PATCH 43/46] Adding Identity Server 4 Admin UI (#31) --- .../ClassifiedAds.Domain/Entities/Role.cs | 2 + .../ClassifiedAds.Domain/Entities/User.cs | 2 + .../ClassifiedAds.Domain/Entities/UserRole.cs | 15 +++ .../Entities/UserToken.cs | 2 +- .../Repositories/IUserRepository.cs | 11 ++ .../Controllers/RoleController.cs | 16 +++ .../Controllers/UserController.cs | 100 +++++++++++++++++- .../Models/RoleModels/RoleModel.cs | 11 +- .../Models/RoleModels/UsersModel.cs | 13 +++ .../Models/UserModels/RoleModel.cs | 16 +++ .../Models/UserModels/RolesModel.cs | 16 +++ .../Views/Role/Users.cshtml | 78 ++++++++++++++ .../Views/User/DeleteRole.cshtml | 55 ++++++++++ .../Views/User/Roles.cshtml | 88 +++++++++++++++ .../Identity/UserStore.cs | 40 +++---- .../RoleConfiguration.cs | 5 + .../UserConfiguration.cs | 5 + .../UserRoleConfiguration.cs | 15 +++ .../PersistenceExtensions.cs | 1 + .../Repositories/UserRepository.cs | 21 ++++ 20 files changed, 484 insertions(+), 28 deletions(-) create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserRole.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Domain/Repositories/IUserRepository.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserRoleConfiguration.cs create mode 100644 src/ClassifiedAds.Projects/ClassifiedAds.Persistence/Repositories/UserRepository.cs diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/Role.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/Role.cs index f0d5c35d2..ff22340a1 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/Role.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/Role.cs @@ -12,5 +12,7 @@ public class Role : AggregateRoot public virtual string ConcurrencyStamp { get; set; } public IList Claims { get; set; } + + public IList UserRoles { get; set; } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/User.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/User.cs index 07647b27b..717e1f4b1 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/User.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/User.cs @@ -36,5 +36,7 @@ public class User : AggregateRoot public IList Tokens { get; set; } public IList Claims { get; set; } + + public IList UserRoles { get; set; } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserRole.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserRole.cs new file mode 100644 index 000000000..fb810607b --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserRole.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class UserRole : Entity + { + public Guid UserId { get; set; } + + public Guid RoleId { get; set; } + + public User User { get; set; } + + public Role Role { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserToken.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserToken.cs index 366719bc5..9f2337439 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserToken.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Entities/UserToken.cs @@ -2,7 +2,7 @@ namespace ClassifiedAds.Domain.Entities { - public class UserToken : AggregateRoot + public class UserToken : Entity { public Guid UserId { get; set; } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Repositories/IUserRepository.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Repositories/IUserRepository.cs new file mode 100644 index 000000000..747da32f0 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Domain/Repositories/IUserRepository.cs @@ -0,0 +1,11 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Linq; + +namespace ClassifiedAds.Domain.Repositories +{ + public interface IUserRepository : IRepository + { + IQueryable GetAllIncludeTokens(); + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/RoleController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/RoleController.cs index 50f06eb99..15ae21ce5 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/RoleController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/RoleController.cs @@ -137,5 +137,21 @@ public IActionResult DeleteClaim(ClaimModel model) return RedirectToAction(nameof(Claims), new { id = role.Id }); } + + public IActionResult Users(Guid id) + { + var role = _dbContext.Set() + .Include("UserRoles.User") + .FirstOrDefault(x => x.Id == id); + + var users = role.UserRoles.Select(x => x.User).ToList(); + var model = new UsersModel + { + Role = role, + Users = users, + }; + + return View(model); + } } } \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/UserController.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/UserController.cs index 6cb39832d..7bc691c27 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/UserController.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Controllers/UserController.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ClassifiedAds.Domain.Entities; @@ -36,6 +35,38 @@ public IActionResult Profile(Guid id) return View(user); } + [HttpPost] + public async Task Profile(User model) + { + User user; + if (model.Id != Guid.Empty) + { + user = _dbContext.Set().FirstOrDefault(x => x.Id == model.Id); + } + else + { + user = new User(); + } + + user.UserName = model.UserName; + user.NormalizedUserName = model.UserName.ToUpper(); + user.Email = model.Email; + user.NormalizedEmail = model.Email.ToUpper(); + user.EmailConfirmed = model.EmailConfirmed; + user.PhoneNumber = model.PhoneNumber; + user.PhoneNumberConfirmed = model.PhoneNumberConfirmed; + user.TwoFactorEnabled = model.TwoFactorEnabled; + user.LockoutEnabled = model.LockoutEnabled; + user.LockoutEnd = model.LockoutEnd; + user.AccessFailedCount = model.AccessFailedCount; + + _ = model.Id != Guid.Empty + ? await _userManager.UpdateAsync(user) + : await _userManager.CreateAsync(user); + + return RedirectToAction(nameof(Profile), new { user.Id }); + } + public IActionResult ChangePassword(Guid id) { var user = _dbContext.Set().FirstOrDefault(x => x.Id == id); @@ -135,5 +166,72 @@ public IActionResult DeleteClaim(ClaimModel model) return RedirectToAction(nameof(Claims), new { id = user.Id }); } + + public IActionResult Roles(Guid id) + { + var user = _dbContext.Set() + .Include("UserRoles.Role") + .Where(x => x.Id == id) + .AsNoTracking() + .FirstOrDefault(); + + var roles = _dbContext.Set().AsNoTracking().ToList(); + + var model = new RolesModel + { + User = user, + UserRoles = user.UserRoles.Select(x => new RoleModel { Role = x.Role, RoleId = x.RoleId }).ToList(), + Roles = roles.Where(x => !user.UserRoles.Any(y => y.RoleId == x.Id)).ToList(), + }; + + return View(model); + } + + [HttpPost] + public IActionResult Roles(RolesModel model) + { + var user = _dbContext.Set() + .Include(x => x.UserRoles) + .Where(x => x.Id == model.User.Id) + .FirstOrDefault(); + + user.UserRoles.Add(new UserRole + { + RoleId = model.Role.RoleId, + }); + + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Roles), new { model.User.Id }); + } + + public IActionResult DeleteRole(Guid id, Guid roleId) + { + var user = _dbContext.Set() + .Include("UserRoles.Role") + .Where(x => x.Id == id) + .FirstOrDefault(); + var role = user.UserRoles.FirstOrDefault(x => x.RoleId == roleId); + var model = new RoleModel { User = user, Role = role.Role }; + + return View(model); + } + + [HttpPost] + public IActionResult DeleteRole(RoleModel model) + { + var user = _dbContext.Set() + .Include(x => x.UserRoles) + .Where(x => x.Id == model.User.Id) + .FirstOrDefault(); + + var role = user.UserRoles.FirstOrDefault(x => x.RoleId == model.Role.Id); + + user.UserRoles.Remove(role); + + _dbContext.SaveChanges(); + + return RedirectToAction(nameof(Roles), new { model.User.Id }); + } } } \ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs index 4ac80438e..5eb2e3fbb 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs @@ -1,4 +1,5 @@ -using System; +using ClassifiedAds.Domain.Entities; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -7,5 +8,13 @@ namespace ClassifiedAds.IdentityServer.Models.RoleModels { public class RoleModel { + public Guid Id { get; set; } + + public string Name { get; set; } + + public static RoleModel FromEntity(Role role) + { + return new RoleModel { Id = role.Id, Name = role.Name }; + } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs new file mode 100644 index 000000000..bc70311a2 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs @@ -0,0 +1,13 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.IdentityServer.Models.RoleModels +{ + public class UsersModel + { + public Role Role { get; set; } + + public List Users { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs new file mode 100644 index 000000000..3036e8eee --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.Domain.Entities; +using System; + +namespace ClassifiedAds.IdentityServer.Models.UserModels +{ + public class RoleModel + { + public Guid RoleId { get; set; } + + public Guid UserId { get; set; } + + public User User { get; set; } + + public Role Role { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs new file mode 100644 index 000000000..afb9c716c --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.Domain.Entities; +using System.Collections.Generic; + +namespace ClassifiedAds.IdentityServer.Models.UserModels +{ + public class RolesModel + { + public User User { get; set; } + + public RoleModel Role { get; set; } + + public List Roles { get; set; } + + public List UserRoles { get; set; } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml new file mode 100644 index 000000000..8dda9c830 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml @@ -0,0 +1,78 @@ +@using ClassifiedAds.IdentityServer.Models.RoleModels +@model UsersModel + +
+
+ +
+ +
+

Users (@Model.Role.Name)

+
+
+ +@*
+
+
+
+
+ + +
+
+ +
+
+ +
+
+
+
+
*@ + +
+
+
+ + + + + + + + + + + + + @foreach (var user in Model.Users) + { + + + + + + + + } + +
User IdUser NameEmail
+ + @user.Id@user.UserName@user.Email + +
+
+
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "RoleUsers", PageSize = Model.PageSize, TotalCount = Model.TotalCount, EnableSearch = true, Search = ViewBag.Search })*@ +
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml new file mode 100644 index 000000000..d00e0ff3c --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml @@ -0,0 +1,55 @@ +@using ClassifiedAds.IdentityServer.Models.UserModels +@model RoleModel + +
+
+ +
+ +
+ + +
+ +

Delete Role

+ + + + + + + +
+
Delete Role
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml new file mode 100644 index 000000000..9c84004cd --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml @@ -0,0 +1,88 @@ +@using ClassifiedAds.IdentityServer.Models.UserModels +@model RolesModel + +
+
+ +
+ +
+ +
+

Roles

+ +
+ + + + +
+
Roles
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+
Roles
+
+ +
+ + + + + + + + + @foreach (var role in Model.UserRoles) + { + + + + + } + +
Name
@role.Role.NameRemove
+
+ +
+
+ @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "UserRoles", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ +
+
+
+
+
+
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/Identity/UserStore.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/Identity/UserStore.cs index 362da0d37..3209e302b 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/Identity/UserStore.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Infrastructure/Identity/UserStore.cs @@ -21,14 +21,12 @@ public class UserStore : IUserStore, IUserTwoFactorRecoveryCodeStore { private readonly IUnitOfWork _unitOfWork; - private readonly IRepository _userRepository; - private readonly IRepository _userTokenRepository; + private readonly IUserRepository _userRepository; - public UserStore(IUnitOfWork unitOfWork, IRepository userRepository, IRepository userTokenRepository) + public UserStore(IUnitOfWork unitOfWork, IUserRepository userRepository) { _unitOfWork = unitOfWork; _userRepository = userRepository; - _userTokenRepository = userTokenRepository; } public void Dispose() @@ -51,17 +49,17 @@ public Task DeleteAsync(User user, CancellationToken cancellatio public Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken) { - return Task.FromResult(_userRepository.GetAll().FirstOrDefault(x => x.NormalizedEmail == normalizedEmail)); + return Task.FromResult(_userRepository.GetAllIncludeTokens().FirstOrDefault(x => x.NormalizedEmail == normalizedEmail)); } public Task FindByIdAsync(string userId, CancellationToken cancellationToken) { - return Task.FromResult(_userRepository.GetAll().FirstOrDefault(x => x.Id == Guid.Parse(userId))); + return Task.FromResult(_userRepository.GetAllIncludeTokens().FirstOrDefault(x => x.Id == Guid.Parse(userId))); } public Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken) { - return Task.FromResult(_userRepository.GetAll().FirstOrDefault(x => x.NormalizedUserName == normalizedUserName)); + return Task.FromResult(_userRepository.GetAllIncludeTokens().FirstOrDefault(x => x.NormalizedUserName == normalizedUserName)); } public Task GetAccessFailedCountAsync(User user, CancellationToken cancellationToken) @@ -235,33 +233,27 @@ public Task UpdateAsync(User user, CancellationToken cancellatio public Task GetTokenAsync(User user, string loginProvider, string name, CancellationToken cancellationToken) { - var tokenEntity = - _userTokenRepository.GetAll().SingleOrDefault( - l => - l.TokenName == name && l.LoginProvider == loginProvider && - l.UserId == user.Id); + var tokenEntity = user.Tokens.SingleOrDefault( + l => l.TokenName == name && l.LoginProvider == loginProvider); return Task.FromResult(tokenEntity?.TokenValue); } public Task SetTokenAsync(User user, string loginProvider, string name, string value, CancellationToken cancellationToken) { - var tokenEntity = - _userTokenRepository.GetAll().SingleOrDefault( - l => - l.TokenName == name && l.LoginProvider == loginProvider && - l.UserId == user.Id); + var tokenEntity = user.Tokens.SingleOrDefault( + l => l.TokenName == name && l.LoginProvider == loginProvider); if (tokenEntity != null) { tokenEntity.TokenValue = value; } else { - _userTokenRepository.Add(new UserToken + user.Tokens.Add(new UserToken { UserId = user.Id, LoginProvider = loginProvider, TokenName = name, - TokenValue = value + TokenValue = value, }); } @@ -272,14 +264,12 @@ public Task SetTokenAsync(User user, string loginProvider, string name, string v public Task RemoveTokenAsync(User user, string loginProvider, string name, CancellationToken cancellationToken) { - var tokenEntity = - _userTokenRepository.GetAll().SingleOrDefault( - l => - l.TokenName == name && l.LoginProvider == loginProvider && - l.UserId == user.Id); + var tokenEntity = user.Tokens.SingleOrDefault( + l => l.TokenName == name && l.LoginProvider == loginProvider); if (tokenEntity != null) { - _userTokenRepository.Delete(tokenEntity); + user.Tokens.Remove(tokenEntity); + _unitOfWork.SaveChanges(); } return Task.FromResult(0); } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs index 1b7e37e5a..57d5e6351 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs @@ -10,9 +10,14 @@ public void Configure(EntityTypeBuilder builder) { builder.ToTable("Roles"); builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + builder.HasMany(x => x.Claims) .WithOne(x => x.Role) .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(x => x.UserRoles) + .WithOne(x => x.Role) + .OnDelete(DeleteBehavior.Cascade); } } } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs index 659e79b5e..a0bac1d1b 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs @@ -12,10 +12,15 @@ public void Configure(EntityTypeBuilder builder) { builder.ToTable("Users"); builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + builder.HasMany(x => x.Claims) .WithOne(x => x.User) .OnDelete(DeleteBehavior.Cascade); + builder.HasMany(x => x.UserRoles) + .WithOne(x => x.User) + .OnDelete(DeleteBehavior.Cascade); + // Seed builder.HasData(new List { diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserRoleConfiguration.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserRoleConfiguration.cs new file mode 100644 index 000000000..d34427b65 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/MappingConfigurations/UserRoleConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class UserRoleConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("UserRoles"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/PersistenceExtensions.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/PersistenceExtensions.cs index 3a09f3cfa..2f0d9747d 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/PersistenceExtensions.cs +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/PersistenceExtensions.cs @@ -19,6 +19,7 @@ public static IServiceCollection AddPersistence(this IServiceCollection services })) .AddScoped() .AddScoped(typeof(IRepository<,>), typeof(Repository<,>)) + .AddScoped(typeof(IUserRepository), typeof(UserRepository)) .AddScoped(typeof(IStoreRepository), typeof(StoreRepository)); return services; } diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/Repositories/UserRepository.cs b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/Repositories/UserRepository.cs new file mode 100644 index 000000000..c9e65d723 --- /dev/null +++ b/src/ClassifiedAds.Projects/ClassifiedAds.Persistence/Repositories/UserRepository.cs @@ -0,0 +1,21 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + +namespace ClassifiedAds.Persistence.Repositories +{ + public class UserRepository : Repository, IUserRepository + { + public UserRepository(AdsDbContext dbContext) + : base(dbContext) + { + } + + public IQueryable GetAllIncludeTokens() + { + return GetAll().Include(x => x.Tokens); + } + } +} From 502db828e951052ae9baa016258c1d5384b39207 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Sun, 8 Mar 2020 14:36:32 +0700 Subject: [PATCH 44/46] Adding Identity Server 4 Admin UI (#31) --- .../Views/Account/ForgotPassword.cshtml | 19 ++-- .../Views/Account/Login.cshtml | 81 +++++++++----- .../Views/Account/Register.cshtml | 57 +++++----- .../Views/Account/ResetPassword.cshtml | 48 +++++---- .../Views/Consent/Index.cshtml | 100 +++++++++++------- .../Views/Home/Index.cshtml | 3 - .../Views/Shared/_ScopeListItem.cshtml | 28 +++-- 7 files changed, 199 insertions(+), 137 deletions(-) diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml index 25b693577..5377f8e64 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml @@ -1,17 +1,20 @@ @model ForgotPasswordModel +@await Html.PartialAsync("_ValidationSummary") + +

Forgot Password

+

Enter your email

+ +
-
-

Login

-
+
+
- -
- -
+ + +
-
\ No newline at end of file diff --git a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml index 5905626e4..b838d8378 100644 --- a/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml +++ b/src/ClassifiedAds.Projects/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml @@ -5,43 +5,68 @@

Login

- + @await Html.PartialAsync("_ValidationSummary")
@if (Model.EnableLocalLogin) {
-
-
-

Local Login

-
-
- +
+
Local Login
+
-
- - +
+
+
+
+ + + +
+ +
+
-
- - + +
+
+
+
+ + + +
+ +
+
+ @if (Model.AllowRememberLogin) { -
@@ -52,17 +77,15 @@ @if (Model.VisibleExternalProviders.Any()) { -
-
-
-

External Login

-
-
+
+
+
External Login
+