diff --git a/Gemfile.lock b/Gemfile.lock index ca4cef7..6b4df15 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -107,6 +107,7 @@ GEM aws-sigv4 (~> 1.6) aws-sigv4 (1.6.0) aws-eventstream (~> 1, >= 1.0.2) + base64 (0.2.0) bootsnap (1.16.0) msgpack (~> 1.2) build-environment (1.13.0) @@ -120,6 +121,9 @@ GEM console (1.17.2) fiber-annotation fiber-local + console-adapter-rails (0.1.4) + console + rails (>= 6.1) counter_culture (3.4.0) activerecord (>= 4.2) activesupport (>= 4.2) @@ -173,6 +177,8 @@ GEM reline (>= 0.3.6) jmespath (1.6.2) json (2.6.3) + jwt (2.8.1) + base64 language_server-protocol (3.17.0.3) llhttp-ffi (0.4.0) ffi-compiler (~> 1.0) @@ -267,6 +273,8 @@ GEM zeitwerk (~> 2.5) rainbow (3.1.1) rake (13.0.6) + rbnacl (7.1.1) + ffi redis (4.8.1) redis-client (0.14.1) connection_pool @@ -375,17 +383,20 @@ DEPENDENCIES color-generator (~> 0.0.4) connection_pool (~> 2.3) console (~> 1.17) + console-adapter-rails (~> 0.1.4) counter_culture (~> 3.2) debug dotenv-rails (~> 2.8) falcon (~> 0.42) http (~> 5.1) + jwt (~> 2.8) nokogiri (~> 1.14.0.rc1) openssl-oaep (~> 0.1.0) parallel_tests (~> 4.2) pg (~> 1.4) puma (~> 6.3) rails (~> 7.0.4) + rbnacl (~> 7.1) redis (~> 4.0) request_store_rails (~> 2.0) rspec (~> 3.12) diff --git a/backend/Gemfile b/backend/Gemfile index 4877148..c9a78d3 100644 --- a/backend/Gemfile +++ b/backend/Gemfile @@ -89,3 +89,8 @@ gem "sentry-ruby", "~> 5.10" gem "sentry-rails", "~> 5.10" gem "sentry-sidekiq", "~> 5.10" + +gem "jwt", "~> 2.8" +gem "rbnacl", "~> 7.1" + +gem "console-adapter-rails", "~> 0.1.4" diff --git a/backend/Gemfile.lock b/backend/Gemfile.lock index 989c916..30c36fe 100644 --- a/backend/Gemfile.lock +++ b/backend/Gemfile.lock @@ -107,6 +107,7 @@ GEM aws-sigv4 (~> 1.6) aws-sigv4 (1.6.0) aws-eventstream (~> 1, >= 1.0.2) + base64 (0.2.0) bootsnap (1.16.0) msgpack (~> 1.2) build-environment (1.13.0) @@ -120,6 +121,9 @@ GEM console (1.17.2) fiber-annotation fiber-local + console-adapter-rails (0.1.4) + console + rails (>= 6.1) counter_culture (3.4.0) activerecord (>= 4.2) activesupport (>= 4.2) @@ -173,6 +177,8 @@ GEM reline (>= 0.3.6) jmespath (1.6.2) json (2.6.3) + jwt (2.8.1) + base64 language_server-protocol (3.17.0.3) llhttp-ffi (0.4.0) ffi-compiler (~> 1.0) @@ -269,6 +275,8 @@ GEM zeitwerk (~> 2.5) rainbow (3.1.1) rake (13.0.6) + rbnacl (7.1.1) + ffi redis (4.8.1) redis-client (0.14.1) connection_pool @@ -378,17 +386,20 @@ DEPENDENCIES color-generator (~> 0.0.4) connection_pool (~> 2.3) console (~> 1.17) + console-adapter-rails (~> 0.1.4) counter_culture (~> 3.2) debug dotenv-rails (~> 2.8) falcon (~> 0.42) http (~> 5.1) + jwt (~> 2.8) nokogiri (~> 1.14.0.rc1) openssl-oaep (~> 0.1.0) parallel_tests (~> 4.2) pg (~> 1.4) puma (~> 6.3) rails (~> 7.0.4) + rbnacl (~> 7.1) redis (~> 4.0) request_store_rails (~> 2.0) rspec (~> 3.12) diff --git a/backend/app/controllers/api/auth_controller.rb b/backend/app/controllers/api/auth_controller.rb index fcb2f72..fcfbdcc 100644 --- a/backend/app/controllers/api/auth_controller.rb +++ b/backend/app/controllers/api/auth_controller.rb @@ -1,65 +1,76 @@ # frozen_string_literal: true module Api class AuthController < FrontendController - def create_code - code = SecureRandom.random_number(100_000_000 - 1).to_s.rjust(8, "0") - Rails.logger.info("New auth code: #{code}") - $redis.with do |conn| - conn.set( - "auth_code/#{code}", - { authorized: false }.to_json, - ex: 1.5.minutes.to_i - ) - end - - render json: { code: "ok", authCode: code } + def start + uuid = SecureRandom.uuid + url = + "https://open.sonolus.com/external-login/" + request.host + + "/api/login/callback?uuid=#{uuid}" + render json: { url:, uuid: } end - def check_code - params.require :code - code = params[:code] - if code.blank? - render json: { - code: "invalid_request", - message: "Missing auth code" - }, - status: :bad_request + def callback + body = request.body.read + signature = request.headers["Sonolus-Signature"] + unless signature + logger.warn "No signature" + render json: { error: "No signature" }, status: :unauthorized return end - unless ( - auth_data = - $redis.with do |conn| - conn - .get("auth_code/#{code}") - &.then { |v| JSON.parse(v, symbolize_names: true) } - end + unless JWT::JWA::Ecdsa.verify( + "ES256", + SonolusController::SONOLUS_PUBLIC_KEY.verify_key, + body, + Base64.strict_decode64(signature) ) - render json: { - code: "unknown_code", - message: "Unknown auth code" - }, - status: :not_found + logger.warn "Invalid signature" + render json: { error: "Invalid signature" }, status: :unauthorized return end - if auth_data[:authorized] - token_data = - $redis.with do |conn| - conn - .get("auth_token/#{auth_data[:token]}") - &.then { |v| JSON.parse(v, symbolize_names: true) } - end - user = User.new(token_data[:user]) - render json: { - code: "ok", - user: user.to_frontend - } - session[:user_id] = user.id - else - render json: { - code: "not_authorized", - message: "Auth code not authorized" - }, - status: :forbidden + unless params[:type] == "authenticateExternal" + logger.warn "Invalid type: #{params[:type]}" + render json: { error: "Invalid type" }, status: :unauthorized + return + end + unless Rails.env.development? + unless params[:url].ends_with?( + "//" + request.host + + "/api/login/callback?uuid=#{params[:uuid]}" + ) + logger.warn "Invalid url: #{params[:url]}" + render json: { error: "Invalid url" }, status: :unauthorized + return + end + end + unless params[:time] && (Time.now.to_i - params[:time] / 1000) < 1.minutes + render json: { error: "Expired time" }, status: :unauthorized + return + end + + user = User.from_profile(params[:userProfile]) + uuid = params[:uuid] + $redis.with do |conn| + conn.set("sonolus_login/#{uuid}", user.id, ex: 30.minutes) + end + + render json: { code: "ok" } + end + + def status + uuid = params[:uuid] + unless uuid + render json: { code: "not_found", message: "Not Found" }, status: 404 + return + end + + $redis.with do |conn| + status = conn.get("sonolus_login/#{uuid}") + if status + session[:user_id] = status.to_i + render json: { code: "ok" } + else + render json: { code: "not_found", message: "Not Found" }, status: 404 + end end end diff --git a/backend/app/controllers/api/charts_controller.rb b/backend/app/controllers/api/charts_controller.rb index a3e4493..fe2b0c1 100644 --- a/backend/app/controllers/api/charts_controller.rb +++ b/backend/app/controllers/api/charts_controller.rb @@ -215,6 +215,7 @@ def process_chart_request unless (current_user.admin?) || author.id == session[:user_id] || author.owner_id == session[:user_id] + logger.warn "User #{session[:user_id].inspect} is not allowed to upload charts as user #{author.id}" render json: { code: "forbidden", error: "You are not allowed to upload charts as this user" diff --git a/backend/app/controllers/sonolus/asset_controller.rb b/backend/app/controllers/sonolus/asset_controller.rb index e852816..1b12f55 100644 --- a/backend/app/controllers/sonolus/asset_controller.rb +++ b/backend/app/controllers/sonolus/asset_controller.rb @@ -3,6 +3,29 @@ module Sonolus class AssetController < SonolusController + def info + params.permit(:type) + type = params[:type] + names = + Rails + .root + .glob("assets/#{type}/*.yml") + .map { |path| File.basename(path).delete_suffix(".yml") } + render json: { + sections: [ + { + title: "#ALL", + items: + names.map do |name| + Sonolus::AssetController.asset_get( + type.delete_suffix("s"), + name + ) + end + } + ] + } + end def list params.permit(:type) type = params[:type] @@ -32,7 +55,7 @@ def show if item.nil? render json: { error: "not_found", message: "Not Found" }, status: 404 else - render json: { item:, description: "", recommended: [] } + render json: { item:, description: "", sections: [] } end end @@ -156,28 +179,26 @@ def self.asset_get(type, name) rescue Errno::ENOENT return nil end - data.to_h do |k, v| - next k, v unless v.is_a?(String) + data + .to_h do |k, v| + next k, v unless v.is_a?(String) - if k == "name" - v = "chcy-#{v}" - elsif v.start_with?("!asset:") - v = asset_get(k, v.delete_prefix("!asset:")) - elsif v.start_with?("!file:") - name, srl_type = v.delete_prefix("!file:").split("/") - srl_type ||= name - hash = - Digest::SHA1.file( - Rails.root.join("assets", "#{type}s", name) - ).hexdigest - v = { - hash:, - url: "/sonolus/assets/#{type}s/#{name}?hash=#{hash}", - type: srl_type - } + if k == "name" + v = "chcy-#{v}" + elsif v.start_with?("!asset:") + v = asset_get(k, v.delete_prefix("!asset:")) + elsif v.start_with?("!file:") + name, srl_type = v.delete_prefix("!file:").split("/") + srl_type ||= name + hash = + Digest::SHA1.file( + Rails.root.join("assets", "#{type}s", name) + ).hexdigest + v = { hash:, url: "/sonolus/assets/#{type}s/#{name}?hash=#{hash}" } + end + [k, v] end - [k, v] - end + .merge({ source: ENV["HOST"], tags: [] }) end end end diff --git a/backend/app/controllers/sonolus/auth_controller.rb b/backend/app/controllers/sonolus/auth_controller.rb deleted file mode 100644 index 06910a9..0000000 --- a/backend/app/controllers/sonolus/auth_controller.rb +++ /dev/null @@ -1,113 +0,0 @@ -# frozen_string_literal: true -module Sonolus - class AuthController < SonolusController - DUMMY_SECTION = { items: [], search: { options: [] } }.freeze - - def code_search - { - options: [ - { - name: I18n.t("auth.code_search.option.name"), - placeholder: I18n.t("auth.code_search.option.placeholder"), - query: "code", - type: "text" - } - ] - } - end - - def sonolus_info - render json: { - title: I18n.t("auth.title"), - banner: banner("banner_login"), - levels: { - items: [ - dummy_level("auth.welcome", "auth-welcome", cover: "logo") - ], - search: code_search - }, - backgrounds: DUMMY_SECTION, - skins: DUMMY_SECTION, - effects: DUMMY_SECTION, - particles: DUMMY_SECTION, - engines: DUMMY_SECTION - } - end - - def sonolus_levels_list - params.permit(:code) - params.permit(:page) - if params[:code].blank? - item = dummy_level("auth.guide", "auth-guide", cover: "info") - elsif !params[:code].match?(/^[0-9]{8}$/) - item = dummy_level("auth.invalid", "auth-invalid", cover: "error") - elsif session_data.nil? - render json: {}, status: :unauthorized - return - else - item = - dummy_level( - "auth.confirm", - "auth-confirm-#{params[:code]}", - cover: "warning", - code: params[:code] - ) - end - render json: { items: [item], search: code_search, pageCount: 1 } - end - - def sonolus_confirm - params.permit(:code) - if params[:code].blank? - render json: {} - return - end - if session_data.nil? - render json: {}, status: :unauthorized - return - end - if !( - auth_data = - $redis.with do |conn| - conn - .get("auth_code/#{params[:code]}") - &.then { |v| JSON.parse(v, symbolize_names: true) } - end - ) || auth_data[:authorized] - render json: { - item: - dummy_level("auth.expired", "auth-expired", cover: "error"), - description: "", - recommended: [] - } - return - end - auth_data[:authorized] = true - - token = SecureRandom.urlsafe_base64(32) - auth_data[:token] = token - auth_data[:user_handle] = session_data[:user][:handle] - - $redis.with do |conn| - conn.set( - "auth_token/#{token}", - { user: self.current_user }.to_json, - ex: 1.day - ) - - conn.set( - "auth_code/#{params[:code]}", - auth_data.to_json, - ex: 1.5.minutes.to_i - ) - end - - render json: { - item: - dummy_level("auth.success", "auth-success", cover: "success"), - description: "", - recommended: [] - } - end - end -end diff --git a/backend/app/controllers/sonolus/info_controller.rb b/backend/app/controllers/sonolus/info_controller.rb index 230646f..116f5d2 100644 --- a/backend/app/controllers/sonolus/info_controller.rb +++ b/backend/app/controllers/sonolus/info_controller.rb @@ -4,30 +4,25 @@ class InfoController < SonolusController def info title = I18n.t("sonolus.title") title += " (dev)" if ENV["RAILS_ENV"] != "production" + description = I18n.t("sonolus.info.description") + if current_user + pp current_user + description += + "\n\n" + + I18n.t( + "sonolus.info.logged_in", + name: current_user.name, + handle: current_user.handle + ) + end - render json: - { - title:, - banner: banner("banner"), - levels: { - items: - Chart - .order(published_at: :desc) - .limit(5) - .includes(:author) - .eager_load(file_resources: { file_attachment: :blob }) - .where(visibility: :public) - .sonolus_listed - .map(&:to_sonolus), - search: { - options: Sonolus::LevelsController.search_options - } - } - }.tap { |json| - %i[backgrounds skins effects particles engines].each do |key| - json[key] = { items: [], search: { options: [] } } - end - } + render json: { + title:, + banner: banner("banner"), + hasMultiplayer: false, + hasAuthentication: true, + description: + } end def test_info diff --git a/backend/app/controllers/sonolus/levels_controller.rb b/backend/app/controllers/sonolus/levels_controller.rb index b17497e..a068cec 100644 --- a/backend/app/controllers/sonolus/levels_controller.rb +++ b/backend/app/controllers/sonolus/levels_controller.rb @@ -80,6 +80,32 @@ def self.test_search_options end end + def info + render json: { + searches: [ + { + type: "advanced", + title: "#ADVANCED", + options: self.class.search_options + } + ], + sections: [ + { + title: "#NEWEST", + items: + Chart + .order(published_at: :desc) + .limit(5) + .includes(:author) + .eager_load(file_resources: { file_attachment: :blob }) + .where(visibility: :public) + .sonolus_listed + .map(&:to_sonolus) + } + ] + } + end + def list params.permit(:page, *(self.class.search_options.map { |o| o[:query] })) @@ -188,7 +214,10 @@ def list end if params[:q_id].present? charts = - charts.where("LOWER(charts.name) LIKE ?", "%#{params[:q_id].downcase}%") + charts.where( + "LOWER(charts.name) LIKE ?", + "%#{params[:q_id].downcase}%" + ) end page_count = (charts.count / 20.0).ceil @@ -260,24 +289,7 @@ def show user_faved = chart.likes.exists?(user_id: current_user&.id) render json: { item: chart.to_sonolus, - recommended: [ - ( - if user_faved - dummy_level( - "like.button.to_off", - "like-off-#{chart.name}", - cover: "like_on" - ) - else - dummy_level( - "like.button.to_on", - "like-on-#{chart.name}", - cover: "like_off" - ) - end - ), - chart.variant_of&.to_sonolus, - chart.variants.map(&:to_sonolus) + sections: [ ].flatten.compact, description: chart.sonolus_description } diff --git a/backend/app/controllers/sonolus/like_controller.rb b/backend/app/controllers/sonolus/like_controller.rb index 21eda80..5703531 100644 --- a/backend/app/controllers/sonolus/like_controller.rb +++ b/backend/app/controllers/sonolus/like_controller.rb @@ -24,7 +24,7 @@ def to_on cover: "error" ), description: "", - recommended: [] + sections: [] } return end @@ -40,7 +40,7 @@ def to_on cover: "success" ), description: "", - recommended: [] + sections: [] }, status: :created end @@ -68,7 +68,7 @@ def to_off cover: "error" ), description: "", - recommended: [] + sections: [] } return end @@ -82,7 +82,7 @@ def to_off cover: "success" ), description: "", - recommended: [] + sections: [] } end end diff --git a/backend/app/controllers/sonolus_controller.rb b/backend/app/controllers/sonolus_controller.rb index 4104d07..e3839a8 100644 --- a/backend/app/controllers/sonolus_controller.rb +++ b/backend/app/controllers/sonolus_controller.rb @@ -1,13 +1,18 @@ # frozen_string_literal: true -require "openssl" -require "openssl/oaep" +require "jwt" +require "base64" require "request_store_rails" class SonolusController < ApplicationController SONOLUS_PUBLIC_KEY = - OpenSSL::PKey::RSA.new( - Rails.root.join("config/sonolus_key.pub").read - ).freeze + JWT::JWK::EC.new( + { + kty: "EC", + crv: "P-256", + x: "d2B14ZAn-zDsqY42rHofst8rw3XB90-a5lT80NFdXo0", + y: "Hxzi9DHrlJ4CVSJVRnydxFWBZAgkFxZXbyxPSa8SJQw" + } + ) around_action do |_, action| params.permit(:localization) @@ -151,48 +156,61 @@ def dummy_level_info render json: { item: dummy_level("dummy", "dummy", cover: "error"), description: "", - recommended: [] + sections: [] } end def authenticate - auth_data = { - id: SecureRandom.urlsafe_base64(32), - key: SecureRandom.random_bytes(32), - iv: SecureRandom.random_bytes(16) - } - auth_data_encoded = { - id: auth_data[:id], - key: Base64.strict_encode64(auth_data[:key]), - iv: Base64.strict_encode64(auth_data[:iv]) - } - encrypted = - SONOLUS_PUBLIC_KEY.public_encrypt_oaep(auth_data_encoded.to_json) + body = request.body.read + signature = request.headers["Sonolus-Signature"] + unless signature + logger.warn "No signature" + render json: { error: "No signature" }, status: :unauthorized + return + end + unless JWT::JWA::Ecdsa.verify( + "ES256", + SONOLUS_PUBLIC_KEY.verify_key, + body, + Base64.strict_decode64(signature) + ) + logger.warn "Invalid signature" + render json: { error: "Invalid signature" }, status: :unauthorized + return + end + unless params[:type] == "authenticateServer" + logger.warn "Invalid type" + render json: { error: "Invalid type" }, status: :unauthorized + return + end + if params[:address].exclude?(ENV["HOST"]) + logger.warn "Invalid address" + render json: { error: "Invalid address" }, status: :unauthorized + return + end + if Time.now.to_i - (params[:time] / 1000) >= 1.minutes + logger.warn "Time too old" + render json: { error: "Expired time" }, status: :unauthorized + return + end + + session_id = SecureRandom.uuid $redis.with do |conn| conn.set( - "sonolus_auth_session/#{auth_data[:id]}", - auth_data_encoded.to_json, - ex: 5.minutes + "sonolus_session/#{session_id}", + params[:userProfile], + ex: 30.minutes ) end - address = - ENV.fetch( - "HOST", - (Rails.env.development? ? "http://" : "https://") + - request.host_with_port - ) - address += "/auth" if request.path.start_with?("/auth/sonolus") - address += "/test" if request.path.start_with?("/test/sonolus") render json: { - address:, - session: Base64.strict_encode64(encrypted), - expiration: ((Time.now.to_f + 5.minutes) * 1000).to_i + session: session_id, + expiration: (Time.now.to_f + 30.minutes) * 1000 } end - after_action { headers["Sonolus-Version"] = "0.7.5" } + after_action { headers["Sonolus-Version"] = "0.8.0" } around_action do |_controller, action| success = false diff --git a/backend/app/models/chart.rb b/backend/app/models/chart.rb index c87361c..1917827 100644 --- a/backend/app/models/chart.rb +++ b/backend/app/models/chart.rb @@ -100,15 +100,14 @@ def to_sonolus title:, artists: "#{composer} / #{artist.presence || "-"}", author: "#{author_name.presence || author.name}##{author.display_handle}", + source: ENV["HOST"], + tags: [], cover: resources[:cover]&.to_srl, bgm: resources[:bgm]&.to_srl, preview: resources[:preview]&.to_srl, data: resources[:data]&.to_srl || - { - url: "/sonolus/assets/generate?chart=#{name}&type=data", - type: "LevelData" - }, + { hash: "", url: "/sonolus/assets/generate?chart=#{name}&type=data" }, rating:, version: 1, useSkin: { @@ -116,7 +115,7 @@ def to_sonolus }, useBackground: { useDefault: false, - item: to_sonolus_background(resources, version: 3) + item: to_sonolus_background(resources) }, useEffect: { useDefault: true @@ -134,20 +133,20 @@ def to_sonolus_background(resources, version: 3) Digest::SHA1.file( Rails.root.join("assets/backgrounds/configuration.json.gz") ).hexdigest, - url: "/sonolus/assets/backgrounds/configuration.json.gz", - type: "BackgroundConfiguration" + url: "/sonolus/assets/backgrounds/configuration.json.gz" } data = { hash: Digest::SHA1.file( Rails.root.join("assets/backgrounds/v#{version}_data.json.gz") ).hexdigest, - url: "/sonolus/assets/backgrounds/v#{version}_data.json.gz", - type: "BackgroundData" + url: "/sonolus/assets/backgrounds/v#{version}_data.json.gz" } { name: "chcy-bg-#{name}-v#{version}", version: 2, + tags: [], + source: ENV["HOST"], title:, subtitle: "#{composer}#{artist.presence ? " / #{artist}" : ""}", author: "#{author_name.presence || author.name}##{author.display_handle}", @@ -156,9 +155,9 @@ def to_sonolus_background(resources, version: 3) image: resources[:"background_v#{version}"]&.to_srl || { + hash: "", url: - "/sonolus/assets/generate?chart=#{name}&type=background_v#{version}", - type: "BackgroundImage" + "/sonolus/assets/generate?chart=#{name}&type=background_v#{version}" }, configuration: config } diff --git a/backend/app/models/file_resource.rb b/backend/app/models/file_resource.rb index 30a4f33..fc733a4 100644 --- a/backend/app/models/file_resource.rb +++ b/backend/app/models/file_resource.rb @@ -71,6 +71,6 @@ def to_frontend alias url to_frontend def to_srl - { hash: sha1, type: TYPES[kind.to_sym], url: to_frontend } + { hash: sha1, url: to_frontend } end end diff --git a/backend/app/models/user.rb b/backend/app/models/user.rb index 6b471cb..b1493fb 100644 --- a/backend/app/models/user.rb +++ b/backend/app/models/user.rb @@ -83,4 +83,27 @@ def refresh_discord_token def admin? ENV["ADMIN_HANDLE"] && ENV["ADMIN_HANDLE"].split(",").include?(handle) end + + def self.from_profile(user_profile) + table_contents = { + handle: user_profile[:handle], + name: user_profile[:name], + about_me: user_profile[:aboutMe], + fg_color: user_profile[:avatarForegroundColor], + bg_color: user_profile[:avatarBackgroundColor] + } + + if (u = User.find_by(handle: user_profile[:handle], owner_id: nil)) + if table_contents.each_pair.any? { |k, v| u[k] != v } + logger.info "User #{u.handle} updated, updating table" + u.update!(table_contents) + else + logger.info "User #{u.handle} not updated, skipping table update" + end + u + else + logger.info "User #{user_profile[:handle]} not found, creating" + User.create(table_contents) + end + end end diff --git a/backend/assets/engines/EngineConfiguration b/backend/assets/engines/EngineConfiguration index 000880f..1b7a787 100644 Binary files a/backend/assets/engines/EngineConfiguration and b/backend/assets/engines/EngineConfiguration differ diff --git a/backend/assets/engines/EngineData b/backend/assets/engines/EngineData new file mode 100644 index 0000000..6d5cee6 Binary files /dev/null and b/backend/assets/engines/EngineData differ diff --git a/backend/assets/engines/EnginePlayData b/backend/assets/engines/EnginePlayData index 925f3e7..07a7b72 100644 Binary files a/backend/assets/engines/EnginePlayData and b/backend/assets/engines/EnginePlayData differ diff --git a/backend/assets/engines/EnginePreviewData b/backend/assets/engines/EnginePreviewData index 64592ce..853de7e 100644 Binary files a/backend/assets/engines/EnginePreviewData and b/backend/assets/engines/EnginePreviewData differ diff --git a/backend/assets/engines/EngineWatchData b/backend/assets/engines/EngineWatchData index bb75095..a72efd2 100644 Binary files a/backend/assets/engines/EngineWatchData and b/backend/assets/engines/EngineWatchData differ diff --git a/backend/assets/engines/pjsekai-extended.yml b/backend/assets/engines/pjsekai-extended.yml index 961ade4..7ccdf0d 100644 --- a/backend/assets/engines/pjsekai-extended.yml +++ b/backend/assets/engines/pjsekai-extended.yml @@ -13,4 +13,4 @@ description: PJSekai + Trace notes, custom judgement, etc. subtitle: From servers.sonolus.com/pjsekai thumbnail: "!file:EngineThumbnail" title: PJSekai+ -version: 11 +version: 12 diff --git a/backend/assets/particles/pjsekai-v1.yml b/backend/assets/particles/pjsekai-v1.yml index 3aa4da4..ba2f6e9 100644 --- a/backend/assets/particles/pjsekai-v1.yml +++ b/backend/assets/particles/pjsekai-v1.yml @@ -6,4 +6,4 @@ description: Nothing changed. texture: "!file:v1.png/ParticleTexture" thumbnail: "!file:thumbnail.png/ParticleThumbnail" title: PJSekai / v1 -version: 2 +version: 3 diff --git a/backend/assets/particles/pjsekai-v3.yml b/backend/assets/particles/pjsekai-v3.yml index d092b15..e8d4769 100644 --- a/backend/assets/particles/pjsekai-v3.yml +++ b/backend/assets/particles/pjsekai-v3.yml @@ -6,4 +6,4 @@ description: "From: https://canary.discord.com/channels/696599620259807242/69660 texture: "!file:v3.png/ParticleTexture" thumbnail: "!file:thumbnail.png/ParticleThumbnail" title: PJSekai / v3 -version: 2 +version: 3 diff --git a/backend/assets/particles/v1.json.gz b/backend/assets/particles/v1.json.gz index 9de87ad..271e4c8 100644 Binary files a/backend/assets/particles/v1.json.gz and b/backend/assets/particles/v1.json.gz differ diff --git a/backend/assets/particles/v3.json.gz b/backend/assets/particles/v3.json.gz index bf62576..3a0b56d 100644 Binary files a/backend/assets/particles/v3.json.gz and b/backend/assets/particles/v3.json.gz differ diff --git a/backend/assets/particles/v3.png b/backend/assets/particles/v3.png index 910b33b..bd3dad7 100644 Binary files a/backend/assets/particles/v3.png and b/backend/assets/particles/v3.png differ diff --git a/backend/config/environment.rb b/backend/config/environment.rb index a21c0c5..d813fb0 100644 --- a/backend/config/environment.rb +++ b/backend/config/environment.rb @@ -2,5 +2,8 @@ # Load the Rails application. require_relative "application" +require "console/adapter/rails" + +Console::Adapter::Rails.apply! # Initialize the Rails application. Rails.application.initialize! diff --git a/backend/config/environments/development.rb b/backend/config/environments/development.rb index b220372..d4025e2 100644 --- a/backend/config/environments/development.rb +++ b/backend/config/environments/development.rb @@ -8,6 +8,8 @@ Bullet.add_footer = true end + config.hosts.clear + # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded any time diff --git a/backend/config/initializers/discord.rb b/backend/config/initializers/discord.rb index a468a1a..ec32ca8 100644 --- a/backend/config/initializers/discord.rb +++ b/backend/config/initializers/discord.rb @@ -21,6 +21,6 @@ def method_missing(*_args) end Rails.logger.info( - "Discord: Logged in as #{info[:username]}##{info[:discriminator]}" + "Discord: Logged in as #{info["username"]}##{info["discriminator"]}" ) end diff --git a/backend/config/initializers/logger.rb b/backend/config/initializers/logger.rb deleted file mode 100644 index ce6e701..0000000 --- a/backend/config/initializers/logger.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require "rails" - -require "console" -require "console/compatible/logger" - -if ActiveSupport::Logger.respond_to?(:logger_outputs_to?) - # https://github.com/rails/rails/issues/44800 - class ActiveSupport::Logger - def self.logger_outputs_to?(*) - true - end - end -end - -Rails.logger = ActiveSupport::TaggedLogging.new(Console::Compatible::Logger.new("Rails", Console.logger.output)) diff --git a/backend/config/locales/en.yml b/backend/config/locales/en.yml index 4368dbb..1e8a343 100644 --- a/backend/config/locales/en.yml +++ b/backend/config/locales/en.yml @@ -65,6 +65,9 @@ en: description: "You can test your chart here, by tapping the [ More ] button below." sonolus: title: "Chart Cyanvas" + info: + description: "Chart Cyanvas is a sekai custom charts platform for Sonolus." + logged_in: "Logged in as %{name}#%{handle}." tag_separator: ", " levels: description: | diff --git a/backend/config/routes.rb b/backend/config/routes.rb index 1002f6e..8e4da76 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -26,10 +26,11 @@ get "/charts/:name/download_chart", to: "api/charts#download_chart" post "/charts", to: "api/charts#create" - post "/auth", to: "api/auth#create_code" - get "/auth", to: "api/auth#check_code" - get "/auth/session", to: "api/auth#restore_session" - delete "/auth/session", to: "api/auth#logout" + post "/login/start", to: "api/auth#start" + get "/login/status", to: "api/auth#status" + post "/login/callback", to: "api/auth#callback" + get "/login/session", to: "api/auth#restore_session" + delete "/login/session", to: "api/auth#logout" get "/my/alt_users", to: "api/my#alt_users" post "/my/alt_users", to: "api/my#create_alt_user" @@ -48,6 +49,7 @@ scope "/sonolus" do get "/info", to: "sonolus/info#info" + get "/levels/info", to: "sonolus/levels#info" get "/levels/list", to: "sonolus/levels#list" end scope "/test" do @@ -74,6 +76,11 @@ end types = %w[backgrounds effects particles engines skins] + get "/:type/info", + to: "sonolus/asset#info", + constraints: { + type: Regexp.new(types.join("|")) + } get "/:type/list", to: "sonolus/asset#list", constraints: { diff --git a/backend/config/sonolus_key.pub b/backend/config/sonolus_key.pub index acccdad..cfa6f80 100644 --- a/backend/config/sonolus_key.pub +++ b/backend/config/sonolus_key.pub @@ -1,9 +1,4 @@ -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8DDWNplPkFiQI2QywLOT -OLAsIA+H0kc9RjFK4pJV6MKJxvhAGsJ8uA18Wsug4YU7Kp93gV3Zv7/RlV0yMkWv -CxhsQO/K9NI5MdyJSxTI7UcVukZDAQbiFBT+/1od28XKhn6eO2PqI3E7uXpN44Cd -O7rgtLSYBBT1/+Aw/gJHn+u5fo60xusfPEYYpXNnIHEL52niNW52wmk/LGItZDlJ -+oSwZH2qRFol6t63ymzFUNbred0DwJD+RmqWEq/J/57ofCaL65148BmD2KkJoA8k -MR4hNOP9cYs7iQQguboCa0SsJPl4V2SOG+Mn6IkSkZJRfYkC3SXdjmxf+i4qA801 -RQIDAQAB +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd2B14ZAn+zDsqY42rHofst8rw3XB +90+a5lT80NFdXo0fHOL0MeuUngJVIlVGfJ3EVYFkCCQXFldvLE9JrxIlDA== -----END PUBLIC KEY----- diff --git a/backend/spec/requests/sonolus_spec.rb b/backend/spec/requests/sonolus_spec.rb index 4cba5ad..4a5b51d 100644 --- a/backend/spec/requests/sonolus_spec.rb +++ b/backend/spec/requests/sonolus_spec.rb @@ -20,7 +20,7 @@ ) ) end - it "returns in right schema" do + xit "returns in right schema" do post authenticate_path expect(response).to have_http_status(:ok) match_schema = @@ -32,7 +32,7 @@ expect(match_schema).to be true end - it "encrypts the session correctly" do + xit "encrypts the session correctly" do post authenticate_path session = JSON.parse(response.body)["session"] diff --git a/frontend/components/Header.tsx b/frontend/components/Header.tsx index 5001c6d..83aa4ce 100644 --- a/frontend/components/Header.tsx +++ b/frontend/components/Header.tsx @@ -1,18 +1,72 @@ import Link from "next/link" -import { useState } from "react" +import { useCallback, useEffect, useRef, useState } from "react" import useTranslation from "next-translate/useTranslation" import LogoCF from "public/logo-cf.svg" +import Trans from "next-translate/Trans" +import urlcat from "urlcat" import { useSession } from "../lib/atom" import SideMenu from "./SideMenu" +import ModalPortal from "./ModalPortal" +type LoginState = { uuid: string; url: URL } const Header = () => { const { t } = useTranslation("header") const [session] = useSession() const [showMenu, setShowMenu] = useState(false) + const [loginState, setLoginState] = useState() + const loginUuid = useRef() + useEffect(() => { + if (loginState) { + loginUuid.current = loginState.uuid + } + }, [loginState]) + const loginInterval = useRef() + const checkLogin = useCallback(() => { + fetch(urlcat("/api/login/status", { uuid: loginUuid.current }), { + method: "GET", + }) + .then((res) => res.json()) + .then((state: { code: string }) => { + if (state.code === "ok") { + window.location.reload() + } + }) + }, []) + const onLogin = useCallback(() => { + fetch("/api/login/start", { method: "POST" }) + .then((res) => res.json()) + .then((state: { uuid: string; url: string }) => { + setLoginState({ uuid: state.uuid, url: new URL(state.url) }) + loginInterval.current = setInterval( + checkLogin, + 2500 + ) as unknown as number + window.open(state.url, "_blank") + }) + }, [setLoginState, checkLogin]) + return ( <> + { + setLoginState(undefined) + clearInterval(loginInterval.current) + }} + > +

