From a44f3144e18a1427d135f60ceae85addbdb22675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Meadows-J=C3=B6nsson?= Date: Fri, 22 Aug 2025 00:21:56 +0200 Subject: [PATCH 1/7] Lazy load diffs --- assets/css/app.css | 56 ++ assets/js/app.js | 35 +- assets/yarn.lock | 657 +++++++++--------- lib/diff/hex/hex.ex | 17 +- lib/diff/storage/gcs.ex | 85 ++- lib/diff/storage/local.ex | 75 +- lib/diff/storage/storage.ex | 42 +- lib/diff_web.ex | 4 +- lib/diff_web/controllers/page_controller.ex | 225 ------ .../live/components/diff_component.ex | 94 +++ .../live/components/too_large_component.ex | 21 + lib/diff_web/live/diff_live_view.ex | 339 +++++++++ lib/diff_web/live/search_view.ex | 65 +- lib/diff_web/router.ex | 4 +- lib/diff_web/templates/live/diff.html.leex | 55 ++ lib/diff_web/templates/render/patch.html.eex | 41 -- .../templates/render/too_large.html.eex | 13 - .../templates/search/search.html.leex | 54 -- lib/diff_web/views/live_view.ex | 52 ++ lib/diff_web/views/page_view.ex | 3 - lib/diff_web/views/render_view.ex | 51 -- lib/diff_web/views/search_view.ex | 16 - mix.exs | 6 +- mix.lock | 37 +- priv/gettext/errors.pot | 1 - test/diff/storage/local_test.exs | 88 +++ .../controllers/page_controller_test.exs | 58 -- test/diff_web/integration_test.exs | 54 ++ test/diff_web/live/diff_live_view_test.exs | 96 +++ test/diff_web/live/search_view_test.exs | 4 +- test/support/conn_case.ex | 8 +- 31 files changed, 1505 insertions(+), 851 deletions(-) delete mode 100644 lib/diff_web/controllers/page_controller.ex create mode 100644 lib/diff_web/live/components/diff_component.ex create mode 100644 lib/diff_web/live/components/too_large_component.ex create mode 100644 lib/diff_web/live/diff_live_view.ex create mode 100644 lib/diff_web/templates/live/diff.html.leex delete mode 100644 lib/diff_web/templates/render/patch.html.eex delete mode 100644 lib/diff_web/templates/render/too_large.html.eex delete mode 100644 lib/diff_web/templates/search/search.html.leex create mode 100644 lib/diff_web/views/live_view.ex delete mode 100644 lib/diff_web/views/page_view.ex delete mode 100644 lib/diff_web/views/render_view.ex delete mode 100644 lib/diff_web/views/search_view.ex create mode 100644 test/diff/storage/local_test.exs delete mode 100644 test/diff_web/controllers/page_controller_test.exs create mode 100644 test/diff_web/integration_test.exs create mode 100644 test/diff_web/live/diff_live_view_test.exs diff --git a/assets/css/app.css b/assets/css/app.css index 3ef5f34..b8ded72 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -382,6 +382,62 @@ table.package-list .button { margin-bottom: 0; } +.diff-stats-header { + margin: 15px 0; + padding: 10px 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.diff-stats-header span { + margin-right: 15px; + font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Menlo', monospace; + font-size: 14px; +} + +.diff-stats-header .files-changed { + color: #ddd; +} + +.diff-stats-header .additions { + color: #4CAF50; + font-weight: 600; +} + +.diff-stats-header .deletions { + color: #f44336; + font-weight: 600; +} + +#loading-trigger { + height: 60px; + width: 100%; + margin: 30px 0; + display: flex; + align-items: center; + justify-content: center; + color: #888; + font-size: 14px; + opacity: 0.8; +} + +#loading-trigger::before { + content: ''; + width: 20px; + height: 20px; + border: 2px solid #f3f3f3; + border-top: 2px solid #888; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-right: 10px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + + + @media only screen and (min-device-width: 320px) and (max-device-width: 480px) { diff --git a/assets/js/app.js b/assets/js/app.js index 71c339f..f01d730 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -14,7 +14,40 @@ import "phoenix_html" import { Socket } from 'phoenix' import { LiveSocket } from "phoenix_live_view" -let liveSocket = new LiveSocket("/live", Socket, {}) +// Define hooks for LiveView +window.Hooks = {} + +window.Hooks.InfiniteScroll = { + mounted() { + this.pending = false + + this.observer = new IntersectionObserver((entries) => { + const target = entries[0] + if (target.isIntersecting && !this.pending) { + this.pending = true + this.pushEvent("load-more", {}) + } + }, { + root: null, + rootMargin: '100px', + threshold: 0.1 + }) + + this.observer.observe(this.el) + }, + + destroyed() { + if (this.observer) { + this.observer.disconnect() + } + }, + + updated() { + this.pending = false + } +} + +let liveSocket = new LiveSocket("/live", Socket, { hooks: window.Hooks }) liveSocket.connect() /* diff --git a/assets/yarn.lock b/assets/yarn.lock index 5cd3e6b..d1c6a5f 100644 --- a/assets/yarn.lock +++ b/assets/yarn.lock @@ -4,7 +4,7 @@ "@ampproject/remapping@^2.2.0": version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz" integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -12,7 +12,7 @@ "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== dependencies: "@babel/helper-validator-identifier" "^7.25.9" @@ -21,12 +21,12 @@ "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0": version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz" integrity sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g== "@babel/core@^7.24.4": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== dependencies: "@ampproject/remapping" "^2.2.0" @@ -47,7 +47,7 @@ "@babel/generator@^7.26.0", "@babel/generator@^7.26.3": version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz" integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== dependencies: "@babel/parser" "^7.26.3" @@ -58,14 +58,14 @@ "@babel/helper-annotate-as-pure@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz" integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== dependencies: "@babel/types" "^7.25.9" "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz" integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== dependencies: "@babel/compat-data" "^7.25.9" @@ -76,7 +76,7 @@ "@babel/helper-create-class-features-plugin@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz" integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -89,7 +89,7 @@ "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9": version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz#5169756ecbe1d95f7866b90bb555b022595302a0" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz" integrity sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -98,7 +98,7 @@ "@babel/helper-define-polyfill-provider@^0.6.2", "@babel/helper-define-polyfill-provider@^0.6.3": version "0.6.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21" + resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz" integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg== dependencies: "@babel/helper-compilation-targets" "^7.22.6" @@ -109,7 +109,7 @@ "@babel/helper-member-expression-to-functions@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz" integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== dependencies: "@babel/traverse" "^7.25.9" @@ -117,7 +117,7 @@ "@babel/helper-module-imports@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz" integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== dependencies: "@babel/traverse" "^7.25.9" @@ -125,7 +125,7 @@ "@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz" integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== dependencies: "@babel/helper-module-imports" "^7.25.9" @@ -134,7 +134,7 @@ "@babel/helper-optimise-call-expression@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz" integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== dependencies: "@babel/types" "^7.25.9" @@ -146,12 +146,12 @@ "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz" integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== "@babel/helper-remap-async-to-generator@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" + resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz" integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -160,7 +160,7 @@ "@babel/helper-replace-supers@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz" integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ== dependencies: "@babel/helper-member-expression-to-functions" "^7.25.9" @@ -169,7 +169,7 @@ "@babel/helper-skip-transparent-expression-wrappers@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz" integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== dependencies: "@babel/traverse" "^7.25.9" @@ -177,7 +177,7 @@ "@babel/helper-string-parser@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== "@babel/helper-validator-identifier@^7.12.11": @@ -187,17 +187,17 @@ "@babel/helper-validator-identifier@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== "@babel/helper-validator-option@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz" integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== "@babel/helper-wrap-function@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" + resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz" integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== dependencies: "@babel/template" "^7.25.9" @@ -206,7 +206,7 @@ "@babel/helpers@^7.26.0": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz" integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== dependencies: "@babel/template" "^7.25.9" @@ -214,14 +214,14 @@ "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3": version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz" integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== dependencies: "@babel/types" "^7.26.3" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz" integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -229,21 +229,21 @@ "@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz" integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz" integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz" integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -252,7 +252,7 @@ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz" integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -260,26 +260,26 @@ "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== "@babel/plugin-syntax-import-assertions@^7.26.0": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz" integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-import-attributes@^7.26.0": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz" integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz" integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.18.6" @@ -287,14 +287,14 @@ "@babel/plugin-transform-arrow-functions@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz" integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-async-generator-functions@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz#1b18530b077d18a407c494eb3d1d72da505283a2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz" integrity sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -303,7 +303,7 @@ "@babel/plugin-transform-async-to-generator@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz" integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== dependencies: "@babel/helper-module-imports" "^7.25.9" @@ -312,21 +312,21 @@ "@babel/plugin-transform-block-scoped-functions@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz" integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-block-scoping@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz" integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-class-properties@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" + resolved "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz" integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== dependencies: "@babel/helper-create-class-features-plugin" "^7.25.9" @@ -334,7 +334,7 @@ "@babel/plugin-transform-class-static-block@^7.26.0": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz" integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== dependencies: "@babel/helper-create-class-features-plugin" "^7.25.9" @@ -342,7 +342,7 @@ "@babel/plugin-transform-classes@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz" integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -354,7 +354,7 @@ "@babel/plugin-transform-computed-properties@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz" integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -362,14 +362,14 @@ "@babel/plugin-transform-destructuring@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz" integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-dotall-regex@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz" integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -377,14 +377,14 @@ "@babel/plugin-transform-duplicate-keys@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz" integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz" integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -392,28 +392,28 @@ "@babel/plugin-transform-dynamic-import@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz" integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-exponentiation-operator@^7.25.9": version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" + resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz" integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-export-namespace-from@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz" integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-for-of@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz" integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -421,7 +421,7 @@ "@babel/plugin-transform-function-name@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz" integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== dependencies: "@babel/helper-compilation-targets" "^7.25.9" @@ -430,35 +430,35 @@ "@babel/plugin-transform-json-strings@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" + resolved "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz" integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-literals@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz" integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-logical-assignment-operators@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz" integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-member-expression-literals@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz" integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-modules-amd@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz" integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== dependencies: "@babel/helper-module-transforms" "^7.25.9" @@ -466,7 +466,7 @@ "@babel/plugin-transform-modules-commonjs@^7.25.9": version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz" integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== dependencies: "@babel/helper-module-transforms" "^7.26.0" @@ -474,7 +474,7 @@ "@babel/plugin-transform-modules-systemjs@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz" integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== dependencies: "@babel/helper-module-transforms" "^7.25.9" @@ -484,7 +484,7 @@ "@babel/plugin-transform-modules-umd@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz" integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== dependencies: "@babel/helper-module-transforms" "^7.25.9" @@ -492,7 +492,7 @@ "@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz" integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -500,28 +500,28 @@ "@babel/plugin-transform-new-target@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" + resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz" integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-nullish-coalescing-operator@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz#bcb1b0d9e948168102d5f7104375ca21c3266949" + resolved "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz" integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-numeric-separator@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz" integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-object-rest-spread@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz" integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== dependencies: "@babel/helper-compilation-targets" "^7.25.9" @@ -530,7 +530,7 @@ "@babel/plugin-transform-object-super@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz" integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -538,14 +538,14 @@ "@babel/plugin-transform-optional-catch-binding@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz" integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-optional-chaining@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" + resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz" integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -553,14 +553,14 @@ "@babel/plugin-transform-parameters@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz" integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-private-methods@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" + resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz" integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== dependencies: "@babel/helper-create-class-features-plugin" "^7.25.9" @@ -568,7 +568,7 @@ "@babel/plugin-transform-private-property-in-object@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" + resolved "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz" integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== dependencies: "@babel/helper-annotate-as-pure" "^7.25.9" @@ -577,14 +577,14 @@ "@babel/plugin-transform-property-literals@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz" integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-regenerator@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz" integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -592,7 +592,7 @@ "@babel/plugin-transform-regexp-modifiers@^7.26.0": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz" integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -600,21 +600,21 @@ "@babel/plugin-transform-reserved-words@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" + resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz" integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-shorthand-properties@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz" integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-spread@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz" integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" @@ -622,35 +622,35 @@ "@babel/plugin-transform-sticky-regex@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" + resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz" integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-template-literals@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz" integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-typeof-symbol@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz#224ba48a92869ddbf81f9b4a5f1204bbf5a2bc4b" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz" integrity sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-unicode-escapes@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz" integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-unicode-property-regex@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz" integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -658,7 +658,7 @@ "@babel/plugin-transform-unicode-regex@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz" integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -666,7 +666,7 @@ "@babel/plugin-transform-unicode-sets-regex@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz" integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.25.9" @@ -674,7 +674,7 @@ "@babel/preset-env@^7.24.4": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.0.tgz#30e5c6bc1bcc54865bff0c5a30f6d4ccdc7fa8b1" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz" integrity sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw== dependencies: "@babel/compat-data" "^7.26.0" @@ -749,7 +749,7 @@ "@babel/preset-modules@0.1.6-no-external-plugins": version "0.1.6-no-external-plugins" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz" integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -765,7 +765,7 @@ "@babel/template@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz" integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== dependencies: "@babel/code-frame" "^7.25.9" @@ -774,7 +774,7 @@ "@babel/traverse@^7.25.9": version "7.26.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz" integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== dependencies: "@babel/code-frame" "^7.26.2" @@ -787,7 +787,7 @@ "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3": version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz" integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== dependencies: "@babel/helper-string-parser" "^7.25.9" @@ -804,19 +804,19 @@ "@discoveryjs/json-ext@^0.5.0": version "0.5.7" - resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== "@jest/schemas@^29.6.3": version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: "@sinclair/typebox" "^0.27.8" "@jest/types@^29.6.3": version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: "@jest/schemas" "^29.6.3" @@ -828,7 +828,7 @@ "@jridgewell/gen-mapping@^0.3.5": version "0.3.8" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz" integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== dependencies: "@jridgewell/set-array" "^1.2.1" @@ -837,17 +837,17 @@ "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== "@jridgewell/set-array@^1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/source-map@^0.3.3": version "0.3.6" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz" integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -855,12 +855,12 @@ "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== "@jridgewell/trace-mapping@^0.3.21", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: "@jridgewell/resolve-uri" "^3.1.0" @@ -868,7 +868,7 @@ "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -876,12 +876,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -889,22 +889,22 @@ "@sinclair/typebox@^0.27.8": version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sindresorhus/merge-streams@^2.1.0": version "2.3.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" + resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz" integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== "@trysound/sax@0.2.0": version "0.2.0" - resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== "@types/eslint-scope@^3.7.3": version "3.7.7" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz" integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" @@ -912,7 +912,7 @@ "@types/eslint@*": version "9.6.1" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz" integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== dependencies: "@types/estree" "*" @@ -920,31 +920,31 @@ "@types/estree@*", "@types/estree@^1.0.5": version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz" integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz" integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/node@*": @@ -954,19 +954,19 @@ "@types/yargs-parser@*": version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.8": version "17.0.33" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz" integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz" integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== dependencies: "@webassemblyjs/helper-numbers" "1.13.2" @@ -974,22 +974,22 @@ "@webassemblyjs/floating-point-hex-parser@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz" integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== "@webassemblyjs/helper-api-error@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz" integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== "@webassemblyjs/helper-buffer@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz" integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== "@webassemblyjs/helper-numbers@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz" integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== dependencies: "@webassemblyjs/floating-point-hex-parser" "1.13.2" @@ -998,12 +998,12 @@ "@webassemblyjs/helper-wasm-bytecode@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz" integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== "@webassemblyjs/helper-wasm-section@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz" integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -1013,26 +1013,26 @@ "@webassemblyjs/ieee754@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz" integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== dependencies: "@xtuc/ieee754" "^1.2.0" "@webassemblyjs/leb128@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz" integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== dependencies: "@xtuc/long" "4.2.2" "@webassemblyjs/utf8@1.13.2": version "1.13.2" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz" integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== "@webassemblyjs/wasm-edit@^1.12.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz" integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -1046,7 +1046,7 @@ "@webassemblyjs/wasm-gen@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz" integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -1057,7 +1057,7 @@ "@webassemblyjs/wasm-opt@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz" integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -1067,7 +1067,7 @@ "@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.12.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz" integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -1079,7 +1079,7 @@ "@webassemblyjs/wast-printer@1.14.1": version "1.14.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz" integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== dependencies: "@webassemblyjs/ast" "1.14.1" @@ -1087,42 +1087,42 @@ "@webpack-cli/configtest@^2.1.1": version "2.1.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz" integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== "@webpack-cli/info@^2.0.2": version "2.0.2" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz" integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== "@webpack-cli/serve@^2.0.5": version "2.0.5" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== "@xtuc/long@4.2.2": version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== acorn-import-assertions@^1.9.0: version "1.9.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz" integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== acorn@^8.7.1, acorn@^8.8.2: version "8.14.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== ajv-formats@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== dependencies: ajv "^8.0.0" @@ -1134,14 +1134,14 @@ ajv-keywords@^3.5.2: ajv-keywords@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== dependencies: fast-deep-equal "^3.1.3" ajv@^6.12.5: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -1151,7 +1151,7 @@ ajv@^6.12.5: ajv@^8.0.0, ajv@^8.9.0: version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: fast-deep-equal "^3.1.3" @@ -1168,7 +1168,7 @@ ansi-styles@^4.1.0: babel-loader@^9.1.3: version "9.2.1" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.2.1.tgz#04c7835db16c246dd19ba0914418f3937797587b" + resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz" integrity sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA== dependencies: find-cache-dir "^4.0.0" @@ -1176,7 +1176,7 @@ babel-loader@^9.1.3: babel-plugin-polyfill-corejs2@^0.4.10: version "0.4.12" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz" integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og== dependencies: "@babel/compat-data" "^7.22.6" @@ -1185,7 +1185,7 @@ babel-plugin-polyfill-corejs2@^0.4.10: babel-plugin-polyfill-corejs3@^0.10.6: version "0.10.6" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz" integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA== dependencies: "@babel/helper-define-polyfill-provider" "^0.6.2" @@ -1193,26 +1193,26 @@ babel-plugin-polyfill-corejs3@^0.10.6: babel-plugin-polyfill-regenerator@^0.6.1: version "0.6.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz#abeb1f3f1c762eace37587f42548b08b57789bc8" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz" integrity sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q== dependencies: "@babel/helper-define-polyfill-provider" "^0.6.3" boolbase@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== braces@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" browserslist@^4.0.0, browserslist@^4.21.10, browserslist@^4.23.0, browserslist@^4.24.0, browserslist@^4.24.2: version "4.24.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.3.tgz#5fc2725ca8fb3c1432e13dac278c7cc103e026d2" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz" integrity sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA== dependencies: caniuse-lite "^1.0.30001688" @@ -1227,7 +1227,7 @@ buffer-from@^1.0.0: caniuse-api@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== dependencies: browserslist "^4.0.0" @@ -1237,12 +1237,12 @@ caniuse-api@^3.0.0: caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001688: version "1.0.30001688" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz#f9d3ede749f083ce0db4c13db9d828adaf2e8d0a" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz" integrity sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA== chalk@^4.0.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -1250,12 +1250,12 @@ chalk@^4.0.0: chrome-trace-event@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz" integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== ci-info@^3.2.0: version "3.9.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== clone-deep@^4.0.1: @@ -1281,42 +1281,42 @@ color-name@~1.1.4: colord@^2.9.3: version "2.9.3" - resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== colorette@^2.0.14: version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== commander@^10.0.1: version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== commander@^2.20.0: version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@^7.2.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== common-path-prefix@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" + resolved "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz" integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== convert-source-map@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== copy-webpack-plugin@^12.0.2: version "12.0.2" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz#935e57b8e6183c82f95bd937df658a59f6a2da28" + resolved "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz" integrity sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA== dependencies: fast-glob "^3.3.2" @@ -1328,14 +1328,14 @@ copy-webpack-plugin@^12.0.2: core-js-compat@^3.38.0, core-js-compat@^3.38.1: version "3.39.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.39.0.tgz#b12dccb495f2601dc860bdbe7b4e3ffa8ba63f61" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz" integrity sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw== dependencies: browserslist "^4.24.2" cross-spawn@^7.0.3: version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" @@ -1344,12 +1344,12 @@ cross-spawn@^7.0.3: css-declaration-sorter@^7.2.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz#6dec1c9523bc4a643e088aab8f09e67a54961024" + resolved "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz" integrity sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow== css-loader@^7.1.1: version "7.1.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-7.1.2.tgz#64671541c6efe06b0e22e750503106bdd86880f8" + resolved "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz" integrity sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA== dependencies: icss-utils "^5.1.0" @@ -1363,7 +1363,7 @@ css-loader@^7.1.1: css-minimizer-webpack-plugin@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-6.0.0.tgz#eb79947af785467739375faf7fcb8c2dbf4f06dc" + resolved "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-6.0.0.tgz" integrity sha512-BLpR9CCDkKvhO3i0oZQgad6v9pCxUuhSc5RT6iUEy9M8hBXi4TJb5vqF2GQ2deqYHmRi3O6IR9hgAZQWg0EBwA== dependencies: "@jridgewell/trace-mapping" "^0.3.21" @@ -1375,7 +1375,7 @@ css-minimizer-webpack-plugin@^6.0.0: css-select@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + resolved "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz" integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== dependencies: boolbase "^1.0.0" @@ -1386,7 +1386,7 @@ css-select@^5.1.0: css-tree@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz" integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== dependencies: mdn-data "2.0.30" @@ -1394,7 +1394,7 @@ css-tree@^2.3.1: css-tree@~2.2.0: version "2.2.1" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz" integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== dependencies: mdn-data "2.0.28" @@ -1402,7 +1402,7 @@ css-tree@~2.2.0: css-what@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== cssesc@^3.0.0: @@ -1412,7 +1412,7 @@ cssesc@^3.0.0: cssnano-preset-default@^6.1.2: version "6.1.2" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz#adf4b89b975aa775f2750c89dbaf199bbd9da35e" + resolved "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz" integrity sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg== dependencies: browserslist "^4.23.0" @@ -1448,12 +1448,12 @@ cssnano-preset-default@^6.1.2: cssnano-utils@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-4.0.2.tgz#56f61c126cd0f11f2eef1596239d730d9fceff3c" + resolved "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz" integrity sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ== cssnano@^6.0.3: version "6.1.2" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-6.1.2.tgz#4bd19e505bd37ee7cf0dc902d3d869f6d79c66b8" + resolved "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz" integrity sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA== dependencies: cssnano-preset-default "^6.1.2" @@ -1461,7 +1461,7 @@ cssnano@^6.0.3: csso@^5.0.5: version "5.0.5" - resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + resolved "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz" integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== dependencies: css-tree "~2.2.0" @@ -1475,14 +1475,14 @@ debug@^4.1.0, debug@^4.1.1: debug@^4.3.1: version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== dependencies: ms "^2.1.3" dom-serializer@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz" integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== dependencies: domelementtype "^2.3.0" @@ -1491,19 +1491,19 @@ dom-serializer@^2.0.0: domelementtype@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz" integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== dependencies: domelementtype "^2.3.0" domutils@^3.0.1: version "3.1.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + resolved "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz" integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== dependencies: dom-serializer "^2.0.0" @@ -1512,12 +1512,12 @@ domutils@^3.0.1: electron-to-chromium@^1.5.73: version "1.5.73" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz#f32956ce40947fa3c8606726a96cd8fb5bb5f720" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz" integrity sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg== enhanced-resolve@^5.16.0: version "5.17.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz" integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" @@ -1525,27 +1525,27 @@ enhanced-resolve@^5.16.0: entities@^4.2.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== envinfo@^7.7.3: version "7.14.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz" integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== es-module-lexer@^1.2.1: version "1.5.4" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz" integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== escalade@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== eslint-scope@5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -1553,7 +1553,7 @@ eslint-scope@5.1.1: esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" @@ -1575,17 +1575,17 @@ esutils@^2.0.2: events@^3.2.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -1601,31 +1601,31 @@ fast-json-stable-stringify@^2.0.0: fast-uri@^3.0.1: version "3.0.3" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz" integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== fastest-levenshtein@^1.0.12: version "1.0.16" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: version "1.17.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz" integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== dependencies: reusify "^1.0.4" fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" find-cache-dir@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz" integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== dependencies: common-path-prefix "^3.0.0" @@ -1641,7 +1641,7 @@ find-up@^4.0.0: find-up@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" + resolved "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz" integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== dependencies: locate-path "^7.1.0" @@ -1649,36 +1649,36 @@ find-up@^6.3.0: flat@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== glob-parent@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-parent@^6.0.1: version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob-to-regexp@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== globals@^11.1.0: @@ -1688,7 +1688,7 @@ globals@^11.1.0: globby@^14.0.0: version "14.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.2.tgz#06554a54ccfe9264e5a9ff8eded46aa1e306482f" + resolved "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz" integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== dependencies: "@sindresorhus/merge-streams" "^2.1.0" @@ -1705,7 +1705,7 @@ graceful-fs@^4.1.2: graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== has-flag@^4.0.0: @@ -1715,24 +1715,24 @@ has-flag@^4.0.0: hasown@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== ignore@^5.2.4: version "5.3.2" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== import-local@^3.0.2: version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" @@ -1740,12 +1740,12 @@ import-local@^3.0.2: interpret@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== is-core-module@^2.16.0: version "2.16.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.0.tgz#6c01ffdd5e33c49c1d2abfa93334a85cb56bd81c" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz" integrity sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g== dependencies: hasown "^2.0.2" @@ -1764,7 +1764,7 @@ is-glob@^4.0.1: is-glob@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" @@ -1793,7 +1793,7 @@ isobject@^3.0.1: jest-util@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: "@jest/types" "^29.6.3" @@ -1805,7 +1805,7 @@ jest-util@^29.7.0: jest-worker@^27.4.5: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" @@ -1814,7 +1814,7 @@ jest-worker@^27.4.5: jest-worker@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" @@ -1829,17 +1829,17 @@ js-tokens@^4.0.0: jsesc@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== jsesc@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz" integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== json-parse-even-better-errors@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: @@ -1849,12 +1849,12 @@ json-schema-traverse@^0.4.1: json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json5@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== kind-of@^6.0.2: @@ -1864,12 +1864,12 @@ kind-of@^6.0.2: lilconfig@^3.1.1: version "3.1.3" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz" integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== loader-runner@^4.2.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== locate-path@^5.0.0: @@ -1881,14 +1881,14 @@ locate-path@^5.0.0: locate-path@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz" integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== dependencies: p-locate "^6.0.0" lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.memoize@^4.1.2: @@ -1908,19 +1908,19 @@ lodash@^4.17.19: lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" mdn-data@2.0.28: version "2.0.28" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz" integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== mdn-data@2.0.30: version "2.0.30" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz" integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== merge-stream@^2.0.0: @@ -1930,12 +1930,12 @@ merge-stream@^2.0.0: merge2@^1.3.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^4.0.4: version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" @@ -1943,24 +1943,29 @@ micromatch@^4.0.4: mime-db@1.52.0: version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.27: version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mini-css-extract-plugin@^2.8.1: version "2.9.2" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz#966031b468917a5446f4c24a80854b2947503c5b" + resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz" integrity sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w== dependencies: schema-utils "^4.0.0" tapable "^2.2.1" +morphdom@2.7.7: + version "2.7.7" + resolved "https://registry.yarnpkg.com/morphdom/-/morphdom-2.7.7.tgz#72559af781357a4eb2169a0a578acb76ff7e8abf" + integrity sha512-04GmsiBcalrSCNmzfo+UjU8tt3PhZJKzcOy+r1FlGA7/zri8wre3I1WkYN9PT3sIeIKfW9bpyElA+VzOg2E24g== + ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" @@ -1968,22 +1973,22 @@ ms@2.1.2: ms@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== nanoid@^3.3.7: version "3.3.8" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== neo-async@^2.6.2: version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== node-releases@^2.0.19: version "2.0.19" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== normalize-path@^3.0.0: @@ -1993,7 +1998,7 @@ normalize-path@^3.0.0: nth-check@^2.0.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: boolbase "^1.0.0" @@ -2007,7 +2012,7 @@ p-limit@^2.2.0: p-limit@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz" integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== dependencies: yocto-queue "^1.0.0" @@ -2021,7 +2026,7 @@ p-locate@^4.1.0: p-locate@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz" integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== dependencies: p-limit "^4.0.0" @@ -2038,7 +2043,7 @@ path-exists@^4.0.0: path-exists@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz" integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== path-key@^3.1.0: @@ -2048,50 +2053,52 @@ path-key@^3.1.0: path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-type@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" + resolved "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz" integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== "phoenix@file:../deps/phoenix": - version "1.7.18" + version "1.8.0" "phoenix_html@file:../deps/phoenix_html": - version "4.1.0" + version "4.2.1" "phoenix_live_view@file:../deps/phoenix_live_view": - version "1.0.1" + version "1.1.8" + dependencies: + morphdom "2.7.7" picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" pkg-dir@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz" integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA== dependencies: find-up "^6.3.0" postcss-calc@^9.0.1: version "9.0.1" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-9.0.1.tgz#a744fd592438a93d6de0f1434c572670361eb6c6" + resolved "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz" integrity sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ== dependencies: postcss-selector-parser "^6.0.11" @@ -2099,7 +2106,7 @@ postcss-calc@^9.0.1: postcss-colormin@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-6.1.0.tgz#076e8d3fb291fbff7b10e6b063be9da42ff6488d" + resolved "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz" integrity sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw== dependencies: browserslist "^4.23.0" @@ -2109,7 +2116,7 @@ postcss-colormin@^6.1.0: postcss-convert-values@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz#3498387f8efedb817cbc63901d45bd1ceaa40f48" + resolved "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz" integrity sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w== dependencies: browserslist "^4.23.0" @@ -2117,27 +2124,27 @@ postcss-convert-values@^6.1.0: postcss-discard-comments@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz#e768dcfdc33e0216380623652b0a4f69f4678b6c" + resolved "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz" integrity sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw== postcss-discard-duplicates@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz#d121e893c38dc58a67277f75bb58ba43fce4c3eb" + resolved "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz" integrity sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw== postcss-discard-empty@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz#ee39c327219bb70473a066f772621f81435a79d9" + resolved "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz" integrity sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ== postcss-discard-overridden@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz#4e9f9c62ecd2df46e8fdb44dc17e189776572e2d" + resolved "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz" integrity sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ== postcss-merge-longhand@^6.0.5: version "6.0.5" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz#ba8a8d473617c34a36abbea8dda2b215750a065a" + resolved "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz" integrity sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w== dependencies: postcss-value-parser "^4.2.0" @@ -2145,7 +2152,7 @@ postcss-merge-longhand@^6.0.5: postcss-merge-rules@^6.1.1: version "6.1.1" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz#7aa539dceddab56019469c0edd7d22b64c3dea9d" + resolved "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz" integrity sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ== dependencies: browserslist "^4.23.0" @@ -2155,14 +2162,14 @@ postcss-merge-rules@^6.1.1: postcss-minify-font-values@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz#a0e574c02ee3f299be2846369211f3b957ea4c59" + resolved "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz" integrity sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg== dependencies: postcss-value-parser "^4.2.0" postcss-minify-gradients@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz#ca3eb55a7bdb48a1e187a55c6377be918743dbd6" + resolved "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz" integrity sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q== dependencies: colord "^2.9.3" @@ -2171,7 +2178,7 @@ postcss-minify-gradients@^6.0.3: postcss-minify-params@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz#54551dec77b9a45a29c3cb5953bf7325a399ba08" + resolved "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz" integrity sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA== dependencies: browserslist "^4.23.0" @@ -2180,19 +2187,19 @@ postcss-minify-params@^6.1.0: postcss-minify-selectors@^6.0.4: version "6.0.4" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz#197f7d72e6dd19eed47916d575d69dc38b396aff" + resolved "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz" integrity sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ== dependencies: postcss-selector-parser "^6.0.16" postcss-modules-extract-imports@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + resolved "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz" integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== postcss-modules-local-by-default@^4.0.5: version "4.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz#d150f43837831dae25e4085596e84f6f5d6ec368" + resolved "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz" integrity sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw== dependencies: icss-utils "^5.0.0" @@ -2201,61 +2208,61 @@ postcss-modules-local-by-default@^4.0.5: postcss-modules-scope@^3.2.0: version "3.2.1" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz#1bbccddcb398f1d7a511e0a2d1d047718af4078c" + resolved "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz" integrity sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA== dependencies: postcss-selector-parser "^7.0.0" postcss-modules-values@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + resolved "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz" integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== dependencies: icss-utils "^5.0.0" postcss-normalize-charset@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz#1ec25c435057a8001dac942942a95ffe66f721e1" + resolved "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz" integrity sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ== postcss-normalize-display-values@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz#54f02764fed0b288d5363cbb140d6950dbbdd535" + resolved "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz" integrity sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-positions@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz#e982d284ec878b9b819796266f640852dbbb723a" + resolved "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz" integrity sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-repeat-style@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz#f8006942fd0617c73f049dd8b6201c3a3040ecf3" + resolved "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz" integrity sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-string@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz#e3cc6ad5c95581acd1fc8774b309dd7c06e5e363" + resolved "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz" integrity sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-timing-functions@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz#40cb8726cef999de984527cbd9d1db1f3e9062c0" + resolved "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz" integrity sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-unicode@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz#aaf8bbd34c306e230777e80f7f12a4b7d27ce06e" + resolved "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz" integrity sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg== dependencies: browserslist "^4.23.0" @@ -2263,21 +2270,21 @@ postcss-normalize-unicode@^6.1.0: postcss-normalize-url@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz#292792386be51a8de9a454cb7b5c58ae22db0f79" + resolved "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz" integrity sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ== dependencies: postcss-value-parser "^4.2.0" postcss-normalize-whitespace@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz#fbb009e6ebd312f8b2efb225c2fcc7cf32b400cd" + resolved "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz" integrity sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q== dependencies: postcss-value-parser "^4.2.0" postcss-ordered-values@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz#366bb663919707093451ab70c3f99c05672aaae5" + resolved "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz" integrity sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q== dependencies: cssnano-utils "^4.0.2" @@ -2285,7 +2292,7 @@ postcss-ordered-values@^6.0.2: postcss-reduce-initial@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz#4401297d8e35cb6e92c8e9586963e267105586ba" + resolved "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz" integrity sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw== dependencies: browserslist "^4.23.0" @@ -2293,14 +2300,14 @@ postcss-reduce-initial@^6.1.0: postcss-reduce-transforms@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz#6fa2c586bdc091a7373caeee4be75a0f3e12965d" + resolved "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz" integrity sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA== dependencies: postcss-value-parser "^4.2.0" postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16: version "6.1.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz" integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== dependencies: cssesc "^3.0.0" @@ -2308,7 +2315,7 @@ postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16: postcss-selector-parser@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz#41bd8b56f177c093ca49435f65731befe25d6b9c" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz" integrity sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ== dependencies: cssesc "^3.0.0" @@ -2316,7 +2323,7 @@ postcss-selector-parser@^7.0.0: postcss-svgo@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-6.0.3.tgz#1d6e180d6df1fa8a3b30b729aaa9161e94f04eaa" + resolved "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz" integrity sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g== dependencies: postcss-value-parser "^4.2.0" @@ -2324,19 +2331,19 @@ postcss-svgo@^6.0.3: postcss-unique-selectors@^6.0.4: version "6.0.4" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz#983ab308896b4bf3f2baaf2336e14e52c11a2088" + resolved "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz" integrity sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg== dependencies: postcss-selector-parser "^6.0.16" postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.4.33: version "8.4.49" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz" integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== dependencies: nanoid "^3.3.7" @@ -2350,33 +2357,33 @@ punycode@^2.1.0: queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" rechoir@^0.8.0: version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz" integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: resolve "^1.20.0" regenerate-unicode-properties@^10.2.0: version "10.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz" integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== dependencies: regenerate "^1.4.2" regenerate@^1.4.2: version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== regenerator-runtime@^0.13.4: @@ -2386,14 +2393,14 @@ regenerator-runtime@^0.13.4: regenerator-transform@^0.15.2: version "0.15.2" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz" integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== dependencies: "@babel/runtime" "^7.8.4" regexpu-core@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz" integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== dependencies: regenerate "^1.4.2" @@ -2405,36 +2412,36 @@ regexpu-core@^6.2.0: regjsgen@^0.8.0: version "0.8.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" + resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== regjsparser@^0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz" integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== dependencies: jsesc "~3.0.2" require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve@^1.14.2, resolve@^1.20.0: version "1.22.9" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.9.tgz#6da76e4cdc57181fa4471231400e8851d0a924f3" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz" integrity sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A== dependencies: is-core-module "^2.16.0" @@ -2443,12 +2450,12 @@ resolve@^1.14.2, resolve@^1.20.0: reusify@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" @@ -2460,7 +2467,7 @@ safe-buffer@^5.1.0: schema-utils@^3.2.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== dependencies: "@types/json-schema" "^7.0.8" @@ -2469,7 +2476,7 @@ schema-utils@^3.2.0: schema-utils@^4.0.0, schema-utils@^4.2.0, schema-utils@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.0.tgz#3b669f04f71ff2dfb5aba7ce2d5a9d79b35622c0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz" integrity sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g== dependencies: "@types/json-schema" "^7.0.9" @@ -2479,17 +2486,17 @@ schema-utils@^4.0.0, schema-utils@^4.2.0, schema-utils@^4.3.0: semver@^6.3.1: version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.5.4: version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== serialize-javascript@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -2515,17 +2522,17 @@ shebang-regex@^3.0.0: slash@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz" integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== source-map-js@^1.0.1, source-map-js@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map-support@~0.5.20: version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" @@ -2533,12 +2540,12 @@ source-map-support@~0.5.20: source-map@^0.6.0: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== stylehacks@^6.1.1: version "6.1.1" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-6.1.1.tgz#543f91c10d17d00a440430362d419f79c25545a6" + resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz" integrity sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg== dependencies: browserslist "^4.23.0" @@ -2553,19 +2560,19 @@ supports-color@^7.1.0: supports-color@^8.0.0: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== svgo@^3.2.0: version "3.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" + resolved "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz" integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw== dependencies: "@trysound/sax" "0.2.0" @@ -2578,12 +2585,12 @@ svgo@^3.2.0: tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== terser-webpack-plugin@^5.3.10: version "5.3.11" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz#93c21f44ca86634257cac176f884f942b7ba3832" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz" integrity sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ== dependencies: "@jridgewell/trace-mapping" "^0.3.25" @@ -2594,7 +2601,7 @@ terser-webpack-plugin@^5.3.10: terser@^5.31.1: version "5.37.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.37.0.tgz#38aa66d1cfc43d0638fab54e43ff8a4f72a21ba3" + resolved "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz" integrity sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA== dependencies: "@jridgewell/source-map" "^0.3.3" @@ -2616,12 +2623,12 @@ to-regex-range@^5.0.1: unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" + resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz" integrity sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: unicode-canonical-property-names-ecmascript "^2.0.0" @@ -2629,22 +2636,22 @@ unicode-match-property-ecmascript@^2.0.0: unicode-match-property-value-ecmascript@^2.1.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz#a0401aee72714598f739b68b104e4fe3a0cb3c71" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz" integrity sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg== unicode-property-aliases-ecmascript@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== unicorn-magic@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz" integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== update-browserslist-db@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz" integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: escalade "^3.2.0" @@ -2664,7 +2671,7 @@ util-deprecate@^1.0.2: watchpack@^2.4.1: version "2.4.2" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz" integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== dependencies: glob-to-regexp "^0.4.1" @@ -2672,7 +2679,7 @@ watchpack@^2.4.1: webpack-cli@^5.1.4: version "5.1.4" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz" integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== dependencies: "@discoveryjs/json-ext" "^0.5.0" @@ -2691,7 +2698,7 @@ webpack-cli@^5.1.4: webpack-merge@^5.7.3: version "5.10.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz" integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" @@ -2700,12 +2707,12 @@ webpack-merge@^5.7.3: webpack-sources@^3.2.3: version "3.2.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@5.91.0: version "5.91.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz" integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== dependencies: "@types/eslint-scope" "^3.7.3" @@ -2742,15 +2749,15 @@ which@^2.0.1: wildcard@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz" integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yocto-queue@^1.0.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz" integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== diff --git a/lib/diff/hex/hex.ex b/lib/diff/hex/hex.ex index 92f0405..00a45ea 100644 --- a/lib/diff/hex/hex.ex +++ b/lib/diff/hex/hex.ex @@ -101,9 +101,12 @@ defmodule Diff.Hex do with {_, true} <- {:file_size_old, file_size_check?(path_old)}, {_, true} <- {:file_size_new, file_size_check?(path_new)}, - {_, {:ok, output}} <- {:git_diff, git_diff(path_old, path_new)}, - {_, {:ok, patches}} <- {:parse_patch, parse_patch(output, path_from, path_to)} do - Enum.map(patches, &{:ok, &1}) + {_, {:ok, output}} <- {:git_diff, git_diff(path_old, path_new)} do + if output do + [{:ok, {output, path_from, path_to}}] + else + [] + end else {:file_size_old, false} -> [{:too_large, Path.relative_to(path_old, path_from)}] @@ -150,14 +153,6 @@ defmodule Diff.Hex do end end - defp parse_patch(_output = nil, _path_from, _path_to) do - {:ok, []} - end - - defp parse_patch(output, path_from, path_to) do - GitDiff.parse_patch(output, relative_from: path_from, relative_to: path_to) - end - defp file_size_check?(path) do File.stat!(path).size <= @max_file_size end diff --git a/lib/diff/storage/gcs.ex b/lib/diff/storage/gcs.ex index f27f1f1..ccb1d0e 100644 --- a/lib/diff/storage/gcs.ex +++ b/lib/diff/storage/gcs.ex @@ -5,12 +5,12 @@ defmodule Diff.Storage.GCS do @gs_xml_url "https://storage.googleapis.com" - def get(package, from_version, to_version) do + def get_diff(package, from_version, to_version, diff_id) do with {:ok, hash} <- combined_checksum(package, from_version, to_version), - url = url(key(package, from_version, to_version, hash)), - {:ok, 200, _headers, stream} <- - Diff.HTTP.retry("gs", fn -> Diff.HTTP.get_stream(url, headers()) end) do - {:ok, stream} + url = url(diff_key(package, from_version, to_version, hash, diff_id)), + {:ok, 200, _headers, body} <- + Diff.HTTP.retry("gs", fn -> Diff.HTTP.get(url, headers()) end) do + {:ok, body} else {:ok, 404, _headers, _body} -> {:error, :not_found} @@ -25,16 +25,16 @@ defmodule Diff.Storage.GCS do end end - def put(package, from_version, to_version, stream) do + def put_diff(package, from_version, to_version, diff_id, diff_data) do with {:ok, hash} <- combined_checksum(package, from_version, to_version), - url = url(key(package, from_version, to_version, hash)), + url = url(diff_key(package, from_version, to_version, hash, diff_id)), {:ok, 200, _headers, _body} <- - Diff.HTTP.retry("gs", fn -> Diff.HTTP.put_stream(url, headers(), stream) end) do + Diff.HTTP.retry("gs", fn -> Diff.HTTP.put(url, headers(), diff_data) end) do :ok else {:ok, status, _headers, _body} -> Logger.error("Failed to put diff to storage. Status #{status}") - {:error, :not_found} + {:error, :storage_error} error -> Logger.error("Failed to put diff to storage. Reason #{inspect(error)}") @@ -42,6 +42,65 @@ defmodule Diff.Storage.GCS do end end + def list_diffs(package, from_version, to_version) do + case get_metadata(package, from_version, to_version) do + {:ok, %{total_diffs: total_diffs}} -> + diff_ids = 0..(total_diffs - 1) |> Enum.map(&"diff-#{&1}") + {:ok, diff_ids} + + {:error, :not_found} -> + {:ok, []} + + error -> + error + end + end + + def get_metadata(package, from_version, to_version) do + with {:ok, hash} <- combined_checksum(package, from_version, to_version), + url = url(metadata_key(package, from_version, to_version, hash)), + {:ok, 200, _headers, body} <- + Diff.HTTP.retry("gs", fn -> Diff.HTTP.get(url, headers()) end) do + case Jason.decode(body, keys: :atoms) do + {:ok, metadata} -> {:ok, metadata} + {:error, _} -> {:error, :invalid_metadata} + end + else + {:ok, 404, _headers, _body} -> + {:error, :not_found} + + {:ok, status, _headers, _body} -> + Logger.error("Failed to get metadata from storage. Status #{status}") + {:error, :not_found} + + {:error, reason} -> + Logger.error("Failed to get metadata from storage. Reason #{inspect(reason)}") + {:error, :not_found} + end + end + + def put_metadata(package, from_version, to_version, metadata) do + with {:ok, hash} <- combined_checksum(package, from_version, to_version), + url = url(metadata_key(package, from_version, to_version, hash)), + {:ok, json} <- Jason.encode(metadata), + {:ok, 200, _headers, _body} <- + Diff.HTTP.retry("gs", fn -> Diff.HTTP.put(url, headers(), json) end) do + :ok + else + {:ok, status, _headers, _body} -> + Logger.error("Failed to put metadata to storage. Status #{status}") + {:error, :storage_error} + + {:error, %Jason.EncodeError{}} -> + Logger.error("Failed to encode metadata as JSON") + {:error, :invalid_metadata} + + error -> + Logger.error("Failed to put metadata to storage. Reason #{inspect(error)}") + error + end + end + defp headers() do token = Goth.fetch!(Diff.Goth) [{"authorization", "#{token.type} #{token.token}"}] @@ -53,8 +112,12 @@ defmodule Diff.Storage.GCS do end end - defp key(package, from_version, to_version, hash) do - "diffs/#{package}-#{from_version}-#{to_version}-#{hash}.html" + defp diff_key(package, from_version, to_version, hash, diff_id) do + "diffs/#{package}-#{from_version}-#{to_version}-#{hash}-#{diff_id}.json" + end + + defp metadata_key(package, from_version, to_version, hash) do + "metadata/#{package}-#{from_version}-#{to_version}-#{hash}.json" end defp url(key) do diff --git a/lib/diff/storage/local.ex b/lib/diff/storage/local.ex index 435f414..700f56c 100644 --- a/lib/diff/storage/local.ex +++ b/lib/diff/storage/local.ex @@ -3,14 +3,14 @@ defmodule Diff.Storage.Local do @behaviour Diff.Storage - def get(package, from_version, to_version) do + def get_diff(package, from_version, to_version, diff_id) do case combined_checksum(package, from_version, to_version) do {:ok, hash} -> - filename = key(package, from_version, to_version, hash) + filename = diff_key(package, from_version, to_version, hash, diff_id) path = Path.join([dir(), package, filename]) if File.regular?(path) do - {:ok, File.stream!(path, [:read_ahead])} + {:ok, File.read!(path)} else {:error, :not_found} end @@ -20,12 +20,12 @@ defmodule Diff.Storage.Local do end end - def put(package, from_version, to_version, stream) do + def put_diff(package, from_version, to_version, diff_id, diff_data) do with {:ok, hash} <- combined_checksum(package, from_version, to_version), - filename = key(package, from_version, to_version, hash), + filename = diff_key(package, from_version, to_version, hash, diff_id), path = Path.join([dir(), package, filename]), :ok <- File.mkdir_p(Path.dirname(path)) do - Enum.into(stream, File.stream!(path, [:write_delay])) + File.write!(path, diff_data) :ok else {:error, reason} -> @@ -34,14 +34,73 @@ defmodule Diff.Storage.Local do end end + def list_diffs(package, from_version, to_version) do + case get_metadata(package, from_version, to_version) do + {:ok, %{total_diffs: total_diffs}} -> + diff_ids = 0..(total_diffs - 1) |> Enum.map(&"diff-#{&1}") + {:ok, diff_ids} + + {:error, :not_found} -> + {:ok, []} + + error -> + error + end + end + + def get_metadata(package, from_version, to_version) do + case combined_checksum(package, from_version, to_version) do + {:ok, hash} -> + filename = metadata_key(package, from_version, to_version, hash) + path = Path.join([dir(), package, filename]) + + if File.regular?(path) do + case File.read(path) do + {:ok, content} -> + case Jason.decode(content, keys: :atoms) do + {:ok, metadata} -> {:ok, metadata} + {:error, _} -> {:error, :invalid_metadata} + end + + {:error, reason} -> + {:error, reason} + end + else + {:error, :not_found} + end + + {:error, :not_found} -> + {:error, :not_found} + end + end + + def put_metadata(package, from_version, to_version, metadata) do + with {:ok, hash} <- combined_checksum(package, from_version, to_version), + filename = metadata_key(package, from_version, to_version, hash), + path = Path.join([dir(), package, filename]), + :ok <- File.mkdir_p(Path.dirname(path)), + {:ok, json} <- Jason.encode(metadata) do + File.write!(path, json) + :ok + else + {:error, reason} -> + Logger.error("Failed to store metadata. Reason: #{inspect(reason)}.") + {:error, reason} + end + end + def combined_checksum(package, from, to) do with {:ok, checksums} <- Diff.Hex.get_checksums(package, [from, to]) do {:ok, :erlang.phash2({Application.get_env(:diff, :cache_version), checksums})} end end - defp key(package, from_version, to_version, hash) do - "#{package}-#{from_version}-#{to_version}-#{hash}.html" + defp diff_key(package, from_version, to_version, hash, diff_id) do + "diffs/#{package}-#{from_version}-#{to_version}-#{hash}-#{diff_id}.json" + end + + defp metadata_key(package, from_version, to_version, hash) do + "metadata/#{package}-#{from_version}-#{to_version}-#{hash}.json" end defp dir() do diff --git a/lib/diff/storage/storage.ex b/lib/diff/storage/storage.ex index 1140db6..5915992 100644 --- a/lib/diff/storage/storage.ex +++ b/lib/diff/storage/storage.ex @@ -2,18 +2,46 @@ defmodule Diff.Storage do @type package :: String.t() @type from_version :: String.t() @type to_version :: String.t() - @type diff :: Enum.t() + @type diff_id :: String.t() + @type diff_html :: String.t() + @type diff_metadata :: %{ + total_diffs: non_neg_integer(), + total_additions: non_neg_integer(), + total_deletions: non_neg_integer(), + files_changed: non_neg_integer() + } - @callback get(package, from_version, to_version) :: {:ok, diff} | {:error, term} - @callback put(package, from_version, to_version, diff) :: :ok | {:error, term} + # New diff-level storage callbacks + @callback get_diff(package, from_version, to_version, diff_id) :: + {:ok, diff_html} | {:error, term} + @callback put_diff(package, from_version, to_version, diff_id, diff_html) :: + :ok | {:error, term} + @callback list_diffs(package, from_version, to_version) :: {:ok, [diff_id]} | {:error, term} + + # Metadata storage callbacks + @callback get_metadata(package, from_version, to_version) :: + {:ok, diff_metadata} | {:error, term} + @callback put_metadata(package, from_version, to_version, diff_metadata) :: :ok | {:error, term} defp impl(), do: Application.get_env(:diff, :storage_impl) - def get(package, from_version, to_version) do - impl().get(package, from_version, to_version) + def get_diff(package, from_version, to_version, diff_id) do + impl().get_diff(package, from_version, to_version, diff_id) + end + + def put_diff(package, from_version, to_version, diff_id, diff_html) do + impl().put_diff(package, from_version, to_version, diff_id, diff_html) + end + + def list_diffs(package, from_version, to_version) do + impl().list_diffs(package, from_version, to_version) + end + + def get_metadata(package, from_version, to_version) do + impl().get_metadata(package, from_version, to_version) end - def put(package, from_version, to_version, diff) do - impl().put(package, from_version, to_version, diff) + def put_metadata(package, from_version, to_version, metadata) do + impl().put_metadata(package, from_version, to_version, metadata) end end diff --git a/lib/diff_web.ex b/lib/diff_web.ex index 641812d..b3c6295 100644 --- a/lib/diff_web.ex +++ b/lib/diff_web.ex @@ -19,7 +19,9 @@ defmodule DiffWeb do def controller do quote do - use Phoenix.Controller, namespace: DiffWeb + use Phoenix.Controller, formats: [:html] + + plug :put_layout, html: {DiffWeb.LayoutView, :app} import Plug.Conn import DiffWeb.Gettext diff --git a/lib/diff_web/controllers/page_controller.ex b/lib/diff_web/controllers/page_controller.ex deleted file mode 100644 index 7a3dd86..0000000 --- a/lib/diff_web/controllers/page_controller.ex +++ /dev/null @@ -1,225 +0,0 @@ -defmodule DiffWeb.PageController do - use DiffWeb, :controller - - require Logger - - @chunk_size 64 * 1024 - - def diffs(conn, %{"diffs" => diffs}) do - diffs = - diffs - |> Enum.map(&parse_diff/1) - |> Enum.reject(&is_nil/1) - - conn - |> assign(:diffs, diffs) - |> render() - end - - def diffs(conn, _params), do: render_error(conn, 400) - - def diff(conn, %{"package" => package, "versions" => versions}) do - case parse_versions(versions) do - {:ok, from, to} -> - maybe_cached_diff(conn, package, from, to) - - :error -> - render_error(conn, 400) - end - end - - defp maybe_cached_diff(conn, _package, version, version) do - render_error(conn, 400) - end - - defp maybe_cached_diff(conn, _package, :latest, _version) do - render_error(conn, 400) - end - - defp maybe_cached_diff(conn, package, from, :latest) do - case Diff.Package.Store.get_versions(package) do - {:ok, versions} -> - to = - versions - |> Enum.map(&Version.parse!/1) - |> Enum.filter(&(&1.pre == [])) - |> Enum.max(Version) - - maybe_cached_diff(conn, package, from, to) - - {:error, :not_found} -> - render_error(conn, 404) - end - end - - defp maybe_cached_diff(conn, package, from, to) do - from = to_string(from) - to = to_string(to) - - case Diff.Storage.get(package, from, to) do - {:ok, stream} -> - Logger.debug("cache hit for #{package}/#{from}..#{to}") - - conn - |> put_resp_content_type("text/html") - |> stream_diff(stream, package, from, to) - - {:error, :not_found} -> - Logger.debug("cache miss for #{package}/#{from}..#{to}") - do_diff(conn, package, from, to) - end - end - - defp do_diff(conn, package, from, to) do - case Diff.Hex.diff(package, from, to) do - {:ok, stream} -> - path = render_diff(package, from, to, stream) - stream = File.stream!(path, [:read_ahead], @chunk_size) - - cache_diff(package, from, to, stream) - delete_diff(path) - - conn - |> put_resp_content_type("text/html") - |> stream_diff(stream, package, from, to) - - :error -> - render_error(conn, 500) - end - catch - :throw, {:diff, :invalid_diff} -> - render_error(conn, 500) - end - - defp stream_diff(conn, stream, package, from, to) do - header_assigns = [conn: conn, package: package, from: from, to: to] - - header = [ - Phoenix.View.render_to_iodata(DiffWeb.LayoutView, "header.html", header_assigns), - Phoenix.View.render_to_iodata(DiffWeb.PageView, "diff_header.html", []) - ] - - footer = [ - Phoenix.View.render_to_iodata(DiffWeb.PageView, "diff_footer.html", []), - Phoenix.View.render_to_iodata(DiffWeb.LayoutView, "footer.html", conn: conn) - ] - - conn = send_chunked(conn, 200) - - with {:ok, conn} <- chunk(conn, header), - {:ok, conn} <- stream_chunks(conn, stream), - {:ok, conn} <- chunk(conn, footer) do - conn - else - {:error, reason} -> - Logger.error("chunking failed: #{inspect(reason)}") - conn - end - end - - defp stream_chunks(conn, stream) do - Enum.reduce_while(stream, {:ok, conn}, fn chunk, {:ok, conn} -> - case chunk(conn, chunk) do - {:ok, conn} -> - {:cont, {:ok, conn}} - - {:error, reason} -> - {:halt, {:error, reason}} - end - end) - end - - defp render_diff(package, from, to, stream) do - path = tmp_path("html-#{package}-#{from}-#{to}-") - - File.open!(path, [:write, :raw, :binary, :write_delay], fn file -> - Enum.each(stream, fn - {:ok, patch} -> - if patch.chunks != [] do - html = Phoenix.View.render_to_iodata(DiffWeb.RenderView, "patch.html", patch: patch) - IO.binwrite(file, html) - end - - {:too_large, path} -> - html = Phoenix.View.render_to_iodata(DiffWeb.RenderView, "too_large.html", file: path) - IO.binwrite(file, html) - - {:error, error} -> - Logger.error("Failed to diff #{package} #{from}..#{to} with: #{inspect(error)}") - throw({:diff, :invalid_diff}) - end) - end) - - path - end - - defp cache_diff(package, from, to, stream) do - Task.Supervisor.start_child(Diff.Tasks, fn -> - Diff.Storage.put(package, from, to, stream) - end) - end - - # A bit of a hack to make sure both the GCS upload and chunked response - # has opened file before we delete it - defp delete_diff(path) do - Task.Supervisor.start_child(Diff.Tasks, fn -> - Process.sleep(10_000) - File.rm(path) - end) - end - - defp parse_versions(input) do - with {:ok, [from, to]} <- versions_from_input(input), - {:ok, from} <- parse_version(from), - {:ok, to} <- parse_version(to) do - {:ok, from, to} - else - _ -> - :error - end - end - - defp versions_from_input(input) when is_binary(input) do - input - |> String.split("..", parts: 2) - |> case do - [from] -> - [from, ""] - - [from, to] -> - [from, to] - end - |> versions_from_input() - end - - defp versions_from_input([_from, _to] = versions) do - versions = Enum.map(versions, &String.trim/1) - {:ok, versions} - end - - defp versions_from_input(_), do: :error - - defp parse_version(""), do: {:ok, :latest} - defp parse_version(input), do: Version.parse(input) - - defp render_error(conn, status) do - conn - |> put_view(DiffWeb.ErrorView) - |> put_status(status) - |> render("#{status}.html") - end - - defp tmp_path(prefix) do - random_string = Base.encode16(:crypto.strong_rand_bytes(4)) - Path.join([System.tmp_dir!(), "diff", prefix <> random_string]) - end - - defp parse_diff(diff) do - case String.split(diff, ":", trim: true) do - [app, from, to] -> {app, from, to, build_url(app, from, to)} - _ -> nil - end - end - - defp build_url(app, from, to), do: "/diff/#{app}/#{from}..#{to}" -end diff --git a/lib/diff_web/live/components/diff_component.ex b/lib/diff_web/live/components/diff_component.ex new file mode 100644 index 0000000..78757eb --- /dev/null +++ b/lib/diff_web/live/components/diff_component.ex @@ -0,0 +1,94 @@ +defmodule DiffWeb.DiffComponent do + use DiffWeb, :live_component + + def render(assigns) do + ~H""" +
+
+ + <%= diff_status(@diff) %> + + <%= file_header(@diff, diff_status(@diff)) %> + + +
+
+ + <%= for chunk <- @diff.chunks do %> + + + + + <%= for line <- chunk.lines do %> + + + + + <% end %> + <% end %> +
+
 
+
+
+
<%= chunk.header %>
+
+
+ <%= line_number(line.from_line_number) %> +
+
+ <%= line_number(line.to_line_number) %> +
+
+
<%= line_text(line.text) %>
+
+
+
+ """ + end + + defp file_header(diff, status) do + from = diff.from + to = diff.to + + case status do + "changed" -> from + "renamed" -> "#{from} -> #{to}" + "removed" -> from + "added" -> to + end + end + + defp diff_status(diff) do + from = diff.from + to = diff.to + + cond do + !from -> "added" + !to -> "removed" + from == to -> "changed" + true -> "renamed" + end + end + + defp line_number(ln) when is_nil(ln), do: "" + defp line_number(ln), do: to_string(ln) + + defp line_id(diff, line) do + hash = :erlang.phash2({diff.from, diff.to}) + ln = "-#{line.from_line_number}-#{line.to_line_number}" + "#{hash}#{ln}" + end + + defp line_type(line), do: to_string(line.type) + + defp line_text("+" <> text), + do: [content_tag(:span, "+ ", class: "ghd-line-status"), content_tag(:span, text)] + + defp line_text("-" <> text), + do: [content_tag(:span, "- ", class: "ghd-line-status"), content_tag(:span, text)] + + defp line_text(" " <> text), + do: [content_tag(:span, " ", class: "ghd-line-status"), content_tag(:span, text)] + + defp line_text(text), do: [content_tag(:span, text)] +end diff --git a/lib/diff_web/live/components/too_large_component.ex b/lib/diff_web/live/components/too_large_component.ex new file mode 100644 index 0000000..7d5ea1c --- /dev/null +++ b/lib/diff_web/live/components/too_large_component.ex @@ -0,0 +1,21 @@ +defmodule DiffWeb.TooLargeComponent do + use DiffWeb, :live_component + + def render(assigns) do + ~H""" +
+
+ + unknown + + <%= @file %> + + +
+
+ CANNOT RENDER FILES LARGER THAN 1MB +
+
+ """ + end +end diff --git a/lib/diff_web/live/diff_live_view.ex b/lib/diff_web/live/diff_live_view.ex new file mode 100644 index 0000000..4a8a5dc --- /dev/null +++ b/lib/diff_web/live/diff_live_view.ex @@ -0,0 +1,339 @@ +defmodule DiffWeb.DiffLiveView do + use DiffWeb, :live_view + + require Logger + + def render(assigns) do + Phoenix.View.render(DiffWeb.LiveView, "diff.html", assigns) + end + + # Mount for single diff view + def mount(%{"package" => package, "versions" => versions}, _session, socket) do + case parse_versions(versions) do + {:ok, from, to} -> + case resolve_latest_version(package, from, to) do + {:ok, resolved_from, resolved_to} -> + mount_single_diff(socket, package, resolved_from, resolved_to) + + {:error, reason} -> + {:ok, assign(socket, error: "Package not found: #{reason}")} + end + + :error -> + {:ok, assign(socket, error: "Invalid version format")} + end + end + + # Mount for diffs list view + def mount(%{"diffs" => raw_diffs}, _session, socket) when is_list(raw_diffs) do + diffs = + raw_diffs + |> Enum.map(&parse_diff/1) + |> Enum.reject(&is_nil/1) + + socket = + assign(socket, + view_mode: :diffs_list, + diffs: diffs + ) + + {:ok, socket} + end + + def mount(params, _session, socket) do + case Map.get(params, "diffs") do + nil -> + {:ok, assign(socket, view_mode: :diffs_list, diffs: [], error: "No diffs provided")} + + [] -> + {:ok, assign(socket, view_mode: :diffs_list, diffs: [], error: "No diffs provided")} + + diffs_param -> + mount(%{"diffs" => List.wrap(diffs_param)}, nil, socket) + end + end + + defp mount_single_diff(socket, package, from, to) do + case Diff.Storage.get_metadata(package, from, to) do + {:ok, metadata} -> + load_existing_diff(socket, package, from, to, metadata) + + {:error, :not_found} -> + generate_new_diff(socket, package, from, to) + + {:error, reason} -> + Logger.error("Failed to load diff metadata: #{inspect(reason)}") + {:ok, assign(socket, error: "Failed to load diff")} + end + end + + defp load_existing_diff(socket, package, from, to, metadata) do + {:ok, diff_ids} = Diff.Storage.list_diffs(package, from, to) + + initial_batch_size = 5 + {initial_diffs, remaining} = Enum.split(diff_ids, initial_batch_size) + + socket = + assign(socket, + view_mode: :single_diff, + package: package, + from: from, + to: to, + metadata: metadata, + all_diff_ids: diff_ids, + loaded_diffs: initial_diffs, + remaining_diffs: remaining, + loading: true, + generating: false, + has_more_diffs: length(remaining) > 0 + ) + + send(self(), {:load_diffs, initial_diffs}) + + {:ok, socket} + end + + defp generate_new_diff(socket, package, from, to) do + socket = + assign(socket, + view_mode: :single_diff, + package: package, + from: from, + to: to, + metadata: %{files_changed: 0, total_additions: 0, total_deletions: 0}, + all_diff_ids: [], + loaded_diffs: [], + remaining_diffs: [], + loading: false, + generating: true, + has_more_diffs: false + ) + + send(self(), {:generate_diff, package, from, to}) + + {:ok, socket} + end + + defp resolve_latest_version(package, from, to) when to == :latest or to == "latest" do + case Diff.Package.Store.get_versions(package) do + {:ok, versions} -> + to = + versions + |> Enum.map(&Version.parse!/1) + |> Enum.filter(&(&1.pre == [])) + |> Enum.max(Version) + + {:ok, from, to_string(to)} + + {:error, :not_found} -> + {:error, :not_found} + end + end + + defp resolve_latest_version(_package, from, to), do: {:ok, from, to} + + def handle_event("load-more", _params, socket) do + batch_size = 5 + {next_batch, remaining} = Enum.split(socket.assigns.remaining_diffs, batch_size) + + socket = + socket + |> assign( + remaining_diffs: remaining, + has_more_diffs: length(remaining) > 0 + ) + + send(self(), {:load_diffs_and_update, next_batch}) + + {:noreply, socket} + end + + def handle_info({:generate_diff, package, from, to}, socket) do + case Diff.Hex.diff(package, from, to) do + {:ok, stream} -> + case process_stream_to_diffs(package, from, to, stream) do + {:ok, metadata, diff_ids} -> + initial_batch_size = 5 + {initial_diffs, remaining} = Enum.split(diff_ids, initial_batch_size) + + socket = + socket + |> assign( + metadata: metadata, + all_diff_ids: diff_ids, + loaded_diffs: initial_diffs, + remaining_diffs: remaining, + generating: false, + loading: true, + has_more_diffs: length(remaining) > 0 + ) + + send(self(), {:load_diffs, initial_diffs}) + + {:noreply, socket} + + {:error, reason} -> + Logger.error("Failed to generate diff: #{inspect(reason)}") + {:noreply, assign(socket, error: "Failed to generate diff", generating: false)} + end + + :error -> + {:noreply, assign(socket, error: "Failed to generate diff", generating: false)} + end + catch + :throw, {:diff, :invalid_diff} -> + {:noreply, assign(socket, error: "Invalid diff", generating: false)} + end + + def handle_info({:load_diffs_and_update, diff_ids}, socket) do + # Simply add new diffs to loaded_diffs - no server memory management needed + # diffs are loaded on-demand during rendering from storage + new_loaded_diffs = socket.assigns.loaded_diffs ++ diff_ids + + socket = + socket + |> assign( + loaded_diffs: new_loaded_diffs, + loading: false + ) + + {:noreply, socket} + end + + def handle_info({:load_diffs, _diff_ids}, socket) do + # With on-demand loading, we just need to mark loading as complete + # diff content will be loaded during rendering + socket = assign(socket, loading: false) + {:noreply, socket} + end + + defp process_stream_to_diffs(package, from, to, stream) do + diff_index = 0 + + metadata = %{ + total_diffs: 0, + total_additions: 0, + total_deletions: 0, + files_changed: 0 + } + + {final_metadata, diff_ids, _} = + Enum.reduce(stream, {metadata, [], diff_index}, fn element, + {acc_metadata, acc_diff_ids, index} -> + case element do + {:ok, {raw_diff, path_from, path_to}} -> + # Store raw git diff output with base paths for relative conversion + diff_id = "diff-#{index}" + + diff_data = + Jason.encode!(%{ + "diff" => DiffWeb.LiveView.sanitize_utf8(raw_diff), + "path_from" => path_from, + "path_to" => path_to + }) + + Diff.Storage.put_diff(package, from, to, diff_id, diff_data) + + # Count additions and deletions from raw diff (exclude +++ and --- headers) + lines = String.split(raw_diff, "\n") + + additions = + Enum.count(lines, fn line -> + String.starts_with?(line, "+") and not String.starts_with?(line, "+++") + end) + + deletions = + Enum.count(lines, fn line -> + String.starts_with?(line, "-") and not String.starts_with?(line, "---") + end) + + updated_metadata = %{ + acc_metadata + | total_diffs: acc_metadata.total_diffs + 1, + total_additions: acc_metadata.total_additions + additions, + total_deletions: acc_metadata.total_deletions + deletions, + files_changed: acc_metadata.files_changed + 1 + } + + {updated_metadata, acc_diff_ids ++ [diff_id], index + 1} + + {:too_large, file_path} -> + # Store raw too_large data directly + too_large_data = Jason.encode!(%{type: "too_large", file: file_path}) + diff_id = "diff-#{index}" + + Diff.Storage.put_diff(package, from, to, diff_id, too_large_data) + + updated_metadata = %{ + acc_metadata + | total_diffs: acc_metadata.total_diffs + 1, + files_changed: acc_metadata.files_changed + 1 + } + + {updated_metadata, acc_diff_ids ++ [diff_id], index + 1} + + {:error, error} -> + Logger.error( + "Failed to process diff #{index} for #{package} #{from}..#{to} with: #{inspect(error)}" + ) + + {acc_metadata, acc_diff_ids, index} + end + end) + + case Diff.Storage.put_metadata(package, from, to, final_metadata) do + :ok -> + {:ok, final_metadata, diff_ids} + + {:error, reason} -> + Logger.error("Failed to store metadata: #{inspect(reason)}") + {:error, reason} + end + catch + :throw, {:diff, :invalid_diff} -> + {:error, :invalid_diff} + end + + defp parse_versions(input) do + with {:ok, [from, to]} <- versions_from_input(input), + {:ok, from} <- parse_version(from), + {:ok, to} <- parse_version(to) do + {:ok, to_string(from), to_string(to)} + else + _ -> + :error + end + end + + defp versions_from_input(input) when is_binary(input) do + input + |> String.split("..", parts: 2) + |> case do + [from] -> + [from, ""] + + [from, to] -> + [from, to] + end + |> versions_from_input() + end + + defp versions_from_input([_from, _to] = versions) do + versions = Enum.map(versions, &String.trim/1) + {:ok, versions} + end + + defp versions_from_input(_), do: :error + + defp parse_version(""), do: {:ok, :latest} + defp parse_version(input), do: Version.parse(input) + + defp parse_diff(diff) do + case String.split(diff, ":", trim: true) do + [app, from, to] -> {app, from, to, build_url(app, from, to)} + _ -> nil + end + end + + defp build_url(app, from, to), do: "/diff/#{app}/#{from}..#{to}" +end diff --git a/lib/diff_web/live/search_view.ex b/lib/diff_web/live/search_view.ex index cd28287..82922eb 100644 --- a/lib/diff_web/live/search_view.ex +++ b/lib/diff_web/live/search_view.ex @@ -2,7 +2,60 @@ defmodule DiffWeb.SearchLiveView do use DiffWeb, :live_view def render(assigns) do - DiffWeb.SearchView.render("search.html", assigns) + ~H""" +
+
+ +
+ <%= if length(@suggestions) > 0 do %> + Did you mean: + <%= for suggestion <- @suggestions do %> + <%= suggestion %> + <% end %> + <% end %> +
+
+ <%= if length(@releases) == 1 do %> + The package only has one version so there's nothing to diff with. + <% else %> + <%= if @result do %> +
+
+
+ + + + +
+ +
+
+ <% end %> + <% end %> + <%= @not_found %> +
+ """ end def mount(_params, _session, socket) do @@ -110,6 +163,16 @@ defmodule DiffWeb.SearchLiveView do defp build_url(app, from, to), do: "/diff/#{app}/#{from}..#{to}" + # Helper functions for template + defp disabled(things) when is_list(things) do + Enum.any?(things, &(!&1)) + end + + defp disabled(thing), do: disabled([thing]) + + defp selected(x, x), do: true + defp selected(_, _), do: false + defp get_suggestions(query, number) do package_names = Diff.Package.Store.get_names() starts_with = package_starts_with(package_names, query) diff --git a/lib/diff_web/router.ex b/lib/diff_web/router.ex index c87144b..31f96f9 100644 --- a/lib/diff_web/router.ex +++ b/lib/diff_web/router.ex @@ -17,7 +17,7 @@ defmodule DiffWeb.Router do pipe_through :browser live "/", SearchLiveView - get "/diff/:package/:versions", PageController, :diff - get "/diffs", PageController, :diffs + live "/diff/:package/:versions", DiffLiveView + live "/diffs", DiffLiveView end end diff --git a/lib/diff_web/templates/live/diff.html.leex b/lib/diff_web/templates/live/diff.html.leex new file mode 100644 index 0000000..ffb3508 --- /dev/null +++ b/lib/diff_web/templates/live/diff.html.leex @@ -0,0 +1,55 @@ +<%= if assigns[:error] do %> +
+

Error

+

<%= @error %>

+
+<% else %> + <%= if assigns[:view_mode] == :diffs_list do %> +
+

Package Diffs

+
+ <%= for {package, from, to, url} <- @diffs do %> +
+ + <%= package %> (<%= from %> → <%= to %>) + +
+ <% end %> +
+
+ <% else %> +
+
+ <%= @metadata.files_changed %> files changed + +<%= @metadata.total_additions %> + -<%= @metadata.total_deletions %> +
+
+ + <%= if @generating do %> +
+
Generating diffs...
+
+ <% end %> + + <%= if @loading do %> +
+
Loading diffs...
+
+ <% end %> + +
+ <%= for diff_id <- @loaded_diffs do %> +
+ <%= raw(load_diff_content(@package, @from, @to, diff_id)) %> +
+ <% end %> + + <%= if @has_more_diffs do %> +
+ Loading more diffs... +
+ <% end %> +
+ <% end %> +<% end %> diff --git a/lib/diff_web/templates/render/patch.html.eex b/lib/diff_web/templates/render/patch.html.eex deleted file mode 100644 index b5414e7..0000000 --- a/lib/diff_web/templates/render/patch.html.eex +++ /dev/null @@ -1,41 +0,0 @@ -<% status = patch_status(@patch) %> -
-
- - <%= status %> - - <%= file_header(@patch, status) %> - - -
-
- - <%= for chunk <- @patch.chunks do %> - - - - - <%= for line <- chunk.lines do %> - - - - - <% end %> - <% end %> -
-
 
-
-
-
<%= chunk.header %>
-
-
- <%= line_number(line.from_line_number) %> -
-
- <%= line_number(line.to_line_number) %> -
-
-
<%= line_text(line.text) %>
-
-
-
diff --git a/lib/diff_web/templates/render/too_large.html.eex b/lib/diff_web/templates/render/too_large.html.eex deleted file mode 100644 index 14aba57..0000000 --- a/lib/diff_web/templates/render/too_large.html.eex +++ /dev/null @@ -1,13 +0,0 @@ -
-
- - unknown - - <%= @file %> - - -
-
- CANNOT RENDER FILES LARGER THAN 1MB -
-
diff --git a/lib/diff_web/templates/search/search.html.leex b/lib/diff_web/templates/search/search.html.leex deleted file mode 100644 index 4ee93eb..0000000 --- a/lib/diff_web/templates/search/search.html.leex +++ /dev/null @@ -1,54 +0,0 @@ - -
-
- -
- <%= if length(@suggestions) > 0 do %> - Did you mean: - <%= for suggestion <- @suggestions do %> - <%= suggestion %> - <% end %> - <% end %> -
-
- <%= if length(@releases) == 1 do %> - The package only has one version so there's nothing to diff with. - <% else %> - <%= if @result do %> -
-
-
- - - - -
- - -
-
- <% end %> - <% end %> - <%= @not_found %> -
diff --git a/lib/diff_web/views/live_view.ex b/lib/diff_web/views/live_view.ex new file mode 100644 index 0000000..bccae08 --- /dev/null +++ b/lib/diff_web/views/live_view.ex @@ -0,0 +1,52 @@ +defmodule DiffWeb.LiveView do + use DiffWeb, :view + require Logger + + def load_diff_content(package, from, to, diff_id) do + case Diff.Storage.get_diff(package, from, to, diff_id) do + {:ok, raw_content} -> + # Parse the stored content based on format + case Jason.decode(raw_content) do + {:ok, %{"type" => "too_large", "file" => file_path}} -> + DiffWeb.TooLargeComponent.render(%{file: file_path}) + |> Phoenix.HTML.Safe.to_iodata() + |> IO.iodata_to_binary() + + {:ok, %{"diff" => raw_diff, "path_from" => path_from, "path_to" => path_to}} -> + case GitDiff.parse_patch(raw_diff, relative_from: path_from, relative_to: path_to) do + {:ok, []} -> + "
No changes in diff
" + + {:ok, diffs} -> + # Take the first diff (should only be one per file) + diff = List.first(diffs) + + DiffWeb.DiffComponent.render(%{diff: diff}) + |> Phoenix.HTML.Safe.to_iodata() + |> IO.iodata_to_binary() + |> sanitize_utf8() + + {:error, reason} -> + Logger.error("Failed to parse diff #{diff_id}: #{inspect(reason)}") + "
Failed to parse diff
" + end + end + + {:error, _reason} -> + "
Failed to load diff
" + end + end + + def sanitize_utf8(content) when is_binary(content) do + content + |> String.chunk(:valid) + |> Enum.map(fn chunk -> + if String.valid?(chunk) do + chunk + else + String.duplicate("?", byte_size(chunk)) + end + end) + |> Enum.join("") + end +end diff --git a/lib/diff_web/views/page_view.ex b/lib/diff_web/views/page_view.ex deleted file mode 100644 index 688d611..0000000 --- a/lib/diff_web/views/page_view.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule DiffWeb.PageView do - use DiffWeb, :view -end diff --git a/lib/diff_web/views/render_view.ex b/lib/diff_web/views/render_view.ex deleted file mode 100644 index 7874757..0000000 --- a/lib/diff_web/views/render_view.ex +++ /dev/null @@ -1,51 +0,0 @@ -defmodule DiffWeb.RenderView do - use DiffWeb, :view - - def file_header(patch, status) do - from = patch.from - to = patch.to - - case status do - "changed" -> from - "renamed" -> "#{from} -> #{to}" - "removed" -> from - "added" -> to - end - end - - def patch_status(patch) do - from = patch.from - to = patch.to - - cond do - !from -> "added" - !to -> "removed" - from == to -> "changed" - true -> "renamed" - end - end - - def line_number(ln) when is_nil(ln), do: "" - def line_number(ln), do: to_string(ln) - - def line_id(patch, line) do - hash = :erlang.phash2({patch.from, patch.to}) - - ln = "-#{line.from_line_number}-#{line.to_line_number}" - - [to_string(hash), ln] - end - - def line_type(line), do: to_string(line.type) - - def line_text("+" <> text), - do: [content_tag(:span, "+ ", class: "ghd-line-status"), content_tag(:span, text)] - - def line_text("-" <> text), - do: [content_tag(:span, "- ", class: "ghd-line-status"), content_tag(:span, text)] - - def line_text(" " <> text), - do: [content_tag(:span, " ", class: "ghd-line-status"), content_tag(:span, text)] - - def line_text(text), do: [content_tag(:span, text)] -end diff --git a/lib/diff_web/views/search_view.ex b/lib/diff_web/views/search_view.ex deleted file mode 100644 index 45742f2..0000000 --- a/lib/diff_web/views/search_view.ex +++ /dev/null @@ -1,16 +0,0 @@ -defmodule DiffWeb.SearchView do - use DiffWeb, :view - - def disabled(things) when is_list(things) do - if Enum.any?(things, &(!&1)) do - "disabled" - else - "" - end - end - - def disabled(thing), do: disabled([thing]) - - def selected(x, x), do: "selected=selected" - def selected(_, _), do: "" -end diff --git a/mix.exs b/mix.exs index 43771a3..6f97773 100644 --- a/mix.exs +++ b/mix.exs @@ -8,6 +8,7 @@ defmodule Diff.MixProject do elixir: "~> 1.18", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, + listeners: [Phoenix.CodeReloader], aliases: aliases(), releases: releases(), deps: deps() @@ -33,7 +34,6 @@ defmodule Diff.MixProject do # Type `mix help deps` for examples and options. defp deps do [ - {:floki, "~> 0.37.0"}, {:gettext, "~> 0.11"}, {:git_diff, github: "ericmj/git_diff", branch: "ericmj/fix-modes"}, {:goth, "~> 1.0"}, @@ -50,7 +50,9 @@ defmodule Diff.MixProject do {:phoenix_view, "~> 2.0"}, {:phoenix, "~> 1.6"}, {:plug_cowboy, "~> 2.1"}, - {:sentry, "~> 10.8"} + {:sentry, "~> 10.8"}, + {:floki, "~> 0.37.0", only: :test}, + {:lazy_html, ">= 0.1.0", only: :test} ] end diff --git a/mix.lock b/mix.lock index 062f1f0..be0ad07 100644 --- a/mix.lock +++ b/mix.lock @@ -1,48 +1,51 @@ %{ - "castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"}, - "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, - "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, + "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, + "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, + "cowboy": {:hex, :cowboy, "2.13.0", "09d770dd5f6a22cc60c071f432cd7cb87776164527f205c5a6b0f24ff6b38990", [:make, :rebar3], [{:cowlib, ">= 2.14.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "e724d3a70995025d654c1992c7b11dbfea95205c047d86ff9bf1cda92ddc5614"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, - "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, + "cowlib": {:hex, :cowlib, "2.15.0", "3c97a318a933962d1c12b96ab7c1d728267d2c523c25a5b57b0f93392b6e9e25", [:make, :rebar3], [], "hexpm", "4f00c879a64b4fe7c8fcb42a4281925e9ffdb928820b03c3ad325a617e857532"}, + "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, - "floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"}, + "fine": {:hex, :fine, "0.1.4", "b19a89c1476c7c57afb5f9314aed5960b5bc95d5277de4cb5ee8e1d1616ce379", [:mix], [], "hexpm", "be3324cc454a42d80951cf6023b9954e9ff27c6daa255483b3e8d608670303f5"}, + "floki": {:hex, :floki, "0.37.1", "d7aaee758c8a5b4a7495799a4260754fec5530d95b9c383c03b27359dea117cf", [:mix], [], "hexpm", "673d040cb594d31318d514590246b6dd587ed341d3b67e17c1c0eb8ce7ca6f04"}, "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"}, "git_diff": {:git, "https://github.com/ericmj/git_diff.git", "e4ee06cfd139b8a911d07e42d0ff3b15eee2b740", [branch: "ericmj/fix-modes"]}, "goth": {:hex, :goth, "1.4.3", "80e86225ae174844e6a77b61982fafadfc715277db465e0956348d8bdd56b231", [:mix], [{:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:jose, "~> 1.11", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "34e2012ed1af2fe2446083da60a988fd9829943d30e4447832646c3a6863a7e6"}, - "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, + "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, "hex_core": {:hex, :hex_core, "0.11.0", "d1c6bbf2a4ee6b5f002bec6fa52b5080c53c8b63b7caf6eb88b943687547bff4", [:rebar3], [], "hexpm", "707893677a425491962a2db522f1d2b1f85f97ea27418b06f7929f1d30cde0b0"}, "hpax": {:hex, :hpax, "1.0.1", "c857057f89e8bd71d97d9042e009df2a42705d6d690d54eca84c8b29af0787b0", [:mix], [], "hexpm", "4e2d5a4f76ae1e3048f35ae7adb1641c36265510a2d4638157fbcb53dda38445"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, + "lazy_html": {:hex, :lazy_html, "0.1.6", "bff2c5901b008fd75d41f777eb54a19fcf47544cc8c5e5509d84c2b3ea471c69", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9.0", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "e04bddfaa09d38e5c3e39278a470550faa7d45d0a30ebc87eb2bd740c364aaaa"}, "logster": {:hex, :logster, "1.0.2", "9a1f8987ac46a4227b7ee15c8097d9a5d3b9f55befbd2c44f06caefa4addacf0", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ec6c5fa0e2a180526e86083c9b33507181103b63b967ac47c52ec4f54c9b828b"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, - "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"}, "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, "mox": {:hex, :mox, "1.2.0", "a2cd96b4b80a3883e3100a221e8adc1b98e4c3a332a8fc434c39526babafd5b3", [:mix], [{:nimble_ownership, "~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}], "hexpm", "c7b92b3cc69ee24a7eeeaf944cd7be22013c52fcb580c1f33f50845ec821089a"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_ownership": {:hex, :nimble_ownership, "1.0.1", "f69fae0cdd451b1614364013544e66e4f5d25f36a2056a9698b793305c5aa3a6", [:mix], [], "hexpm", "3825e461025464f519f3f3e4a1f9b68c47dc151369611629ad08b636b73bb22d"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, - "phoenix": {:hex, :phoenix, "1.7.18", "5310c21443514be44ed93c422e15870aef254cf1b3619e4f91538e7529d2b2e4", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "1797fcc82108442a66f2c77a643a62980f342bfeb63d6c9a515ab8294870004e"}, - "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"}, + "phoenix": {:hex, :phoenix, "1.8.0", "dc5d256bb253110266ded8c4a6a167e24fabde2e14b8e474d262840ae8d8ea18", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "15f6e9cb76646ad8d9f2947240519666fc2c4f29f8a93ad9c7664916ab4c167b"}, + "phoenix_html": {:hex, :phoenix_html, "4.2.1", "35279e2a39140068fc03f8874408d58eef734e488fc142153f055c5454fd1c08", [:mix], [], "hexpm", "cff108100ae2715dd959ae8f2a8cef8e20b593f8dfd031c9cba92702cf23e053"}, "phoenix_html_helpers": {:hex, :phoenix_html_helpers, "1.0.1", "7eed85c52eff80a179391036931791ee5d2f713d76a81d0d2c6ebafe1e11e5ec", [:mix], [{:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cffd2385d1fa4f78b04432df69ab8da63dc5cf63e07b713a4dcf36a3740e3090"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.1", "5389a30658176c0de816636ce276567478bffd063c082515a6e8368b8fc9a0db", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c0f517e6f290f10dbb94343ac22e0109437fb1fa6f0696e7c73967b789c1c285"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.8", "d283d5e047e6c013182a3833e99ff33942e3a8076f9f984c337ea04cc53e8206", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6184cf1e82fe6627d40cfa62236133099438513710d30358f4c085c16ecb84b4"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"}, - "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.7.2", "fdadb973799ae691bf9ecad99125b16625b1c6039999da5fe544d99218e662e4", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "245d8a11ee2306094840c000e8816f0cbed69a23fc0ac2bcf8d7835ae019bb2f"}, - "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, - "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, - "sentry": {:hex, :sentry, "10.8.1", "aa45309785e1521416225adb16e0b4d8b957578804527f3c7babb6fefbc5e456", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0 or ~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "495b3cdadad90ba72eef973aa3dec39b3b8b2a362fe87e2f4ef32133ac3b4097"}, + "plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, + "ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"}, + "sentry": {:hex, :sentry, "10.10.0", "d058b635f3796947545c8057a42996f6dbefd12152da947209b56d16af41b161", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0 or ~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "7c7ddd3cfdd63fcee53b1e28f9a653037e6927b2b1dbd300b7aeee9687c7a8f6"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"}, } diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index d6f47fa..a3a94c9 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -7,4 +7,3 @@ ## Run `mix gettext.extract` to bring this file up to ## date. Leave `msgstr`s empty as changing them here has no ## effect: edit them in PO (`.po`) files instead. - diff --git a/test/diff/storage/local_test.exs b/test/diff/storage/local_test.exs new file mode 100644 index 0000000..900f6e1 --- /dev/null +++ b/test/diff/storage/local_test.exs @@ -0,0 +1,88 @@ +defmodule Diff.Storage.LocalTest do + use ExUnit.Case + + alias Diff.Storage.Local + + setup do + # Use a temporary directory for tests + tmp_dir = System.tmp_dir!() |> Path.join("diff_test_#{:rand.uniform(10000)}") + File.mkdir_p!(tmp_dir) + + Application.put_env(:diff, :tmp_dir, tmp_dir) + Application.put_env(:diff, :cache_version, 1) + + on_exit(fn -> + File.rm_rf!(tmp_dir) + end) + + %{tmp_dir: tmp_dir} + end + + describe "diff storage" do + test "stores and retrieves diffs", %{tmp_dir: _tmp_dir} do + diff_html = "
Test diff
" + + # Store the diff + assert :ok = Local.put_diff("phoenix", "1.4.5", "1.4.9", "diff-0", diff_html) + + # Retrieve the diff + assert {:ok, ^diff_html} = Local.get_diff("phoenix", "1.4.5", "1.4.9", "diff-0") + end + + test "handles non-existent diffs" do + assert {:error, :not_found} = Local.get_diff("phoenix", "1.4.5", "1.4.9", "nonexistent") + end + + test "lists stored diffs" do + diffs = [ + {"diff-0", "
diff 0
"}, + {"diff-1", "
diff 1
"}, + {"diff-2", "
diff 2
"} + ] + + # Store diffs + for {diff_id, content} <- diffs do + assert :ok = Local.put_diff("phoenix", "1.4.5", "1.4.9", diff_id, content) + end + + # Store metadata (required for list_diffs to work) + metadata = %{ + total_diffs: 3, + total_additions: 10, + total_deletions: 5, + files_changed: 3 + } + + assert :ok = Local.put_metadata("phoenix", "1.4.5", "1.4.9", metadata) + + # List diffs + assert {:ok, diff_ids} = Local.list_diffs("phoenix", "1.4.5", "1.4.9") + assert Enum.sort(diff_ids) == ["diff-0", "diff-1", "diff-2"] + end + + test "handles empty diff directory" do + assert {:ok, []} = Local.list_diffs("phoenix", "1.4.5", "1.4.9") + end + end + + describe "metadata storage" do + test "stores and retrieves metadata" do + metadata = %{ + total_diffs: 5, + total_additions: 123, + total_deletions: 45, + files_changed: 8 + } + + # Store metadata + assert :ok = Local.put_metadata("phoenix", "1.4.5", "1.4.9", metadata) + + # Retrieve metadata + assert {:ok, ^metadata} = Local.get_metadata("phoenix", "1.4.5", "1.4.9") + end + + test "handles non-existent metadata" do + assert {:error, :not_found} = Local.get_metadata("phoenix", "1.4.5", "1.4.9") + end + end +end diff --git a/test/diff_web/controllers/page_controller_test.exs b/test/diff_web/controllers/page_controller_test.exs deleted file mode 100644 index 5007c1d..0000000 --- a/test/diff_web/controllers/page_controller_test.exs +++ /dev/null @@ -1,58 +0,0 @@ -defmodule DiffWeb.PageControllerTest do - use DiffWeb.ConnCase - import Mox - - test "GET /", %{conn: conn} do - conn = get(conn, "/") - assert html_response(conn, 200) =~ "" - end - - describe "GET /diff/:package/:versions" do - setup :verify_on_exit! - - test "diff", %{conn: conn} do - diff = "p403n1xd1ff" - - expect(Diff.StorageMock, :get, fn "phoenix", "1.4.5", "1.4.9" -> {:ok, [diff]} end) - - conn = get(conn, "/diff/phoenix/1.4.5..1.4.9") - assert html_response(conn, 200) =~ diff - end - - test "accepts implicit to", %{conn: conn} do - versions = ["1.4.5", "1.4.9", "1.5.0-rc.2"] - diff = "p403n1xd1ff" - - expect(Diff.Package.StoreMock, :get_versions, fn "phoenix" -> {:ok, versions} end) - expect(Diff.StorageMock, :get, fn "phoenix", "1.4.5", "1.4.9" -> {:ok, [diff]} end) - - conn = get(conn, "/diff/phoenix/1.4.5") - assert html_response(conn, 200) =~ diff - end - - test "does not accept implicit from", %{conn: conn} do - conn = get(conn, "/diff/phoenix/%20..") - assert html_response(conn, 400) =~ "Bad request" - end - - test "does not accept too many version parts", %{conn: conn} do - conn = get(conn, "/diff/phoenix/1.4.5..1.4.7..1.4.9") - assert html_response(conn, 400) =~ "Bad request" - end - end - - describe "GET /diffs" do - setup :verify_on_exit! - - test "shows all diffs in list", %{conn: conn} do - diff = "/diff/phoenix/1.4.5..1.4.9" - conn = get(conn, "/diffs?diffs[]=phoenix:1.4.5:1.4.9") - assert html_response(conn, 200) =~ diff - end - - test "does not accept an empty list of diffs", %{conn: conn} do - conn = get(conn, "/diffs") - assert html_response(conn, 400) =~ "Bad request" - end - end -end diff --git a/test/diff_web/integration_test.exs b/test/diff_web/integration_test.exs new file mode 100644 index 0000000..aac7e67 --- /dev/null +++ b/test/diff_web/integration_test.exs @@ -0,0 +1,54 @@ +defmodule DiffWeb.IntegrationTest do + use DiffWeb.ConnCase + import Phoenix.LiveViewTest + import Mox + + describe "route integration tests" do + setup :verify_on_exit! + + test "root route renders search page", %{conn: conn} do + {:ok, _view, html} = live(conn, "/") + assert html =~ ~s(class="search-input") + assert html =~ "Search..." + end + + test "/diffs route with query parameters", %{conn: conn} do + {:ok, _view, html} = live(conn, "/diffs?diffs[]=phoenix:1.4.5:1.4.9") + + assert html =~ "Package Diffs" + assert html =~ "phoenix" + assert html =~ "1.4.5 → 1.4.9" + end + + test "/diff with implicit latest version resolution", %{conn: conn} do + versions = ["1.4.5", "1.4.9", "1.5.0-rc.2"] + + Diff.Package.StoreMock + |> stub(:get_versions, fn "phoenix" -> {:ok, versions} end) + + Diff.StorageMock + |> stub(:get_metadata, fn "phoenix", "1.4.5", "1.4.9" -> + {:error, :not_found} + end) + + {:ok, _view, html} = live(conn, "/diff/phoenix/1.4.5..") + + # Should show generating state since we're resolving to latest version + assert html =~ "Generating diffs" + end + + test "/diff handles package not found", %{conn: conn} do + Diff.Package.StoreMock + |> stub(:get_versions, fn "nonexistent" -> {:error, :not_found} end) + + Diff.StorageMock + |> stub(:get_metadata, fn "nonexistent", "1.0.0", "2.0.0" -> + {:error, :not_found} + end) + + {:ok, _view, html} = live(conn, "/diff/nonexistent/1.0.0..2.0.0") + + assert html =~ "Generating diffs" + end + end +end diff --git a/test/diff_web/live/diff_live_view_test.exs b/test/diff_web/live/diff_live_view_test.exs new file mode 100644 index 0000000..997675e --- /dev/null +++ b/test/diff_web/live/diff_live_view_test.exs @@ -0,0 +1,96 @@ +defmodule DiffWeb.DiffLiveViewTest do + use DiffWeb.ConnCase + import Phoenix.LiveViewTest + import Mox + + setup :verify_on_exit! + + describe "DiffLiveView single diff" do + test "mounts successfully with valid package and versions", %{conn: conn} do + metadata = %{ + total_diffs: 3, + total_additions: 45, + total_deletions: 12, + files_changed: 8 + } + + diffs_ids = ["diffs-0", "diffs-1", "diffs-2"] + + # Mock diffs content - simplified diff format + diff_content = + Jason.encode!(%{ + "diff" => + "diff --git a/test.ex b/test.ex\n--- a/test.ex\n+++ b/test.ex\n@@ -1,3 +1,4 @@\n+new line\n old line", + "path_from" => "/tmp/from", + "path_to" => "/tmp/to" + }) + + Diff.StorageMock + |> stub(:get_metadata, fn "phoenix", "1.4.5", "1.4.9" -> + {:ok, metadata} + end) + |> stub(:list_diffs, fn "phoenix", "1.4.5", "1.4.9" -> + {:ok, diffs_ids} + end) + |> stub(:get_diff, fn "phoenix", "1.4.5", "1.4.9", _diff_id -> + {:ok, diff_content} + end) + + {:ok, _view, html} = live(conn, "/diff/phoenix/1.4.5..1.4.9") + + assert html =~ "phoenix" + assert html =~ "1.4.5" + assert html =~ "1.4.9" + assert html =~ "files changed" + assert html =~ "8" + assert html =~ "+45" + assert html =~ "-12" + end + + test "shows generating state when metadata not found", %{conn: conn} do + Diff.StorageMock + |> stub(:get_metadata, fn "phoenix", "1.4.5", "1.4.9" -> + {:error, :not_found} + end) + + {:ok, _view, html} = live(conn, "/diff/phoenix/1.4.5..1.4.9") + + # Should show generating state when metadata is not found + assert html =~ "Generating diffs" + end + + test "handles invalid version format", %{conn: conn} do + {:ok, _view, html} = live(conn, "/diff/phoenix/invalid..version..format") + assert html =~ "Invalid version format" + end + end + + describe "DiffLiveView diffs list" do + test "shows list of diffs", %{conn: conn} do + {:ok, _view, html} = + live(conn, "/diffs?diffs[]=phoenix:1.4.5:1.4.9&diffs[]=plug:1.0.0:1.1.0") + + assert html =~ "Package Diffs" + assert html =~ "phoenix" + assert html =~ "1.4.5 → 1.4.9" + assert html =~ "plug" + assert html =~ "1.0.0 → 1.1.0" + assert html =~ "/diff/phoenix/1.4.5..1.4.9" + assert html =~ "/diff/plug/1.0.0..1.1.0" + end + + test "handles empty diffs list", %{conn: conn} do + {:ok, _view, html} = live(conn, "/diffs") + assert html =~ "No diffs provided" + end + + test "filters invalid diffs", %{conn: conn} do + {:ok, _view, html} = live(conn, "/diffs?diffs[]=phoenix:1.4.5:1.4.9&diffs[]=invalid") + + assert html =~ "Package Diffs" + assert html =~ "phoenix" + assert html =~ "1.4.5 → 1.4.9" + refute html =~ "invalid" + end + end +end diff --git a/test/diff_web/live/search_view_test.exs b/test/diff_web/live/search_view_test.exs index d247924..da09f36 100644 --- a/test/diff_web/live/search_view_test.exs +++ b/test/diff_web/live/search_view_test.exs @@ -42,9 +42,9 @@ defmodule DiffWeb.SearchLiveViewTest do send(view.pid, {:search, "phoenix"}) rendered = render(view) assert rendered =~ ~s(1.4.11) + assert rendered =~ ~s() assert rendered =~ ~s(Did you mean:) assert rendered =~ diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index bf764cc..65c4c1e 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -28,6 +28,12 @@ defmodule DiffWeb.ConnCase do end setup _tags do - {:ok, conn: Phoenix.ConnTest.build_conn()} + conn = + Phoenix.ConnTest.build_conn() + |> Plug.Conn.put_private(:phoenix_router, DiffWeb.Router) + |> Plug.Conn.put_private(:phoenix_endpoint, DiffWeb.Endpoint) + |> Phoenix.ConnTest.init_test_session(%{}) + + {:ok, conn: conn} end end From a2604a7e1b697aebcd22c6ded2b4559a592376c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Meadows-J=C3=B6nsson?= Date: Fri, 22 Aug 2025 11:51:55 +0200 Subject: [PATCH 2/7] Add ca-certificates --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 15db64d..af1ee3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,7 +48,7 @@ FROM debian:${DEBIAN_VERSION} AS app RUN apt update && \ apt upgrade -y && \ - apt install --no-install-recommends -y bash openssl git && \ + apt install --no-install-recommends -y bash openssl git ca-certificates && \ apt clean -y && rm -rf /var/lib/apt/lists/* RUN mkdir /app From 206d8483b960519a7a2d78fc626e24657791113e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Meadows-J=C3=B6nsson?= Date: Fri, 22 Aug 2025 12:01:36 +0200 Subject: [PATCH 3/7] Check if we're still at the bottom after loading content --- assets/js/app.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/assets/js/app.js b/assets/js/app.js index f01d730..ff4282e 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -44,6 +44,19 @@ window.Hooks.InfiniteScroll = { updated() { this.pending = false + + // Check if we're still at the bottom after loading content + // Use requestAnimationFrame to ensure DOM has fully updated + requestAnimationFrame(() => { + const target = this.el + const rect = target.getBoundingClientRect() + const isIntersecting = rect.top <= (window.innerHeight || document.documentElement.clientHeight) + + if (isIntersecting && !this.pending) { + this.pending = true + this.pushEvent("load-more", {}) + } + }) } } From 5da518075496692b2cc515dac0bf75c34d19e6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Meadows-J=C3=B6nsson?= Date: Fri, 22 Aug 2025 13:04:08 +0200 Subject: [PATCH 4/7] Process in parallel --- lib/diff/application.ex | 8 +- lib/diff_web/live/diff_live_view.ex | 198 +++++++++++++++------ lib/diff_web/templates/live/diff.html.leex | 2 +- 3 files changed, 144 insertions(+), 64 deletions(-) diff --git a/lib/diff/application.ex b/lib/diff/application.ex index 513ec80..44df1a7 100644 --- a/lib/diff/application.ex +++ b/lib/diff/application.ex @@ -12,13 +12,9 @@ defmodule Diff.Application do children = [ goth_spec(), {Task.Supervisor, name: Diff.Tasks}, - # Start the PubSub system {Phoenix.PubSub, name: Diff.PubSub}, - # Start the endpoint when the application starts - DiffWeb.Endpoint, - # Starts a worker by calling: Diff.Worker.start_link(arg) - # {Diff.Worker, arg}, - Diff.Package.Supervisor + Diff.Package.Supervisor, + DiffWeb.Endpoint ] # See https://hexdocs.pm/elixir/Supervisor.html diff --git a/lib/diff_web/live/diff_live_view.ex b/lib/diff_web/live/diff_live_view.ex index 4a8a5dc..ff11ab0 100644 --- a/lib/diff_web/live/diff_live_view.ex +++ b/lib/diff_web/live/diff_live_view.ex @@ -82,6 +82,7 @@ defmodule DiffWeb.DiffLiveView do metadata: metadata, all_diff_ids: diff_ids, loaded_diffs: initial_diffs, + loaded_diff_content: %{}, remaining_diffs: remaining, loading: true, generating: false, @@ -103,6 +104,7 @@ defmodule DiffWeb.DiffLiveView do metadata: %{files_changed: 0, total_additions: 0, total_deletions: 0}, all_diff_ids: [], loaded_diffs: [], + loaded_diff_content: %{}, remaining_diffs: [], loading: false, generating: true, @@ -162,6 +164,7 @@ defmodule DiffWeb.DiffLiveView do metadata: metadata, all_diff_ids: diff_ids, loaded_diffs: initial_diffs, + loaded_diff_content: %{}, remaining_diffs: remaining, generating: false, loading: true, @@ -186,54 +189,114 @@ defmodule DiffWeb.DiffLiveView do end def handle_info({:load_diffs_and_update, diff_ids}, socket) do - # Simply add new diffs to loaded_diffs - no server memory management needed - # diffs are loaded on-demand during rendering from storage + # Load diffs in parallel and add to existing loaded content + new_loaded_content = + load_diffs_in_parallel( + socket.assigns.package, + socket.assigns.from, + socket.assigns.to, + diff_ids + ) + + existing_content = Map.get(socket.assigns, :loaded_diff_content, %{}) + all_loaded_content = Map.merge(existing_content, new_loaded_content) + new_loaded_diffs = socket.assigns.loaded_diffs ++ diff_ids socket = socket |> assign( loaded_diffs: new_loaded_diffs, + loaded_diff_content: all_loaded_content, loading: false ) {:noreply, socket} end - def handle_info({:load_diffs, _diff_ids}, socket) do - # With on-demand loading, we just need to mark loading as complete - # diff content will be loaded during rendering - socket = assign(socket, loading: false) + def handle_info({:load_diffs, diff_ids}, socket) do + # Load initial diffs in parallel + loaded_content = + load_diffs_in_parallel( + socket.assigns.package, + socket.assigns.from, + socket.assigns.to, + diff_ids + ) + + socket = + socket + |> assign( + loaded_diff_content: loaded_content, + loading: false + ) + {:noreply, socket} end defp process_stream_to_diffs(package, from, to, stream) do - diff_index = 0 - - metadata = %{ + initial_metadata = %{ total_diffs: 0, total_additions: 0, total_deletions: 0, files_changed: 0 } - {final_metadata, diff_ids, _} = - Enum.reduce(stream, {metadata, [], diff_index}, fn element, - {acc_metadata, acc_diff_ids, index} -> - case element do - {:ok, {raw_diff, path_from, path_to}} -> - # Store raw git diff output with base paths for relative conversion - diff_id = "diff-#{index}" + # Process stream elements in parallel with indices + results = + stream + |> Stream.with_index() + |> Task.async_stream( + Diff.Tasks, + fn {element, index} -> + process_stream_element(package, from, to, element, index) + end, + max_concurrency: 10, + timeout: 30_000, + ordered: true + ) + |> Enum.reduce({initial_metadata, []}, fn + {:ok, {:ok, diff_id, metadata_update}}, {acc_metadata, acc_diff_ids} -> + updated_metadata = merge_metadata(acc_metadata, metadata_update) + {updated_metadata, acc_diff_ids ++ [diff_id]} + + {:ok, {:error, _}}, acc -> + acc + + {:error, _}, acc -> + acc + end) + + case results do + {final_metadata, diff_ids} -> + case Diff.Storage.put_metadata(package, from, to, final_metadata) do + :ok -> + {:ok, final_metadata, diff_ids} + + {:error, reason} -> + Logger.error("Failed to store metadata: #{inspect(reason)}") + {:error, reason} + end + end + catch + :throw, {:diff, :invalid_diff} -> + {:error, :invalid_diff} + end - diff_data = - Jason.encode!(%{ - "diff" => DiffWeb.LiveView.sanitize_utf8(raw_diff), - "path_from" => path_from, - "path_to" => path_to - }) + defp process_stream_element(package, from, to, element, index) do + case element do + {:ok, {raw_diff, path_from, path_to}} -> + diff_id = "diff-#{index}" - Diff.Storage.put_diff(package, from, to, diff_id, diff_data) + diff_data = + Jason.encode!(%{ + "diff" => DiffWeb.LiveView.sanitize_utf8(raw_diff), + "path_from" => path_from, + "path_to" => path_to + }) + case Diff.Storage.put_diff(package, from, to, diff_id, diff_data) do + :ok -> # Count additions and deletions from raw diff (exclude +++ and --- headers) lines = String.split(raw_diff, "\n") @@ -247,51 +310,56 @@ defmodule DiffWeb.DiffLiveView do String.starts_with?(line, "-") and not String.starts_with?(line, "---") end) - updated_metadata = %{ - acc_metadata - | total_diffs: acc_metadata.total_diffs + 1, - total_additions: acc_metadata.total_additions + additions, - total_deletions: acc_metadata.total_deletions + deletions, - files_changed: acc_metadata.files_changed + 1 + metadata_update = %{ + total_diffs: 1, + total_additions: additions, + total_deletions: deletions, + files_changed: 1 } - {updated_metadata, acc_diff_ids ++ [diff_id], index + 1} + {:ok, diff_id, metadata_update} - {:too_large, file_path} -> - # Store raw too_large data directly - too_large_data = Jason.encode!(%{type: "too_large", file: file_path}) - diff_id = "diff-#{index}" - - Diff.Storage.put_diff(package, from, to, diff_id, too_large_data) + {:error, reason} -> + Logger.error("Failed to store diff #{diff_id}: #{inspect(reason)}") + {:error, reason} + end - updated_metadata = %{ - acc_metadata - | total_diffs: acc_metadata.total_diffs + 1, - files_changed: acc_metadata.files_changed + 1 + {:too_large, file_path} -> + diff_id = "diff-#{index}" + too_large_data = Jason.encode!(%{type: "too_large", file: file_path}) + + case Diff.Storage.put_diff(package, from, to, diff_id, too_large_data) do + :ok -> + metadata_update = %{ + total_diffs: 1, + total_additions: 0, + total_deletions: 0, + files_changed: 1 } - {updated_metadata, acc_diff_ids ++ [diff_id], index + 1} + {:ok, diff_id, metadata_update} - {:error, error} -> - Logger.error( - "Failed to process diff #{index} for #{package} #{from}..#{to} with: #{inspect(error)}" - ) - - {acc_metadata, acc_diff_ids, index} + {:error, reason} -> + Logger.error("Failed to store too_large diff #{diff_id}: #{inspect(reason)}") + {:error, reason} end - end) - case Diff.Storage.put_metadata(package, from, to, final_metadata) do - :ok -> - {:ok, final_metadata, diff_ids} + {:error, error} -> + Logger.error( + "Failed to process diff #{index} for #{package} #{from}..#{to} with: #{inspect(error)}" + ) - {:error, reason} -> - Logger.error("Failed to store metadata: #{inspect(reason)}") - {:error, reason} + {:error, error} end - catch - :throw, {:diff, :invalid_diff} -> - {:error, :invalid_diff} + end + + defp merge_metadata(acc_metadata, update) do + %{ + total_diffs: acc_metadata.total_diffs + update.total_diffs, + total_additions: acc_metadata.total_additions + update.total_additions, + total_deletions: acc_metadata.total_deletions + update.total_deletions, + files_changed: acc_metadata.files_changed + update.files_changed + } end defp parse_versions(input) do @@ -336,4 +404,20 @@ defmodule DiffWeb.DiffLiveView do end defp build_url(app, from, to), do: "/diff/#{app}/#{from}..#{to}" + + defp load_diffs_in_parallel(package, from, to, diff_ids) do + diff_ids + |> Task.async_stream( + Diff.Tasks, + fn diff_id -> + {diff_id, DiffWeb.LiveView.load_diff_content(package, from, to, diff_id)} + end, + max_concurrency: 10, + timeout: 30_000 + ) + |> Enum.reduce(%{}, fn + {:ok, {diff_id, content}}, acc -> Map.put(acc, diff_id, content) + {:error, _}, acc -> acc + end) + end end diff --git a/lib/diff_web/templates/live/diff.html.leex b/lib/diff_web/templates/live/diff.html.leex index ffb3508..68eeb8c 100644 --- a/lib/diff_web/templates/live/diff.html.leex +++ b/lib/diff_web/templates/live/diff.html.leex @@ -41,7 +41,7 @@
<%= for diff_id <- @loaded_diffs do %>
- <%= raw(load_diff_content(@package, @from, @to, diff_id)) %> + <%= raw(Map.get(@loaded_diff_content, diff_id, "
Diff content not loaded
")) %>
<% end %> From a7ef0d88064e2e98e27ec7a6e4b5375520877484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Meadows-J=C3=B6nsson?= Date: Fri, 22 Aug 2025 20:50:57 +0200 Subject: [PATCH 5/7] Fix Task.async_stream --- config/test.exs | 3 +- lib/diff/hex/behaviour.ex | 5 + lib/diff/hex/hex.ex | 2 + lib/diff_web/live/diff_live_view.ex | 15 +- test/diff_web/live/diff_live_view_test.exs | 175 +++++++++++++++++++++ test/test_helper.exs | 1 + 6 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 lib/diff/hex/behaviour.ex diff --git a/config/test.exs b/config/test.exs index 743ec6a..c934ed9 100644 --- a/config/test.exs +++ b/config/test.exs @@ -11,4 +11,5 @@ config :logger, level: :warning config :diff, package_store_impl: Diff.Package.StoreMock, - storage_impl: Diff.StorageMock + storage_impl: Diff.StorageMock, + hex_impl: Diff.HexMock diff --git a/lib/diff/hex/behaviour.ex b/lib/diff/hex/behaviour.ex new file mode 100644 index 0000000..8744912 --- /dev/null +++ b/lib/diff/hex/behaviour.ex @@ -0,0 +1,5 @@ +defmodule Diff.Hex.Behaviour do + @callback diff(package :: String.t(), from :: String.t(), to :: String.t()) :: + {:ok, Enumerable.t()} | :error +end + diff --git a/lib/diff/hex/hex.ex b/lib/diff/hex/hex.ex index 00a45ea..6995445 100644 --- a/lib/diff/hex/hex.ex +++ b/lib/diff/hex/hex.ex @@ -1,4 +1,6 @@ defmodule Diff.Hex do + @behaviour Diff.Hex.Behaviour + @config %{ :hex_core.default_config() | http_adapter: {Diff.Hex.Adapter, %{}}, diff --git a/lib/diff_web/live/diff_live_view.ex b/lib/diff_web/live/diff_live_view.ex index ff11ab0..c8fe6b9 100644 --- a/lib/diff_web/live/diff_live_view.ex +++ b/lib/diff_web/live/diff_live_view.ex @@ -151,7 +151,9 @@ defmodule DiffWeb.DiffLiveView do end def handle_info({:generate_diff, package, from, to}, socket) do - case Diff.Hex.diff(package, from, to) do + hex_impl = Application.get_env(:diff, :hex_impl, Diff.Hex) + + case hex_impl.diff(package, from, to) do {:ok, stream} -> case process_stream_to_diffs(package, from, to, stream) do {:ok, metadata, diff_ids} -> @@ -243,11 +245,12 @@ defmodule DiffWeb.DiffLiveView do } # Process stream elements in parallel with indices + indexed_stream = Stream.with_index(stream) + results = - stream - |> Stream.with_index() - |> Task.async_stream( + Task.Supervisor.async_stream( Diff.Tasks, + indexed_stream, fn {element, index} -> process_stream_element(package, from, to, element, index) end, @@ -406,9 +409,9 @@ defmodule DiffWeb.DiffLiveView do defp build_url(app, from, to), do: "/diff/#{app}/#{from}..#{to}" defp load_diffs_in_parallel(package, from, to, diff_ids) do - diff_ids - |> Task.async_stream( + Task.Supervisor.async_stream( Diff.Tasks, + diff_ids, fn diff_id -> {diff_id, DiffWeb.LiveView.load_diff_content(package, from, to, diff_id)} end, diff --git a/test/diff_web/live/diff_live_view_test.exs b/test/diff_web/live/diff_live_view_test.exs index 997675e..7e1ed1e 100644 --- a/test/diff_web/live/diff_live_view_test.exs +++ b/test/diff_web/live/diff_live_view_test.exs @@ -2,6 +2,7 @@ defmodule DiffWeb.DiffLiveViewTest do use DiffWeb.ConnCase import Phoenix.LiveViewTest import Mox + import ExUnit.CaptureLog setup :verify_on_exit! @@ -65,6 +66,180 @@ defmodule DiffWeb.DiffLiveViewTest do end end + describe "DiffLiveView diff generation" do + test "successfully generates and processes diffs in parallel", %{conn: conn} do + # Setup mock stream data that simulates parallel processing + mock_stream = [ + {:ok, + {"diff --git a/lib/app.ex b/lib/app.ex\n--- a/lib/app.ex\n+++ b/lib/app.ex\n@@ -1,3 +1,4 @@\n+# New line\n defmodule App do", + "/tmp/from", "/tmp/to"}}, + {:ok, + {"diff --git a/lib/config.ex b/lib/config.ex\n--- a/lib/config.ex\n+++ b/lib/config.ex\n@@ -5,2 +5,3 @@\n- old_config: true\n+ new_config: false\n+ extra_config: :value", + "/tmp/from", "/tmp/to"}}, + {:too_large, "very_large_file.txt"} + ] + + # Mock Diff.Hex to return our test stream + Diff.HexMock + |> expect(:diff, fn "phoenix", "1.4.5", "1.4.9" -> + {:ok, mock_stream} + end) + + # Mock storage operations for parallel processing + Diff.StorageMock + |> stub(:get_metadata, fn "phoenix", "1.4.5", "1.4.9" -> + {:error, :not_found} + end) + |> expect(:put_diff, 3, fn "phoenix", "1.4.5", "1.4.9", diff_id, data -> + # Verify diff data structure + assert diff_id =~ ~r/diff-\d+/ + decoded = Jason.decode!(data) + assert is_map(decoded) + :ok + end) + |> expect(:put_metadata, fn "phoenix", "1.4.5", "1.4.9", metadata -> + # Verify aggregated metadata from parallel processing + assert metadata.total_diffs == 3 + assert metadata.files_changed == 3 + assert metadata.total_additions > 0 + assert metadata.total_deletions > 0 + :ok + end) + |> stub(:list_diffs, fn "phoenix", "1.4.5", "1.4.9" -> + {:ok, ["diff-0", "diff-1", "diff-2"]} + end) + |> stub(:get_diff, fn "phoenix", "1.4.5", "1.4.9", _diff_id -> + {:ok, + Jason.encode!(%{ + "diff" => "test diff", + "path_from" => "/tmp/from", + "path_to" => "/tmp/to" + })} + end) + + capture_log(fn -> + {:ok, view, _html} = live(conn, "/diff/phoenix/1.4.5..1.4.9") + + # Wait for generation and loading to complete + :timer.sleep(200) + final_html = render(view) + + # Should show the metadata from parallel processing + assert final_html =~ "3 files changed" + # additions + assert final_html =~ "+3" + # deletions + assert final_html =~ "-1" + end) + end + + test "handles errors in parallel diff processing", %{conn: conn} do + # Mock stream with some errors + mock_stream = [ + {:ok, {"diff content", "/tmp/from", "/tmp/to"}}, + {:error, {:git_diff, "git command failed"}}, + {:too_large, "large_file.bin"} + ] + + Diff.HexMock + |> expect(:diff, fn "phoenix", "1.4.5", "1.4.9" -> + {:ok, mock_stream} + end) + + # Mock storage - only successful elements should be stored + Diff.StorageMock + |> stub(:get_metadata, fn "phoenix", "1.4.5", "1.4.9" -> + {:error, :not_found} + end) + |> expect(:put_diff, 2, fn "phoenix", "1.4.5", "1.4.9", _diff_id, _data -> + :ok + end) + |> expect(:put_metadata, fn "phoenix", "1.4.5", "1.4.9", metadata -> + # Only 2 diffs should be stored (error one skipped) + assert metadata.total_diffs == 2 + assert metadata.files_changed == 2 + :ok + end) + |> stub(:list_diffs, fn "phoenix", "1.4.5", "1.4.9" -> + # Skip error element + {:ok, ["diff-0", "diff-2"]} + end) + |> stub(:get_diff, fn "phoenix", "1.4.5", "1.4.9", _diff_id -> + {:ok, + Jason.encode!(%{ + "diff" => "test diff", + "path_from" => "/tmp/from", + "path_to" => "/tmp/to" + })} + end) + + capture_log(fn -> + {:ok, view, _html} = live(conn, "/diff/phoenix/1.4.5..1.4.9") + + :timer.sleep(200) + final_html = render(view) + + # Should still succeed with partial results + # Only successful files + assert final_html =~ "2 files changed" + end) + end + + test "handles hex diff failure", %{conn: conn} do + Diff.HexMock + |> expect(:diff, fn "phoenix", "1.4.5", "1.4.9" -> + :error + end) + + Diff.StorageMock + |> stub(:get_metadata, fn "phoenix", "1.4.5", "1.4.9" -> + {:error, :not_found} + end) + + {:ok, view, _html} = live(conn, "/diff/phoenix/1.4.5..1.4.9") + + :timer.sleep(100) + final_html = render(view) + + assert final_html =~ "Failed to generate diff" + end + + test "handles storage failure during parallel processing", %{conn: conn} do + mock_stream = [ + {:ok, {"diff content", "/tmp/from", "/tmp/to"}} + ] + + Diff.HexMock + |> expect(:diff, fn "phoenix", "1.4.5", "1.4.9" -> + {:ok, mock_stream} + end) + + Diff.StorageMock + |> stub(:get_metadata, fn "phoenix", "1.4.5", "1.4.9" -> + {:error, :not_found} + end) + |> expect(:put_diff, fn "phoenix", "1.4.5", "1.4.9", _diff_id, _data -> + {:error, :storage_failed} + end) + |> expect(:put_metadata, fn "phoenix", "1.4.5", "1.4.9", metadata -> + # Should still try to store metadata even with failed individual diffs + # No successful diffs + assert metadata.total_diffs == 0 + :ok + end) + + capture_log(fn -> + {:ok, view, _html} = live(conn, "/diff/phoenix/1.4.5..1.4.9") + + :timer.sleep(100) + final_html = render(view) + + # Should handle storage failures gracefully + refute final_html =~ "Failed to generate diff" + end) + end + end + describe "DiffLiveView diffs list" do test "shows list of diffs", %{conn: conn} do {:ok, _view, html} = diff --git a/test/test_helper.exs b/test/test_helper.exs index 0821716..bf7d825 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,3 +1,4 @@ ExUnit.start() Mox.defmock(Diff.StorageMock, for: Diff.Storage) Mox.defmock(Diff.Package.StoreMock, for: Diff.Package.Store) +Mox.defmock(Diff.HexMock, for: Diff.Hex.Behaviour) From 45e781184ee6a73a56ab50240afab272397073c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Meadows-J=C3=B6nsson?= Date: Fri, 22 Aug 2025 20:52:49 +0200 Subject: [PATCH 6/7] Format --- lib/diff/hex/behaviour.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/diff/hex/behaviour.ex b/lib/diff/hex/behaviour.ex index 8744912..824bcc6 100644 --- a/lib/diff/hex/behaviour.ex +++ b/lib/diff/hex/behaviour.ex @@ -2,4 +2,3 @@ defmodule Diff.Hex.Behaviour do @callback diff(package :: String.t(), from :: String.t(), to :: String.t()) :: {:ok, Enumerable.t()} | :error end - From 92e9d5a52890973ae8a838f5dc1a77635c5527c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Meadows-J=C3=B6nsson?= Date: Fri, 22 Aug 2025 21:49:54 +0200 Subject: [PATCH 7/7] Improve visuals while loading --- assets/css/app.css | 4 +- lib/diff_web/live/diff_live_view.ex | 48 ++++++++++++---------- lib/diff_web/templates/live/diff.html.leex | 34 +++++++-------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index b8ded72..ebecd5f 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -408,7 +408,7 @@ table.package-list .button { font-weight: 600; } -#loading-trigger { +.loading-spinner { height: 60px; width: 100%; margin: 30px 0; @@ -420,7 +420,7 @@ table.package-list .button { opacity: 0.8; } -#loading-trigger::before { +.loading-spinner::before { content: ''; width: 20px; height: 20px; diff --git a/lib/diff_web/live/diff_live_view.ex b/lib/diff_web/live/diff_live_view.ex index c8fe6b9..d85d746 100644 --- a/lib/diff_web/live/diff_live_view.ex +++ b/lib/diff_web/live/diff_live_view.ex @@ -71,7 +71,7 @@ defmodule DiffWeb.DiffLiveView do {:ok, diff_ids} = Diff.Storage.list_diffs(package, from, to) initial_batch_size = 5 - {initial_diffs, remaining} = Enum.split(diff_ids, initial_batch_size) + {initial_diffs, _remaining} = Enum.split(diff_ids, initial_batch_size) socket = assign(socket, @@ -81,12 +81,11 @@ defmodule DiffWeb.DiffLiveView do to: to, metadata: metadata, all_diff_ids: diff_ids, - loaded_diffs: initial_diffs, + loaded_diffs: [], loaded_diff_content: %{}, - remaining_diffs: remaining, - loading: true, + remaining_diffs: diff_ids, generating: false, - has_more_diffs: length(remaining) > 0 + has_more_diffs: length(diff_ids) > 0 ) send(self(), {:load_diffs, initial_diffs}) @@ -106,7 +105,6 @@ defmodule DiffWeb.DiffLiveView do loaded_diffs: [], loaded_diff_content: %{}, remaining_diffs: [], - loading: false, generating: true, has_more_diffs: false ) @@ -136,14 +134,7 @@ defmodule DiffWeb.DiffLiveView do def handle_event("load-more", _params, socket) do batch_size = 5 - {next_batch, remaining} = Enum.split(socket.assigns.remaining_diffs, batch_size) - - socket = - socket - |> assign( - remaining_diffs: remaining, - has_more_diffs: length(remaining) > 0 - ) + {next_batch, _remaining} = Enum.split(socket.assigns.remaining_diffs, batch_size) send(self(), {:load_diffs_and_update, next_batch}) @@ -158,19 +149,18 @@ defmodule DiffWeb.DiffLiveView do case process_stream_to_diffs(package, from, to, stream) do {:ok, metadata, diff_ids} -> initial_batch_size = 5 - {initial_diffs, remaining} = Enum.split(diff_ids, initial_batch_size) + {initial_diffs, _remaining} = Enum.split(diff_ids, initial_batch_size) socket = socket |> assign( metadata: metadata, all_diff_ids: diff_ids, - loaded_diffs: initial_diffs, + loaded_diffs: [], loaded_diff_content: %{}, - remaining_diffs: remaining, + remaining_diffs: diff_ids, generating: false, - loading: true, - has_more_diffs: length(remaining) > 0 + has_more_diffs: length(diff_ids) > 0 ) send(self(), {:load_diffs, initial_diffs}) @@ -203,14 +193,20 @@ defmodule DiffWeb.DiffLiveView do existing_content = Map.get(socket.assigns, :loaded_diff_content, %{}) all_loaded_content = Map.merge(existing_content, new_loaded_content) - new_loaded_diffs = socket.assigns.loaded_diffs ++ diff_ids + # Only add successfully loaded diffs to loaded_diffs + successfully_loaded_new_diffs = Map.keys(new_loaded_content) + new_loaded_diffs = socket.assigns.loaded_diffs ++ successfully_loaded_new_diffs + + # Remove successfully loaded diffs from remaining_diffs + updated_remaining = socket.assigns.remaining_diffs -- successfully_loaded_new_diffs socket = socket |> assign( loaded_diffs: new_loaded_diffs, loaded_diff_content: all_loaded_content, - loading: false + remaining_diffs: updated_remaining, + has_more_diffs: length(updated_remaining) > 0 ) {:noreply, socket} @@ -226,11 +222,19 @@ defmodule DiffWeb.DiffLiveView do diff_ids ) + # Only add diffs to loaded_diffs if content was successfully loaded + successfully_loaded_diffs = Map.keys(loaded_content) + + # Remove successfully loaded diffs from remaining_diffs + updated_remaining = socket.assigns.remaining_diffs -- successfully_loaded_diffs + socket = socket |> assign( + loaded_diffs: successfully_loaded_diffs, loaded_diff_content: loaded_content, - loading: false + remaining_diffs: updated_remaining, + has_more_diffs: length(updated_remaining) > 0 ) {:noreply, socket} diff --git a/lib/diff_web/templates/live/diff.html.leex b/lib/diff_web/templates/live/diff.html.leex index 68eeb8c..1a35425 100644 --- a/lib/diff_web/templates/live/diff.html.leex +++ b/lib/diff_web/templates/live/diff.html.leex @@ -18,35 +18,31 @@
<% else %> -
-
- <%= @metadata.files_changed %> files changed - +<%= @metadata.total_additions %> - -<%= @metadata.total_deletions %> -
-
- - <%= if @generating do %> -
-
Generating diffs...
-
- <% end %> - - <%= if @loading do %> -
-
Loading diffs...
+ <%= unless @generating do %> +
+
+ <%= @metadata.files_changed %> files changed + +<%= @metadata.total_additions %> + -<%= @metadata.total_deletions %> +
<% end %>
+ <%= if @generating do %> +
+ Generating diffs... +
+ <% end %> + <%= for diff_id <- @loaded_diffs do %>
- <%= raw(Map.get(@loaded_diff_content, diff_id, "
Diff content not loaded
")) %> + <%= raw(Map.fetch!(@loaded_diff_content, diff_id)) %>
<% end %> <%= if @has_more_diffs do %> -
+
Loading more diffs...
<% end %>