Merge remote-tracking branch 'origin/main' into staging

This commit is contained in:
Andrey Antukh 2026-05-18 15:23:46 +02:00
commit 208182cab1
7 changed files with 810 additions and 58 deletions

View File

@ -12,6 +12,7 @@
[app.common.time :as ct]
[app.common.uri :as u]
[app.db :as db]
[app.http.access-token :as actoken]
[app.http.session :as session]
[app.storage :as sto]
[integrant.core :as ig]
@ -79,9 +80,17 @@
(let [bucket (-> obj meta :bucket)]
(not (contains? public-buckets bucket))))
(defn- authenticated?
"Check if the request has an authenticated profile, either via session
or access token."
[request]
(or (some? (::session/profile-id request))
(some? (::actoken/profile-id request))))
(defn objects-handler
"Handler that serves storage objects by id.
For non-public buckets (e.g. profile), requires an authenticated session."
For non-public buckets (e.g. profile), requires authentication
via session cookie or access token."
[{:keys [::sto/storage] :as cfg} request]
(let [id (get-id request)
obj (sto/get-object storage id)]
@ -90,7 +99,7 @@
{::yres/status 404}
(and (requires-auth? obj)
(nil? (::session/profile-id request)))
(not (authenticated? request)))
{::yres/status 401}
:else
@ -128,7 +137,8 @@
(defmethod ig/init-key ::routes
[_ cfg]
["/assets" {:middleware [[session/authz cfg]]}
["/assets" {:middleware [[session/authz cfg]
[actoken/authz cfg]]}
["/by-id/:id" {:handler (partial objects-handler cfg)}]
["/by-file-media-id/:id" {:handler (partial file-objects-handler cfg)}]
["/by-file-media-id/:id/thumbnail" {:handler (partial file-thumbnails-handler cfg)}]])

View File

@ -302,7 +302,9 @@
::http.assets/cache-max-age (ct/duration {:hours 24})
::http.assets/signature-max-age (ct/duration {:hours 24 :minutes 15})
::sto/storage (ig/ref ::sto/storage)
::session/manager (ig/ref ::session/manager)}
::session/manager (ig/ref ::session/manager)
::setup/props (ig/ref ::setup/props)
::db/pool (ig/ref ::db/pool)}
::rpc/climit
{::mtx/metrics (ig/ref ::mtx/metrics)

View File

@ -96,6 +96,7 @@
context (assoc @context :param-style pstyle)]
{::yres/status 200
::yres/headers {"content-type" "text/html; charset=utf-8"}
::yres/body (-> (io/resource template)
(tmpl/render context))})))
(fn [_]

View File

@ -0,0 +1,461 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns backend-tests.http-assets-test
(:require
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.db :as db]
[app.http :as-alias http]
[app.http.access-token :as actoken]
[app.http.assets :as assets]
[app.http.session :as session]
[app.rpc.commands.access-token :as access-token]
[app.storage :as sto]
[backend-tests.helpers :as th]
[clojure.test :as t]
[datoteka.fs :as fs]
[yetti.response :as-alias yres]))
(t/use-fixtures :once th/state-init)
(t/use-fixtures :each (th/serial
th/database-reset
th/clean-storage))
;; ----------------------------------------------------------------
;; Helpers
;; ----------------------------------------------------------------
(defn- configure-storage-backend
"Given storage map, returns a storage configured with the
appropriate backend for assets."
[storage]
(assoc storage ::sto/backend :fs))
(defn- create-storage-object!
"Create a storage object with the given bucket and content."
[storage bucket content]
(sto/put-object! storage {::sto/content (sto/content content)
:bucket bucket
:content-type "text/plain"}))
(defn- make-handler-cfg
"Build a minimal cfg map for the assets handlers."
[storage]
{::sto/storage storage
::assets/path "/assets"})
;; ----------------------------------------------------------------
;; Tests: get-id
;; ----------------------------------------------------------------
(t/deftest get-id-with-valid-uuid
(let [id (uuid/next)
request {:path-params {:id (str id)}}
result (assets/get-id request)]
(t/is (= id result))))
(t/deftest get-id-with-invalid-uuid
(let [request {:path-params {:id "not-a-uuid"}}]
(try
(assets/get-id request)
(t/is false "should have thrown")
(catch Exception e
(t/is (= :not-found (:type (ex-data e))))))))
(t/deftest get-id-with-missing-id
(let [request {:path-params {}}]
(try
(assets/get-id request)
(t/is false "should have thrown")
(catch Exception e
(t/is (= :not-found (:type (ex-data e))))))))
;; ----------------------------------------------------------------
;; Tests: objects-handler — non-existent objects
;; ----------------------------------------------------------------
(t/deftest objects-handler-non-existent-object
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
request {:path-params {:id (str (uuid/next))}}
response (assets/objects-handler cfg request)]
(t/is (= 404 (::yres/status response)))))
(t/deftest objects-handler-invalid-uuid
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
request {:path-params {:id "not-a-uuid"}}]
(try
(assets/objects-handler cfg request)
(t/is false "should have thrown")
(catch Exception e
(t/is (= :not-found (:type (ex-data e))))))))
;; ----------------------------------------------------------------
;; Tests: objects-handler — public buckets (no auth required)
;; ----------------------------------------------------------------
(t/deftest objects-handler-public-bucket-no-auth
;; Objects in public buckets should be accessible without authentication.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)]
(doseq [bucket ["file-media-object"
"file-object-thumbnail"
"team-font-variant"
"file-data-fragment"]]
(t/testing (str "bucket: " bucket)
(let [object (create-storage-object! storage bucket "public data")
request {:path-params {:id (str (:id object))}}
response (assets/objects-handler cfg request)]
(t/is (not= 401 (::yres/status response))
(str "bucket " bucket " should not require auth"))
(t/is (not= 404 (::yres/status response))
(str "bucket " bucket " object should exist")))))))
(t/deftest objects-handler-public-bucket-with-auth
;; Objects in public buckets should also be accessible WITH authentication.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
profile (th/create-profile* 1)
object (create-storage-object! storage "file-media-object" "public data")
request {:path-params {:id (str (:id object))}
::session/profile-id (:id profile)}
response (assets/objects-handler cfg request)]
(t/is (not= 401 (::yres/status response)))
(t/is (not= 404 (::yres/status response)))))
;; ----------------------------------------------------------------
;; Tests: objects-handler — non-public buckets (auth required)
;; ----------------------------------------------------------------
(t/deftest objects-handler-non-public-bucket-no-auth
;; Objects in non-public buckets should return 401 without authentication.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
object (create-storage-object! storage "profile" "profile photo")
request {:path-params {:id (str (:id object))}}
response (assets/objects-handler cfg request)]
(t/is (= 401 (::yres/status response)))))
(t/deftest objects-handler-non-public-bucket-with-session-auth
;; Objects in non-public buckets should be accessible with session auth.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
profile (th/create-profile* 1)
object (create-storage-object! storage "profile" "profile photo")
request {:path-params {:id (str (:id object))}
::session/profile-id (:id profile)}
response (assets/objects-handler cfg request)]
(t/is (not= 401 (::yres/status response)))
(t/is (not= 404 (::yres/status response)))))
(t/deftest objects-handler-non-public-bucket-with-access-token-auth
;; Objects in non-public buckets should be accessible with access token auth.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
profile (th/create-profile* 1)
object (create-storage-object! storage "profile" "profile photo")
request {:path-params {:id (str (:id object))}
::actoken/profile-id (:id profile)}
response (assets/objects-handler cfg request)]
(t/is (not= 401 (::yres/status response)))
(t/is (not= 404 (::yres/status response)))))
(t/deftest objects-handler-non-public-bucket-with-both-auth
;; Objects should be accessible when both session and access token auth are present.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
profile (th/create-profile* 1)
object (create-storage-object! storage "profile" "profile photo")
request {:path-params {:id (str (:id object))}
::session/profile-id (:id profile)
::actoken/profile-id (:id profile)}
response (assets/objects-handler cfg request)]
(t/is (not= 401 (::yres/status response)))
(t/is (not= 404 (::yres/status response)))))
;; ----------------------------------------------------------------
;; Tests: objects-handler — all non-public buckets
;; ----------------------------------------------------------------
(t/deftest objects-handler-all-non-public-buckets-require-auth
;; Verify that all buckets NOT in the public set require authentication.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
profile (th/create-profile* 1)]
(doseq [bucket ["profile"
"tempfile"
"file-data"
"file-thumbnail"
"file-change"]]
(t/testing (str "bucket: " bucket)
(let [object (create-storage-object! storage bucket "some data")
request {:path-params {:id (str (:id object))}}]
;; Without auth → 401
(let [response (assets/objects-handler cfg request)]
(t/is (= 401 (::yres/status response))
(str "bucket " bucket " should require auth")))
;; With session auth → not 401
(let [response (assets/objects-handler cfg (assoc request ::session/profile-id (:id profile)))]
(t/is (not= 401 (::yres/status response))
(str "bucket " bucket " should be accessible with session auth")))
;; With access token auth → not 401
(let [response (assets/objects-handler cfg (assoc request ::actoken/profile-id (:id profile)))]
(t/is (not= 401 (::yres/status response))
(str "bucket " bucket " should be accessible with access token auth"))))))))
;; ----------------------------------------------------------------
;; Tests: objects-handler — serve-object response (FS backend)
;; ----------------------------------------------------------------
(t/deftest objects-handler-fs-backend-serves-object
;; Verify that the FS backend returns the correct response structure.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
profile (th/create-profile* 1)
object (create-storage-object! storage "file-media-object" "file content")
request {:path-params {:id (str (:id object))}
::session/profile-id (:id profile)}
response (assets/objects-handler cfg request)]
;; FS backend returns 204 with x-accel-redirect header
(t/is (= 204 (::yres/status response)))
(t/is (some? (get (::yres/headers response) "x-accel-redirect")))
(t/is (= "text/plain" (get (::yres/headers response) "content-type")))
(t/is (some? (get (::yres/headers response) "cache-control")))))
(t/deftest objects-handler-fs-backend-accel-redirect-path
;; Verify that x-accel-redirect contains the object's relative path.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
object (create-storage-object! storage "file-media-object" "file content")
request {:path-params {:id (str (:id object))}}
response (assets/objects-handler cfg request)
redirect (get (::yres/headers response) "x-accel-redirect")]
;; The redirect path should contain the object's relative path
(t/is (string? redirect))
(t/is (clojure.string/includes? redirect (sto/object->relative-path object)))))
;; ----------------------------------------------------------------
;; Tests: objects-handler — cache headers
;; ----------------------------------------------------------------
(t/deftest objects-handler-cache-control-header
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
object (create-storage-object! storage "file-media-object" "content")
request {:path-params {:id (str (:id object))}}
response (assets/objects-handler cfg request)
cc (get (::yres/headers response) "cache-control")]
(t/is (string? cc))
(t/is (clojure.string/starts-with? cc "max-age="))))
;; ----------------------------------------------------------------
;; Tests: middleware integration — session auth end-to-end
;; ----------------------------------------------------------------
(t/deftest session-auth-integration
;; Test the full session auth flow: create session → assign token →
;; authenticate request → access protected asset.
(let [cfg th/*system*
manager (session/inmemory-manager)
profile (th/create-profile* 1)
;; Create a session and generate a token
session (->> (session/create-session manager {:profile-id (:id profile)
:user-agent "test-agent"})
(#'session/assign-token cfg))
;; Create a storage object in a non-public bucket
storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
object (create-storage-object! storage "profile" "profile data")
;; Simulate what the middleware chain does:
;; 1. mw/auth extracts token from cookie and sets ::http/auth-data
;; 2. session/authz reads ::http/auth-data and sets ::session/profile-id
request {::http/auth-data {:type :cookie
:token (:token session)
:claims {:sid (:id session)
:uid (:id profile)}
:metadata {:ver 1}}
:path-params {:id (str (:id object))}}
;; Apply session/authz middleware
handler (#'session/wrap-authz
(fn [req]
;; This is where the actual handler would be called
;; We verify that ::session/profile-id is set
req)
{::session/manager manager})
result (handler request)]
;; Verify the session auth set the profile-id
(t/is (= (:id profile) (::session/profile-id result)))
(t/is (some? (::session/session result)))))
;; ----------------------------------------------------------------
;; Tests: middleware integration — access token auth end-to-end
;; ----------------------------------------------------------------
(t/deftest access-token-auth-integration
;; Test the full access token flow: create token → authenticate
;; request → access protected asset.
(let [profile (th/create-profile* 1)
;; Create an access token in the database
atoken (db/tx-run! th/*system*
access-token/create-access-token
(:id profile) "test-token" nil nil)
;; Create a storage object in a non-public bucket
storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
object (create-storage-object! storage "profile" "profile data")
;; Simulate what the middleware chain does:
;; 1. mw/auth extracts token from Authorization header and sets ::http/auth-data
;; 2. actoken/authz reads ::http/auth-data and sets ::actoken/profile-id
request {::http/auth-data {:type :token
:token (:token atoken)
:claims {:tid (:id atoken)}}
:path-params {:id (str (:id object))}}
;; Apply actoken/authz middleware
handler (#'actoken/wrap-authz
(fn [req]
;; Verify that ::actoken/profile-id is set
req)
th/*system*)
result (handler request)]
;; Verify the access token auth set the profile-id
(t/is (= (:id profile) (::actoken/profile-id result)))))
;; ----------------------------------------------------------------
;; Tests: middleware chain — combined session + access token
;; ----------------------------------------------------------------
(t/deftest combined-middleware-chain
;; Test that both session/authz and actoken/authz work together
;; in the middleware chain, matching the assets route configuration.
(let [cfg th/*system*
manager (session/inmemory-manager)
profile (th/create-profile* 1)
;; Create a session
session (->> (session/create-session manager {:profile-id (:id profile)
:user-agent "test-agent"})
(#'session/assign-token cfg))
;; Create an access token
atoken (db/tx-run! th/*system*
access-token/create-access-token
(:id profile) "test-token" nil nil)
;; Build the middleware chain like assets routes do:
;; session/authz → actoken/authz → handler
inner-handler (fn [request] request)
with-actoken (#'actoken/wrap-authz inner-handler th/*system*)
with-session (#'session/wrap-authz with-actoken {::session/manager manager})]
(t/testing "session cookie auth sets ::session/profile-id"
(let [request {::http/auth-data {:type :cookie
:token (:token session)
:claims {:sid (:id session)
:uid (:id profile)}
:metadata {:ver 1}}}
result (with-session request)]
(t/is (= (:id profile) (::session/profile-id result)))))
(t/testing "access token auth sets ::actoken/profile-id"
(let [request {::http/auth-data {:type :token
:token (:token atoken)
:claims {:tid (:id atoken)}}}
result (with-session request)]
(t/is (= (:id profile) (::actoken/profile-id result)))))
(t/testing "no auth sets neither profile-id"
(let [request {}
result (with-session request)]
(t/is (nil? (::session/profile-id result)))
(t/is (nil? (::actoken/profile-id result)))))))
;; ----------------------------------------------------------------
;; Tests: objects-handler — edge cases
;; ----------------------------------------------------------------
(t/deftest objects-handler-nil-profile-id-in-session
;; When session auth is present but profile-id is nil (e.g. invalid session),
;; non-public objects should still be denied.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
object (create-storage-object! storage "profile" "data")
request {:path-params {:id (str (:id object))}
::session/profile-id nil}
response (assets/objects-handler cfg request)]
(t/is (= 401 (::yres/status response)))))
(t/deftest objects-handler-nil-profile-id-in-access-token
;; When access token auth is present but profile-id is nil,
;; non-public objects should still be denied.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
object (create-storage-object! storage "profile" "data")
request {:path-params {:id (str (:id object))}
::actoken/profile-id nil}
response (assets/objects-handler cfg request)]
(t/is (= 401 (::yres/status response)))))
(t/deftest objects-handler-empty-request
;; A request with no path-params should raise a not-found error.
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
request {}]
(try
(assets/objects-handler cfg request)
(t/is false "should have thrown")
(catch Exception e
(t/is (= :not-found (:type (ex-data e))))))))
;; ----------------------------------------------------------------
;; Tests: objects-handler — expired objects
;; ----------------------------------------------------------------
(t/deftest objects-handler-expired-object
;; Expired objects should return 404 (get-object filters them out).
(let [storage (-> (:app.storage/storage th/*system*)
(configure-storage-backend))
cfg (make-handler-cfg storage)
profile (th/create-profile* 1)
object (sto/put-object! storage {::sto/content (sto/content "expired")
::sto/expired-at (ct/now)
:bucket "profile"
:content-type "text/plain"})
request {:path-params {:id (str (:id object))}
::session/profile-id (:id profile)}
response (assets/objects-handler cfg request)]
(t/is (= 404 (::yres/status response)))))

View File

@ -12,10 +12,12 @@
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[app.common.schema.test :as smt]
[app.config :as cf]
[app.rpc :as-alias rpc]
[app.rpc.doc :as rpc.doc]
[backend-tests.helpers :as th]
[clojure.test :as t]))
[clojure.test :as t]
[yetti.response :as-alias yres]))
(t/use-fixtures :once th/state-init)
@ -31,6 +33,17 @@
false)))
{:num 15}))
(t/deftest doc-handler-returns-html-content-type
(with-redefs [cf/flags #{:backend-api-doc}]
(let [methods (::rpc/methods th/*system*)
handler (#'rpc.doc/handler :methods methods
:label "main"
:entrypoint "http://localhost/api/main/methods"
:openapi "http://localhost/api/main/doc/openapi"
:template "app/templates/main-api-doc.tmpl")
request {}
response (handler request)]
(t/is (= 200 (::yres/status response)))
(t/is (= "text/html; charset=utf-8"
(get-in response [::yres/headers "content-type"]))))))

View File

@ -11,7 +11,7 @@
},
"type": "module",
"dependencies": {
"archiver": "^8.0.0",
"archiver": "7.0.1",
"cookies": "^0.9.1",
"date-fns": "^4.1.0",
"generic-pool": "^3.9.0",

365
exporter/pnpm-lock.yaml generated
View File

@ -9,8 +9,8 @@ importers:
.:
dependencies:
archiver:
specifier: ^8.0.0
version: 8.0.0
specifier: 7.0.1
version: 7.0.1
cookies:
specifier: ^0.9.1
version: 0.9.1
@ -61,6 +61,14 @@ packages:
'@ioredis/commands@1.5.1':
resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@trysound/sax@0.2.0':
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
engines: {node: '>=10.13.0'}
@ -69,9 +77,29 @@ packages:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
archiver@8.0.0:
resolution: {integrity: sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==}
engines: {node: '>=18'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-regex@6.2.2:
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
engines: {node: '>=12'}
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
ansi-styles@6.2.3:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
archiver-utils@5.0.2:
resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==}
engines: {node: '>= 14'}
archiver@7.0.1:
resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==}
engines: {node: '>= 14'}
async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
@ -84,9 +112,8 @@ packages:
react-native-b4a:
optional: true
balanced-match@4.0.4:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
bare-events@2.8.2:
resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==}
@ -135,9 +162,8 @@ packages:
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
brace-expansion@5.0.6:
resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
engines: {node: 18 || 20 || >=22}
brace-expansion@2.1.0:
resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==}
buffer-crc32@1.0.0:
resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==}
@ -157,9 +183,16 @@ packages:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
compress-commons@7.0.1:
resolution: {integrity: sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==}
engines: {node: '>=18'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
compress-commons@6.0.2:
resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==}
engines: {node: '>= 14'}
cookies@0.9.1:
resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
@ -176,9 +209,13 @@ packages:
engines: {node: '>=0.8'}
hasBin: true
crc32-stream@7.0.1:
resolution: {integrity: sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==}
engines: {node: '>=18'}
crc32-stream@6.0.0:
resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==}
engines: {node: '>= 14'}
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
css-select@5.2.2:
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
@ -232,6 +269,15 @@ packages:
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@ -250,6 +296,10 @@ packages:
fast-fifo@1.3.2:
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
foreground-child@3.3.1:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -259,6 +309,14 @@ packages:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
glob@10.5.0:
resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
@ -281,13 +339,23 @@ packages:
resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==}
engines: {node: '>=12.22.0'}
is-stream@4.0.1:
resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
engines: {node: '>=18'}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
keygrip@1.1.0:
resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
engines: {node: '>= 0.6'}
@ -306,15 +374,26 @@ packages:
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
mdn-data@2.0.28:
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
mdn-data@2.12.2:
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
minimatch@5.1.9:
resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==}
engines: {node: '>=10'}
minimatch@9.0.9:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
engines: {node: '>=16 || 14 >=14.17'}
minipass@7.1.3:
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
engines: {node: '>=16 || 14 >=14.17'}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@ -326,6 +405,17 @@ packages:
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
path-scurry@1.11.1:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
playwright-core@1.60.0:
resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==}
engines: {node: '>=18'}
@ -354,9 +444,8 @@ packages:
resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
readdir-glob@3.0.0:
resolution: {integrity: sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==}
engines: {node: '>=18'}
readdir-glob@1.1.3:
resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
redis-errors@1.2.0:
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
@ -381,6 +470,18 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@ -402,12 +503,28 @@ packages:
streamx@2.25.0:
resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-ansi@7.2.0:
resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
engines: {node: '>=12'}
svgo@https://codeload.github.com/penpot/svgo/tar.gz/a46262c12c0d967708395972c374eb2adead4180:
resolution: {tarball: https://codeload.github.com/penpot/svgo/tar.gz/a46262c12c0d967708395972c374eb2adead4180}
version: 4.0.0
@ -441,6 +558,19 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
ws@8.20.1:
resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==}
engines: {node: '>=10.0.0'}
@ -460,9 +590,9 @@ packages:
xregexp@5.1.2:
resolution: {integrity: sha512-6hGgEMCGhqCTFEJbqmWrNIPqfpdirdGWkqshu7fFZddmTSfgv5Sn9D2SaKloR79s5VUiUlpwzg3CM3G6D3VIlw==}
zip-stream@7.0.5:
resolution: {integrity: sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==}
engines: {node: '>=18'}
zip-stream@6.0.1:
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
engines: {node: '>= 14'}
snapshots:
@ -472,23 +602,53 @@ snapshots:
'@ioredis/commands@1.5.1': {}
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
string-width-cjs: string-width@4.2.3
strip-ansi: 7.2.0
strip-ansi-cjs: strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
'@pkgjs/parseargs@0.11.0':
optional: true
'@trysound/sax@0.2.0': {}
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
archiver@8.0.0:
ansi-regex@5.0.1: {}
ansi-regex@6.2.2: {}
ansi-styles@4.3.0:
dependencies:
async: 3.2.6
buffer-crc32: 1.0.0
is-stream: 4.0.1
color-convert: 2.0.1
ansi-styles@6.2.3: {}
archiver-utils@5.0.2:
dependencies:
glob: 10.5.0
graceful-fs: 4.2.11
is-stream: 2.0.1
lazystream: 1.0.1
lodash: 4.17.21
normalize-path: 3.0.0
readable-stream: 4.7.0
readdir-glob: 3.0.0
archiver@7.0.1:
dependencies:
archiver-utils: 5.0.2
async: 3.2.6
buffer-crc32: 1.0.0
readable-stream: 4.7.0
readdir-glob: 1.1.3
tar-stream: 3.2.0
zip-stream: 7.0.5
zip-stream: 6.0.1
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
@ -498,7 +658,7 @@ snapshots:
b4a@1.8.1: {}
balanced-match@4.0.4: {}
balanced-match@1.0.2: {}
bare-events@2.8.2: {}
@ -536,9 +696,9 @@ snapshots:
boolbase@1.0.0: {}
brace-expansion@5.0.6:
brace-expansion@2.1.0:
dependencies:
balanced-match: 4.0.4
balanced-match: 1.0.2
buffer-crc32@1.0.0: {}
@ -553,11 +713,17 @@ snapshots:
cluster-key-slot@1.1.2: {}
compress-commons@7.0.1:
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
compress-commons@6.0.2:
dependencies:
crc-32: 1.2.2
crc32-stream: 7.0.1
is-stream: 4.0.1
crc32-stream: 6.0.0
is-stream: 2.0.1
normalize-path: 3.0.0
readable-stream: 4.7.0
@ -572,11 +738,17 @@ snapshots:
crc-32@1.2.2: {}
crc32-stream@7.0.1:
crc32-stream@6.0.0:
dependencies:
crc-32: 1.2.2
readable-stream: 4.7.0
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
css-select@5.2.2:
dependencies:
boolbase: 1.0.0
@ -629,6 +801,12 @@ snapshots:
domelementtype: 2.3.0
domhandler: 5.0.3
eastasianwidth@0.2.0: {}
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
entities@4.5.0: {}
event-target-shim@5.0.1: {}
@ -643,11 +821,27 @@ snapshots:
fast-fifo@1.3.2: {}
foreground-child@3.3.1:
dependencies:
cross-spawn: 7.0.6
signal-exit: 4.1.0
fsevents@2.3.2:
optional: true
generic-pool@3.9.0: {}
glob@10.5.0:
dependencies:
foreground-child: 3.3.1
jackspeak: 3.4.3
minimatch: 9.0.9
minipass: 7.1.3
package-json-from-dist: 1.0.1
path-scurry: 1.11.1
graceful-fs@4.2.11: {}
http-errors@2.0.1:
dependencies:
depd: 2.0.0
@ -680,10 +874,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
is-stream@4.0.1: {}
is-fullwidth-code-point@3.0.0: {}
is-stream@2.0.1: {}
isarray@1.0.0: {}
isexe@2.0.0: {}
jackspeak@3.4.3:
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
keygrip@1.1.0:
dependencies:
tsscmp: 1.0.6
@ -698,13 +902,21 @@ snapshots:
lodash@4.17.21: {}
lru-cache@10.4.3: {}
mdn-data@2.0.28: {}
mdn-data@2.12.2: {}
minimatch@10.2.5:
minimatch@5.1.9:
dependencies:
brace-expansion: 5.0.6
brace-expansion: 2.1.0
minimatch@9.0.9:
dependencies:
brace-expansion: 2.1.0
minipass@7.1.3: {}
ms@2.1.3: {}
@ -714,6 +926,15 @@ snapshots:
dependencies:
boolbase: 1.0.0
package-json-from-dist@1.0.1: {}
path-key@3.1.1: {}
path-scurry@1.11.1:
dependencies:
lru-cache: 10.4.3
minipass: 7.1.3
playwright-core@1.60.0: {}
playwright@1.60.0:
@ -751,9 +972,9 @@ snapshots:
process: 0.11.10
string_decoder: 1.3.0
readdir-glob@3.0.0:
readdir-glob@1.1.3:
dependencies:
minimatch: 10.2.5
minimatch: 5.1.9
redis-errors@1.2.0: {}
@ -771,6 +992,14 @@ snapshots:
setprototypeof@1.2.0: {}
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
shebang-regex@3.0.0: {}
signal-exit@4.1.0: {}
source-map-js@1.2.1: {}
source-map-support@0.5.21:
@ -793,6 +1022,18 @@ snapshots:
- bare-abort-controller
- react-native-b4a
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
string-width@5.1.2:
dependencies:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.2.0
string_decoder@1.1.1:
dependencies:
safe-buffer: 5.1.2
@ -801,6 +1042,14 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
strip-ansi@6.0.1:
dependencies:
ansi-regex: 5.0.1
strip-ansi@7.2.0:
dependencies:
ansi-regex: 6.2.2
svgo@https://codeload.github.com/penpot/svgo/tar.gz/a46262c12c0d967708395972c374eb2adead4180:
dependencies:
'@trysound/sax': 0.2.0
@ -843,6 +1092,22 @@ snapshots:
util-deprecate@1.0.2: {}
which@2.0.2:
dependencies:
isexe: 2.0.0
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi@8.1.0:
dependencies:
ansi-styles: 6.2.3
string-width: 5.1.2
strip-ansi: 7.2.0
ws@8.20.1: {}
xml-js@1.6.11:
@ -853,8 +1118,8 @@ snapshots:
dependencies:
'@babel/runtime-corejs3': 7.28.4
zip-stream@7.0.5:
zip-stream@6.0.1:
dependencies:
compress-commons: 7.0.1
normalize-path: 3.0.0
archiver-utils: 5.0.2
compress-commons: 6.0.2
readable-stream: 4.7.0