{t("login.title")}

+

+ , + br:
, + }} + /> +

+
{ ) : ( - -
- {t("login")} -
- + ) ) : (
diff --git a/frontend/components/ModalPortal.tsx b/frontend/components/ModalPortal.tsx index 1d0ba3f..ee9f703 100644 --- a/frontend/components/ModalPortal.tsx +++ b/frontend/components/ModalPortal.tsx @@ -4,7 +4,8 @@ import { createPortal } from "react-dom" const ModalPortal: React.FC<{ children: React.ReactNode isOpen: boolean -}> = ({ children, isOpen }) => { + close?: () => void +}> = ({ children, isOpen, close }) => { const [hide, setHide] = useState(true) useEffect(() => { @@ -22,6 +23,9 @@ const ModalPortal: React.FC<{ pointerEvents: isOpen ? "auto" : "none", opacity: isOpen ? 1 : 0, }} + onClick={() => { + close?.() + }} > {isOpen && (
diff --git a/frontend/components/SideMenu.tsx b/frontend/components/SideMenu.tsx index 1fad8f0..c684c1f 100644 --- a/frontend/components/SideMenu.tsx +++ b/frontend/components/SideMenu.tsx @@ -101,7 +101,7 @@ const SideMenu: React.FC<{ close: () => void }> = ({ close }) => { onClick: () => { setSession({ loggedIn: false }) - fetch("/api/auth/session", { method: "delete" }).then(() => { + fetch("/api/login/session", { method: "delete" }).then(() => { router.push("/") }) }, diff --git a/frontend/i18n/en.yml b/frontend/i18n/en.yml index 2d7ba69..fc2b858 100644 --- a/frontend/i18n/en.yml +++ b/frontend/i18n/en.yml @@ -11,17 +11,17 @@ openInSonolus: "Sonolus" adminDecorate: " (Admin)" header: - login: "Log in" + login: + button: "Log in" + title: "Logging in..." + description: | + Log in with your Sonolus account.
+ This page will be refreshed automatically after logging in.
+ Didn't get a popup? Click here. home: newCharts: "New charts" welcome: "Welcome to Chart Cyanvas! If you are new to this site, please read <0>How to use." -login: - title: "Log in" - step1: "1. Register the server below to Sonolus." - step2: "2. Enter the code in the login server." - step2note: "This code will expire in 90 seconds." - alternative: "Alternatively, if Sonolus is installed on this device, you can log in by pressing the button below." - openSonolus: "Open Sonolus" + menu: post: "Post chart" my: "My charts" diff --git a/frontend/i18n/ja.yml b/frontend/i18n/ja.yml index a0a1c0c..45b0c44 100644 --- a/frontend/i18n/ja.yml +++ b/frontend/i18n/ja.yml @@ -11,17 +11,16 @@ openInSonolus: "Sonolus" adminDecorate: "(管理者)" header: - login: "ログイン" + login: + button: "ログイン" + title: "ログイン中..." + description: | + Sonolusアカウントでログインしてください。
+ ログイン後、このページは自動的に更新されます。
+ ポップアップが表示されない場合は、こちらをクリックしてください。 home: newCharts: "新着譜面" welcome: "Chart Cyanvasへようこそ!初めての方は<0>初めての方へを参照して下さい。" -login: - title: "ログイン" - step1: "1. 下のサーバーをSonolusに追加してください。" - step2: "2. ログインサーバー内でコードを入力してください。" - step2note: "このコードは90秒で失効します。" - alternative: "また、このデバイスにSonolusがインストールされている場合は、次のボタンを押すことでもログインできます。" - openSonolus: "Sonolusを開く" menu: post: "譜面投稿" my: "自分の譜面" diff --git a/frontend/lib/requireLogin.tsx b/frontend/lib/requireLogin.tsx index 5db6953..716fbf6 100644 --- a/frontend/lib/requireLogin.tsx +++ b/frontend/lib/requireLogin.tsx @@ -10,7 +10,7 @@ const requireLogin = (component: NextPage) => { if (session.loggedIn === undefined) return
// @ts-expect-error createElement limitation? if (session.loggedIn) return createElement(component, props) - router.push("/login") + router.push("/") return
} Inner.displayName = `RequireLogin(${component.displayName})` diff --git a/frontend/next.config.js b/frontend/next.config.js index 5e491bf..90564f4 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -45,15 +45,7 @@ const nextConfig = nextTranslate({ source: String.raw`/api/:path((?!next).*)`, destination: `${process.env.BACKEND_HOST}/api/:path*`, }, - ...[ - "auth/sonolus", - "auth/assets", - "sonolus", - "test", - "assets", - "rails", - "admin/sidekiq", - ].map((dir) => ({ + ...["sonolus", "test", "assets", "rails", "admin/sidekiq"].map((dir) => ({ source: String.raw`/${dir}/:path*`, destination: `${process.env.BACKEND_HOST}/${dir}/:path*`, })), diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index 7eda4d8..67410fc 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -21,7 +21,7 @@ function App({ Component, pageProps }: AppProps) { if (session && session.loggedIn !== undefined) { return } - fetch(`/api/auth/session`, { + fetch(`/api/login/session`, { method: "GET", }).then(async (res) => { const json = await res.json() diff --git a/frontend/pages/charts/upload.tsx b/frontend/pages/charts/upload.tsx index 6c9d38c..e1b1358 100644 --- a/frontend/pages/charts/upload.tsx +++ b/frontend/pages/charts/upload.tsx @@ -433,7 +433,10 @@ const UploadChart: NextPage< router.push("/") return } - const data = await res.json() + const data = await res.json().catch(() => ({ + code: "error", + errors: {}, + })) if (data.code !== "ok") { setErrors(mapErrors(data.errors)) diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx deleted file mode 100644 index cc44682..0000000 --- a/frontend/pages/login.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { NextPage } from "next" -import Head from "next/head" -import { useRouter } from "next/router" -import { useState, useRef, useEffect } from "react" -import useTranslation from "next-translate/useTranslation" -import urlcat from "urlcat" -import { isDev } from "lib/index" -import { useSession } from "lib/atom" -import { host } from "lib/utils" - -const Login: NextPage<{ host: string }> = () => { - const { t } = useTranslation("login") - const { t: rootT } = useTranslation() - - const [authCode, setAuthCode] = useState("") - const router = useRouter() - const [_session, setSession] = useSession() - const fetchStrictCall = useRef(true) - const regenerateNonce = () => { - fetch("/api/auth", { method: "POST" }) - .then((res) => res.json()) - .then((json) => { - setAuthCode(json.authCode) - }) - } - useEffect(() => { - if (fetchStrictCall.current && isDev) { - fetchStrictCall.current = false - return - } - regenerateNonce() - }, []) - - const intervalStrictCall = useRef(true) - useEffect(() => { - if (intervalStrictCall.current && isDev) { - intervalStrictCall.current = false - return - } - const interval = setInterval(() => { - if (!authCode) return - fetch(urlcat(`/api/auth`, { code: authCode }), { method: "GET" }) - .then((res) => res.json()) - .then(async (json) => { - if (json.code === "ok") { - setSession(null as unknown as Session) - const params = new URLSearchParams(window.location.search) - const url = params.get("to") - if (url) { - router.push(url) - } else { - router.push("/") - } - } else if (json.code === "unknown_code") { - regenerateNonce() - } - }) - }, 2500) - return () => clearInterval(interval) - }, [authCode, setSession, router]) - - return ( - <> - - {t("title") + " | " + rootT("name")} - -
-

{t("title")}

-
- {t("step1")} -
-
{host}/auth
-
- {t("step2")} -
-
- {authCode || "--------"} -
-
{t("step2note")}
-
- {t("alternative")} - -
-
- - ) -} - -export default Login diff --git a/nginx-builder.rb b/nginx-builder.rb index 7ce7ae2..0bcf948 100644 --- a/nginx-builder.rb +++ b/nginx-builder.rb @@ -1,7 +1,7 @@ base = File.read("./nginx.conf.base") inject = +"" -%w[api auth/sonolus test/sonolus sonolus rails admin/sidekiq test].each do |route| +%w[api test/sonolus sonolus rails admin/sidekiq test].each do |route| if route.is_a?(Array) from = route[0] to = route[1] diff --git a/nginx.conf.base b/nginx.conf.base index 48a772d..2f15d06 100644 --- a/nginx.conf.base +++ b/nginx.conf.base @@ -26,9 +26,5 @@ server { try_files /app/backend-public$uri /app/frontend-public$uri @front; } - location /auth/assets/ { - alias /app/backend-public/assets/; - } - # inject } diff --git a/sub-audio/pyproject.toml b/sub-audio/pyproject.toml index 857537e..2fe843a 100644 --- a/sub-audio/pyproject.toml +++ b/sub-audio/pyproject.toml @@ -3,7 +3,6 @@ name = "sub-image" version = "0.1.0" description = "" authors = ["sevenc-nanashi "] -readme = "README.md" [tool.poetry.dependencies] python = "^3.9" diff --git a/sub-chart/package.json b/sub-chart/package.json index 756dc12..2bc45c3 100644 --- a/sub-chart/package.json +++ b/sub-chart/package.json @@ -19,15 +19,15 @@ "author": "", "license": "ISC", "dependencies": { - "@sentry/node": "^7.104.0", - "axios": "^1.6.7", + "@sentry/node": "^7.107.0", + "axios": "^1.6.8", "dotenv": "^16.4.5", "express": "^4.18.3", "morgan": "^1.10.0", - "sonolus-pjsekai-engine-extended": "github:sevenc-nanashi/sonolus-pjsekai-engine-extended#build", + "sonolus-pjsekai-engine-extended": "link:../../sonolus/pjsekai-engine-extended", "tempy": "^1.0.1", "url-join": "^4.0.1", - "usctool": "^0.2.1" + "usctool": "^0.3.0" }, "devDependencies": { "@jest/globals": "^29.7.0", @@ -35,7 +35,7 @@ "@types/express": "^4.17.21", "@types/jest": "^29.5.12", "@types/morgan": "^1.9.9", - "@types/node": "^18.19.21", + "@types/node": "^18.19.24", "@types/supertest": "^2.0.16", "@types/url-join": "^4.0.3", "@typescript-eslint/eslint-plugin": "^5.62.0", @@ -49,7 +49,7 @@ "nodemon": "^2.0.22", "prettier": "^2.8.8", "supertest": "^6.3.4", - "terser": "^5.28.1", + "terser": "^5.29.2", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "typescript": "^4.9.5" diff --git a/sub-chart/pnpm-lock.yaml b/sub-chart/pnpm-lock.yaml index eb67699..49e7899 100644 --- a/sub-chart/pnpm-lock.yaml +++ b/sub-chart/pnpm-lock.yaml @@ -6,11 +6,11 @@ settings: dependencies: '@sentry/node': - specifier: ^7.104.0 - version: 7.104.0 + specifier: ^7.107.0 + version: 7.107.0 axios: - specifier: ^1.6.7 - version: 1.6.7 + specifier: ^1.6.8 + version: 1.6.8 dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -21,8 +21,8 @@ dependencies: specifier: ^1.10.0 version: 1.10.0 sonolus-pjsekai-engine-extended: - specifier: github:sevenc-nanashi/sonolus-pjsekai-engine-extended#build - version: github.com/sevenc-nanashi/sonolus-pjsekai-engine-extended/cf7c149c666bd65399daa33560a2aeaac437dc44 + specifier: link:../../sonolus/pjsekai-engine-extended + version: link:../../sonolus/pjsekai-engine-extended tempy: specifier: ^1.0.1 version: 1.0.1 @@ -30,8 +30,8 @@ dependencies: specifier: ^4.0.1 version: 4.0.1 usctool: - specifier: ^0.2.1 - version: 0.2.1 + specifier: ^0.3.0 + version: 0.3.0 devDependencies: '@jest/globals': @@ -50,8 +50,8 @@ devDependencies: specifier: ^1.9.9 version: 1.9.9 '@types/node': - specifier: ^18.19.21 - version: 18.19.21 + specifier: ^18.19.24 + version: 18.19.24 '@types/supertest': specifier: ^2.0.16 version: 2.0.16 @@ -81,7 +81,7 @@ devDependencies: version: 4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.0)(prettier@2.8.8) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.19.21)(ts-node@10.9.2) + version: 29.7.0(@types/node@18.19.24)(ts-node@10.9.2) nodemon: specifier: ^2.0.22 version: 2.0.22 @@ -92,14 +92,14 @@ devDependencies: specifier: ^6.3.4 version: 6.3.4 terser: - specifier: ^5.28.1 - version: 5.28.1 + specifier: ^5.29.2 + version: 5.29.2 ts-jest: specifier: ^29.1.2 version: 29.1.2(@babel/core@7.24.0)(@jest/types@29.6.3)(esbuild@0.17.19)(jest@29.7.0)(typescript@4.9.5) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@18.19.21)(typescript@4.9.5) + version: 10.9.2(@types/node@18.19.24)(typescript@4.9.5) typescript: specifier: ^4.9.5 version: 4.9.5 @@ -731,7 +731,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -752,14 +752,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@18.19.21)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@18.19.24)(ts-node@10.9.2) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -787,7 +787,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 jest-mock: 29.7.0 dev: true @@ -814,7 +814,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 18.19.21 + '@types/node': 18.19.24 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -847,7 +847,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 18.19.21 + '@types/node': 18.19.24 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -935,7 +935,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 18.19.21 + '@types/node': 18.19.24 '@types/yargs': 17.0.32 chalk: 4.1.2 dev: true @@ -959,8 +959,8 @@ packages: engines: {node: '>=6.0.0'} dev: true - /@jridgewell/source-map@0.3.5: - resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} + /@jridgewell/source-map@0.3.6: + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} dependencies: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 @@ -1002,43 +1002,43 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - /@sentry-internal/tracing@7.104.0: - resolution: {integrity: sha512-2z7OijM1J5ndJUiJJElC3iH9qb/Eb8eYm2v8oJhM8WVdc5uCKfrQuYHNgGOnmY2FOCfEUlTmMQGpDw7DJ67L5w==} + /@sentry-internal/tracing@7.107.0: + resolution: {integrity: sha512-le9wM8+OHBbq7m/8P7JUJ1UhSPIty+Z/HmRXc5Z64ODZcOwFV6TmDpYx729IXDdz36XUKmeI+BeM7yQdTTZPfQ==} engines: {node: '>=8'} dependencies: - '@sentry/core': 7.104.0 - '@sentry/types': 7.104.0 - '@sentry/utils': 7.104.0 + '@sentry/core': 7.107.0 + '@sentry/types': 7.107.0 + '@sentry/utils': 7.107.0 dev: false - /@sentry/core@7.104.0: - resolution: {integrity: sha512-XPndD6IGQGd07/EntvYVzOWQUo/Gd7L3DwYFeEKeBv6ByWjbBNmVZFRhU0GPPsCHKyW9yMU9OO9diLSS4ijsRg==} + /@sentry/core@7.107.0: + resolution: {integrity: sha512-C7ogye6+KPyBi8NVL0P8Rxx3Ur7Td8ufnjxosVy678lqY+dcYPk/HONROrzUFYW5fMKWL4/KYnwP+x9uHnkDmw==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.104.0 - '@sentry/utils': 7.104.0 + '@sentry/types': 7.107.0 + '@sentry/utils': 7.107.0 dev: false - /@sentry/node@7.104.0: - resolution: {integrity: sha512-Ixt8qg6IV8gywi4+H1cAtQeglAAww2nwLHybCxAvnu3czdF8w7ifF+o5BY1FmO5UYVCAfr8vEb+XG4CuRrFb7g==} + /@sentry/node@7.107.0: + resolution: {integrity: sha512-UZXkG7uThT2YyPW8AOSKRXp1LbVcBHufa4r1XAwBukA2FKO6HHJPjMUgY6DYVQ6k+BmA56CNfVjYrdLbyjBYYA==} engines: {node: '>=8'} dependencies: - '@sentry-internal/tracing': 7.104.0 - '@sentry/core': 7.104.0 - '@sentry/types': 7.104.0 - '@sentry/utils': 7.104.0 + '@sentry-internal/tracing': 7.107.0 + '@sentry/core': 7.107.0 + '@sentry/types': 7.107.0 + '@sentry/utils': 7.107.0 dev: false - /@sentry/types@7.104.0: - resolution: {integrity: sha512-5bs0xe0+GZR4QBm9Nrqw59o0sv3kBtCosrZDVxBru/dQbrfnB+/kVorvuM0rV3+coNITTKcKDegSZmK1d2uOGQ==} + /@sentry/types@7.107.0: + resolution: {integrity: sha512-H7qcPjPSUWHE/Zf5bR1EE24G0pGVuJgrSx8Tvvl5nKEepswMYlbXHRVSDN0gTk/E5Z7cqf+hUBOpkQgZyps77w==} engines: {node: '>=8'} dev: false - /@sentry/utils@7.104.0: - resolution: {integrity: sha512-ZVg+xZirI9DlOi0NegNVocswdh/8p6QkzlQzDQY2LP2CC6JQdmwi64o0S4rPH4YIHNKQJTpIjduoxeKgd1EO5g==} + /@sentry/utils@7.107.0: + resolution: {integrity: sha512-C6PbN5gHh73MRHohnReeQ60N8rrLYa9LciHue3Ru2290eSThg4CzsPnx4SzkGpkSeVlhhptKtKZ+hp/ha3iVuw==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.104.0 + '@sentry/types': 7.107.0 dev: false /@sinclair/typebox@0.27.8: @@ -1110,13 +1110,13 @@ packages: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.38 - '@types/node': 18.19.21 + '@types/node': 18.19.24 dev: true /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.24 dev: true /@types/cookiejar@2.1.5: @@ -1126,7 +1126,7 @@ packages: /@types/express-serve-static-core@4.17.43: resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.24 '@types/qs': 6.9.12 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -1144,7 +1144,7 @@ packages: /@types/graceful-fs@4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.24 dev: true /@types/http-errors@2.0.4: @@ -1197,11 +1197,11 @@ packages: /@types/morgan@1.9.9: resolution: {integrity: sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.24 dev: true - /@types/node@18.19.21: - resolution: {integrity: sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw==} + /@types/node@18.19.24: + resolution: {integrity: sha512-eghAz3gnbQbvnHqB+mgB2ZR3aH6RhdEmHGS48BnV75KceQPHqabkxKI0BbUSsqhqy2Ddhc2xD/VAR9ySZd57Lw==} dependencies: undici-types: 5.26.5 dev: true @@ -1222,7 +1222,7 @@ packages: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: '@types/mime': 1.3.5 - '@types/node': 18.19.21 + '@types/node': 18.19.24 dev: true /@types/serve-static@1.15.5: @@ -1230,7 +1230,7 @@ packages: dependencies: '@types/http-errors': 2.0.4 '@types/mime': 3.0.4 - '@types/node': 18.19.21 + '@types/node': 18.19.24 dev: true /@types/stack-utils@2.0.3: @@ -1242,7 +1242,7 @@ packages: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 18.19.21 + '@types/node': 18.19.24 dev: true /@types/supertest@2.0.16: @@ -1597,10 +1597,10 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /axios@1.6.7: - resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==} + /axios@1.6.8: + resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} dependencies: - follow-redirects: 1.15.5 + follow-redirects: 1.15.6 form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -1693,8 +1693,8 @@ packages: safe-buffer: 5.1.2 dev: false - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} dev: true @@ -1735,8 +1735,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001591 - electron-to-chromium: 1.4.690 + caniuse-lite: 1.0.30001598 + electron-to-chromium: 1.4.708 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.23.0) dev: true @@ -1771,7 +1771,7 @@ packages: es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 - set-function-length: 1.2.1 + set-function-length: 1.2.2 /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} @@ -1788,8 +1788,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite@1.0.30001591: - resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} + /caniuse-lite@1.0.30001598: + resolution: {integrity: sha512-j8mQRDziG94uoBfeFuqsJUNECW37DXpnvhcMJMdlH2u3MRkq1sAI0LJcXP1i/Py0KbSIC4UDj8YHPrTn5YsL+Q==} dev: true /chalk@2.4.2: @@ -1928,7 +1928,7 @@ packages: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} dev: true - /create-jest@29.7.0(@types/node@18.19.21)(ts-node@10.9.2): + /create-jest@29.7.0(@types/node@18.19.24)(ts-node@10.9.2): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -1937,7 +1937,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.19.21)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@18.19.24)(ts-node@10.9.2) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -1965,6 +1965,33 @@ packages: engines: {node: '>=8'} dev: false + /data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + + /data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + + /data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -2114,8 +2141,8 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false - /electron-to-chromium@1.4.690: - resolution: {integrity: sha512-+2OAGjUx68xElQhydpcbqH50hE8Vs2K6TkAeLhICYfndb67CVH0UsZaijmRUE3rHlIxU1u0jxwhgVe6fK3YANA==} + /electron-to-chromium@1.4.708: + resolution: {integrity: sha512-iWgEEvREL4GTXXHKohhh33+6Y8XkPI5eHihDmm8zUk5Zo7HICEW+wI/j5kJ2tbuNUCXJ/sNXa03ajW635DiJXA==} dev: true /emittery@0.13.1: @@ -2158,7 +2185,7 @@ packages: has-property-descriptors: 1.0.2 has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.1 + hasown: 2.0.2 internal-slot: 1.0.7 is-array-buffer: 3.0.4 is-callable: 1.2.7 @@ -2172,17 +2199,69 @@ packages: object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.0 + safe-array-concat: 1.1.2 safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 string.prototype.trimstart: 1.0.7 typed-array-buffer: 1.0.2 typed-array-byte-length: 1.0.1 typed-array-byte-offset: 1.0.2 typed-array-length: 1.0.5 unbox-primitive: 1.0.2 - which-typed-array: 1.1.14 + which-typed-array: 1.1.15 + dev: true + + /es-abstract@1.23.1: + resolution: {integrity: sha512-r+YVn6hTqQb+P5kK0u3KeDqrmhHKm+OhU/Mw4jSL4eQtOxXmp75fXIUUb3sUqFZOlb/YtW5JRaIfEC3UyjYUZQ==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 dev: true /es-array-method-boxes-properly@1.0.0: @@ -2199,19 +2278,26 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + /es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + dev: true + /es-set-tostringtag@2.0.3: resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.4 has-tostringtag: 1.0.2 - hasown: 2.0.1 + hasown: 2.0.2 dev: true /es-shim-unscopables@1.0.2: resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} dependencies: - hasown: 2.0.1 + hasown: 2.0.2 dev: true /es-to-primitive@1.2.1: @@ -2345,7 +2431,7 @@ packages: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) - hasown: 2.0.1 + hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 minimatch: 3.1.2 @@ -2662,8 +2748,8 @@ packages: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /follow-redirects@1.15.5: - resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==} + /follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -2692,7 +2778,7 @@ packages: dezalgo: 1.0.4 hexoid: 1.0.0 once: 1.4.0 - qs: 6.11.2 + qs: 6.12.0 dev: true /forwarded@0.2.0: @@ -2751,7 +2837,7 @@ packages: function-bind: 1.1.2 has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.1 + hasown: 2.0.2 /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} @@ -2871,8 +2957,8 @@ packages: has-symbols: 1.0.3 dev: true - /hasown@2.0.1: - resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 @@ -2958,7 +3044,7 @@ packages: engines: {node: '>= 0.4'} dependencies: es-errors: 1.3.0 - hasown: 2.0.1 + hasown: 2.0.2 side-channel: 1.0.6 dev: true @@ -2989,7 +3075,7 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 dev: true /is-boolean-object@1.1.2: @@ -3008,7 +3094,14 @@ packages: /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: - hasown: 2.0.1 + hasown: 2.0.2 + dev: true + + /is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + dependencies: + is-typed-array: 1.1.13 dev: true /is-date-object@1.0.5: @@ -3100,7 +3193,7 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} dependencies: - which-typed-array: 1.1.14 + which-typed-array: 1.1.15 dev: true /is-weakref@1.0.2: @@ -3193,7 +3286,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -3214,7 +3307,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@18.19.21)(ts-node@10.9.2): + /jest-cli@29.7.0(@types/node@18.19.24)(ts-node@10.9.2): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -3228,10 +3321,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.19.21)(ts-node@10.9.2) + create-jest: 29.7.0(@types/node@18.19.24)(ts-node@10.9.2) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.19.21)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@18.19.24)(ts-node@10.9.2) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -3242,7 +3335,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@18.19.21)(ts-node@10.9.2): + /jest-config@29.7.0(@types/node@18.19.24)(ts-node@10.9.2): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -3257,7 +3350,7 @@ packages: '@babel/core': 7.24.0 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 babel-jest: 29.7.0(@babel/core@7.24.0) chalk: 4.1.2 ci-info: 3.9.0 @@ -3277,7 +3370,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.2(@types/node@18.19.21)(typescript@4.9.5) + ts-node: 10.9.2(@types/node@18.19.24)(typescript@4.9.5) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -3318,7 +3411,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -3334,7 +3427,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 18.19.21 + '@types/node': 18.19.24 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -3385,7 +3478,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 jest-util: 29.7.0 dev: true @@ -3440,7 +3533,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -3471,7 +3564,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -3523,7 +3616,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -3548,7 +3641,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 18.19.21 + '@types/node': 18.19.24 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -3560,13 +3653,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.19.21 + '@types/node': 18.19.24 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@18.19.21)(ts-node@10.9.2): + /jest@29.7.0(@types/node@18.19.24)(ts-node@10.9.2): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -3579,7 +3672,7 @@ packages: '@jest/core': 29.7.0(ts-node@10.9.2) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.19.21)(ts-node@10.9.2) + jest-cli: 29.7.0(@types/node@18.19.24)(ts-node@10.9.2) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -4139,8 +4232,8 @@ packages: side-channel: 1.0.6 dev: false - /qs@6.11.2: - resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} + /qs@6.12.0: + resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==} engines: {node: '>=0.6'} dependencies: side-channel: 1.0.6 @@ -4236,8 +4329,8 @@ packages: dependencies: queue-microtask: 1.2.3 - /safe-array-concat@1.1.0: - resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==} + /safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} dependencies: call-bind: 1.0.7 @@ -4323,8 +4416,8 @@ packages: - supports-color dev: false - /set-function-length@1.2.1: - resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} dependencies: define-data-property: 1.1.4 @@ -4388,10 +4481,6 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - /sonolus-core@7.4.1: - resolution: {integrity: sha512-eWjZth0/qM3l+lRdXloJRSOE21FZp0lsKT7G7OfYjcxyCYRpPwDovyzbDSjitiz8obONVWwalgCIN6BYcnyrRQ==} - dev: false - /source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: @@ -4444,21 +4533,22 @@ packages: strip-ansi: 6.0.1 dev: true - /string.prototype.trim@1.2.8: - resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + /string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.5 + es-abstract: 1.23.1 + es-object-atoms: 1.0.0 dev: true - /string.prototype.trimend@1.0.7: - resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + /string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.5 + es-object-atoms: 1.0.0 dev: true /string.prototype.trimstart@1.0.7: @@ -4508,7 +4598,7 @@ packages: formidable: 2.1.2 methods: 1.1.2 mime: 2.6.0 - qs: 6.11.2 + qs: 6.12.0 semver: 7.6.0 transitivePeerDependencies: - supports-color @@ -4566,12 +4656,12 @@ packages: unique-string: 2.0.0 dev: false - /terser@5.28.1: - resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==} + /terser@5.29.2: + resolution: {integrity: sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==} engines: {node: '>=10'} hasBin: true dependencies: - '@jridgewell/source-map': 0.3.5 + '@jridgewell/source-map': 0.3.6 acorn: 8.11.3 commander: 2.20.3 source-map-support: 0.5.21 @@ -4643,7 +4733,7 @@ packages: bs-logger: 0.2.6 esbuild: 0.17.19 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.19.21)(ts-node@10.9.2) + jest: 29.7.0(@types/node@18.19.24)(ts-node@10.9.2) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -4653,7 +4743,7 @@ packages: yargs-parser: 21.1.1 dev: true - /ts-node@10.9.2(@types/node@18.19.21)(typescript@4.9.5): + /ts-node@10.9.2(@types/node@18.19.24)(typescript@4.9.5): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -4672,7 +4762,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 18.19.21 + '@types/node': 18.19.24 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 @@ -4842,8 +4932,8 @@ packages: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} dev: false - /usctool@0.2.1: - resolution: {integrity: sha512-c3YIZn0JlWIRg50Az1rBCeuItC5uH69Jh6YczvWDzHfMUk28sRQxflDljEUbwOsCJ+azmp5OfJtQDz77XImjtQ==} + /usctool@0.3.0: + resolution: {integrity: sha512-ekR6h70ZYIMhP8f720LfK6rmxbLt7vegASheSGNjMEUSLWzEZkmhDUkUDAVoqsOjOlz3aErz/xBFhlGzq1l4XA==} dependencies: '@sinonjs/text-encoding': 0.7.2 base64-js: 1.5.1 @@ -4889,8 +4979,8 @@ packages: is-symbol: 1.0.4 dev: true - /which-typed-array@1.1.14: - resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.7 @@ -4968,12 +5058,3 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true - - github.com/sevenc-nanashi/sonolus-pjsekai-engine-extended/cf7c149c666bd65399daa33560a2aeaac437dc44: - resolution: {tarball: https://codeload.github.com/sevenc-nanashi/sonolus-pjsekai-engine-extended/tar.gz/cf7c149c666bd65399daa33560a2aeaac437dc44} - name: sonolus-pjsekai-engine-extended - version: 1.2.0 - dependencies: - sonolus-core: 7.4.1 - usctool: 0.2.1 - dev: false