diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9d8f54ee13..28fa9e4555 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "ghcr.io/devcontainers/features/node:1": { "version": "22" }, - "ghcr.io/devcontainers/features/git-lfs:1.2.3": {}, + "ghcr.io/devcontainers/features/git-lfs:1.2.4": {}, "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} }, "customizations": { diff --git a/.forgejo/workflows-composite/apt-install-from/action.yaml b/.forgejo/workflows-composite/apt-install-from/action.yaml index 615e7cb184..c1c8c950e3 100644 --- a/.forgejo/workflows-composite/apt-install-from/action.yaml +++ b/.forgejo/workflows-composite/apt-install-from/action.yaml @@ -18,7 +18,7 @@ runs: - name: install packages run: | apt-get update -qq - apt-get -q install -qq -y ${PACKAGES} + apt-get -q install --allow-downgrades -qq -y ${PACKAGES} env: PACKAGES: ${{inputs.packages}} - name: remove temporary package list to prevent using it in other steps diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml new file mode 100644 index 0000000000..9e5cfb92ed --- /dev/null +++ b/.forgejo/workflows/testing-integration.yml @@ -0,0 +1,71 @@ +# +# Additional integration tests designed to run once a day when +# `mirror.yml` pushes to https://codeberg.org/forgejo-integration/forgejo +# and send a notification via email should they fail. +# +# For debug purposes: +# +# - uncomment [on].pull_request +# - swap 'forgejo-integration' and 'forgejo-coding' +# - open a pull request at https://codeberg.org/forgejo/forgejo and fix things +# - swap 'forgejo-integration' and 'forgejo-coding' +# - comment [on].pull_request +# + +name: testing-integration + +on: +# pull_request: + push: + tags: 'v[0-9]+.[0-9]+.*' + branches: + - 'forgejo' + - 'v*/forgejo' + +jobs: + test-unit: +# if: vars.ROLE == 'forgejo-coding' + if: vars.ROLE == 'forgejo-integration' + runs-on: docker + container: + image: 'data.forgejo.org/oci/node:22-bookworm' + options: --tmpfs /tmp:exec,noatime + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/workflows-composite/setup-env + - name: install git 2.30 + uses: ./.forgejo/workflows-composite/apt-install-from + with: + packages: git/bullseye git-lfs/bullseye + release: bullseye + - uses: ./.forgejo/workflows-composite/build-backend + - run: | + su forgejo -c 'make test-backend test-check' + timeout-minutes: 120 + env: + RACE_ENABLED: 'true' + TAGS: bindata + test-sqlite: +# if: vars.ROLE == 'forgejo-coding' + if: vars.ROLE == 'forgejo-integration' + runs-on: docker + container: + image: 'data.forgejo.org/oci/node:22-bookworm' + options: --tmpfs /tmp:exec,noatime + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/workflows-composite/setup-env + - name: install git 2.30 + uses: ./.forgejo/workflows-composite/apt-install-from + with: + packages: git/bullseye git-lfs/bullseye + release: bullseye + - uses: ./.forgejo/workflows-composite/build-backend + - run: | + su forgejo -c 'make test-sqlite-migration test-sqlite' + timeout-minutes: 120 + env: + TAGS: sqlite sqlite_unlock_notify + RACE_ENABLED: true + TEST_TAGS: sqlite sqlite_unlock_notify + USE_REPO_TEST_DIR: 1 diff --git a/build.go b/build.go deleted file mode 100644 index d410e171c7..0000000000 --- a/build.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build vendor - -package main - -// Libraries that are included to vendor utilities used during build. -// These libraries will not be included in a normal compilation. - -import ( - // for embed - _ "github.com/shurcooL/vfsgen" -) diff --git a/build/generate-bindata.go b/build/generate-bindata.go index 2fcb7c2f2a..2bdfc39574 100644 --- a/build/generate-bindata.go +++ b/build/generate-bindata.go @@ -1,5 +1,6 @@ // Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later //go:build ignore @@ -7,15 +8,18 @@ package main import ( "bytes" - "crypto/sha1" + "crypto/sha256" "fmt" + "io" + "io/fs" "log" - "net/http" "os" + "path" "path/filepath" "strconv" + "text/template" - "github.com/shurcooL/vfsgen" + "github.com/klauspost/compress/zstd" ) func needsUpdate(dir, filename string) (bool, []byte) { @@ -30,7 +34,7 @@ func needsUpdate(dir, filename string) (bool, []byte) { oldHash = []byte{} } - hasher := sha1.New() + hasher := sha256.New() err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { if err != nil { @@ -51,7 +55,7 @@ func needsUpdate(dir, filename string) (bool, []byte) { newHash := hasher.Sum([]byte{}) - if bytes.Compare(oldHash, newHash) != 0 { + if !bytes.Equal(oldHash, newHash) { return true, newHash } @@ -77,16 +81,268 @@ func main() { } fmt.Printf("generating bindata for %s\n", packageName) - var fsTemplates http.FileSystem = http.Dir(dir) - err := vfsgen.Generate(fsTemplates, vfsgen.Options{ - PackageName: packageName, - BuildTags: "bindata", - VariableName: "Assets", - Filename: filename, - UseGlobalModTime: useGlobalModTime, - }) + + root, err := os.OpenRoot(dir) if err != nil { - log.Fatalf("%v\n", err) + log.Fatal(err) + } + + out, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644) + if err != nil { + log.Fatal(err) + } + defer out.Close() + + if err := generate(root.FS(), packageName, useGlobalModTime, out); err != nil { + log.Fatal(err) } _ = os.WriteFile(filename+".hash", newHash, 0o666) } + +type file struct { + Path string + Name string + UncompressedSize int + CompressedData []byte + UncompressedData []byte +} + +type direntry struct { + Name string + IsDir bool +} + +func generate(fsRoot fs.FS, packageName string, globalTime bool, output io.Writer) error { + enc, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(zstd.SpeedBestCompression)) + if err != nil { + return err + } + + files := []file{} + + dirs := map[string][]direntry{} + + if err := fs.WalkDir(fsRoot, ".", func(filePath string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + entries, err := fs.ReadDir(fsRoot, filePath) + if err != nil { + return err + } + dirEntries := make([]direntry, 0, len(entries)) + for _, entry := range entries { + dirEntries = append(dirEntries, direntry{Name: entry.Name(), IsDir: entry.IsDir()}) + } + dirs[filePath] = dirEntries + return nil + } + + src, err := fs.ReadFile(fsRoot, filePath) + if err != nil { + return err + } + + dst := enc.EncodeAll(src, nil) + if len(dst) < len(src) { + files = append(files, file{ + Path: filePath, + Name: path.Base(filePath), + UncompressedSize: len(src), + CompressedData: dst, + }) + } else { + files = append(files, file{ + Path: filePath, + Name: path.Base(filePath), + UncompressedData: src, + }) + } + return nil + }); err != nil { + return err + } + + return generatedTmpl.Execute(output, map[string]any{ + "Packagename": packageName, + "GlobalTime": globalTime, + "Files": files, + "Dirs": dirs, + }) +} + +var generatedTmpl = template.Must(template.New("").Parse(`// Code generated by efs-gen. DO NOT EDIT. + +//go:build bindata + +package {{.Packagename}} + +import ( + "bytes" + "time" + "io" + "io/fs" + + "github.com/klauspost/compress/zstd" +) + +type normalFile struct { + name string + content []byte +} + +type compressedFile struct { + name string + uncompressedSize int64 + data []byte +} + +var files = map[string]any{ +{{- range .Files}} + "{{.Path}}": {{if .CompressedData}}compressedFile{"{{.Name}}", {{.UncompressedSize}}, []byte({{printf "%+q" .CompressedData}})}{{else}}normalFile{"{{.Name}}", []byte({{printf "%+q" .UncompressedData}})}{{end}}, +{{- end}} +} + +var dirs = map[string][]fs.DirEntry{ +{{- range $key, $entry := .Dirs}} + "{{$key}}": { +{{- range $entry}} + direntry{"{{.Name}}", {{.IsDir}}}, +{{- end}} + }, +{{- end}} +} + +type assets struct{} + +var Assets = assets{} + +func (a assets) Open(name string) (fs.File, error) { + f, ok := files[name] + if !ok { + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} + } + + switch f := f.(type) { + case normalFile: + return file{name: f.name, size: int64(len(f.content)), data: bytes.NewReader(f.content)}, nil + case compressedFile: + r, _ := zstd.NewReader(bytes.NewReader(f.data)) + return &compressFile{name: f.name, size: f.uncompressedSize, data: r, content: f.data}, nil + default: + panic("unknown file type") + } +} + +func (a assets) ReadDir(name string) ([]fs.DirEntry, error) { + d, ok := dirs[name] + if !ok { + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} + } + return d, nil +} + +type file struct { + name string + size int64 + data io.ReadSeeker +} + +var _ io.ReadSeeker = (*file)(nil) + +func (f file) Stat() (fs.FileInfo, error) { + return fileinfo{name: f.name, size: f.size}, nil +} + +func (f file) Read(p []byte) (int, error) { + return f.data.Read(p) +} + +func (f file) Seek(offset int64, whence int) (int64, error) { + return f.data.Seek(offset, whence) +} + +func (f file) Close() error { return nil } + +type compressFile struct { + name string + size int64 + data *zstd.Decoder + content []byte + zstdPos int64 + seekPos int64 +} + +var _ io.ReadSeeker = (*compressFile)(nil) + +func (f *compressFile) Stat() (fs.FileInfo, error) { + return fileinfo{name: f.name, size: f.size}, nil +} + +func (f *compressFile) Read(p []byte) (int, error) { + if f.zstdPos > f.seekPos { + if err := f.data.Reset(bytes.NewReader(f.content)); err != nil { + return 0, err + } + f.zstdPos = 0 + } + if f.zstdPos < f.seekPos { + if _, err := io.CopyN(io.Discard, f.data, f.seekPos - f.zstdPos); err != nil { + return 0, err + } + f.zstdPos = f.seekPos + } + n, err := f.data.Read(p) + f.zstdPos += int64(n) + f.seekPos = f.zstdPos + return n, err +} + +func (f *compressFile) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + f.seekPos = 0 + offset + case io.SeekCurrent: + f.seekPos += offset + case io.SeekEnd: + f.seekPos = f.size + offset + } + return f.seekPos, nil +} + +func (f *compressFile) Close() error { + f.data.Close() + return nil +} + +func (f *compressFile) ZstdBytes() []byte { return f.content } + +type fileinfo struct { + name string + size int64 +} + +func (f fileinfo) Name() string { return f.name } +func (f fileinfo) Size() int64 { return f.size } +func (f fileinfo) Mode() fs.FileMode { return 0o444 } +func (f fileinfo) ModTime() time.Time { return {{if .GlobalTime}}GlobalModTime(f.name){{else}}time.Unix(0, 0){{end}} } +func (f fileinfo) IsDir() bool { return false } +func (f fileinfo) Sys() any { return nil } + +type direntry struct { + name string + isDir bool +} + +func (d direntry) Name() string { return d.name } +func (d direntry) IsDir() bool { return d.isDir } +func (d direntry) Type() fs.FileMode { + if d.isDir { + return 0o755 | fs.ModeDir + } + return 0o444 +} +func (direntry) Info() (fs.FileInfo, error) { return nil, fs.ErrNotExist } +`)) diff --git a/flake.lock b/flake.lock index 90672733d5..dcf7755013 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1733392399, - "narHash": "sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG+cBns=", + "lastModified": 1749285348, + "narHash": "sha256-frdhQvPbmDYaScPFiCnfdh3B/Vh81Uuoo0w5TkWmmjU=", "owner": "nixos", "repo": "nixpkgs", - "rev": "d0797a04b81caeae77bcff10a9dde78bc17f5661", + "rev": "3e3afe5174c561dee0df6f2c2b2236990146329f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index daa66d1d04..01b23258b9 100644 --- a/flake.nix +++ b/flake.nix @@ -3,35 +3,20 @@ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { - nixpkgs, - flake-utils, - ... - }: + outputs = + { + nixpkgs, + flake-utils, + ... + }: flake-utils.lib.eachDefaultSystem ( - system: let + system: + let pkgs = nixpkgs.legacyPackages.${system}; - in { - devShells.default = pkgs.mkShell { - buildInputs = with pkgs; [ - # generic - git - git-lfs - gnumake - gnused - gnutar - gzip - - # frontend - nodejs_20 - - # backend - gofumpt - sqlite - go - gopls - ]; - }; + in + { + devShells.default = import ./shell.nix { inherit pkgs; }; + formatter = pkgs.nixfmt-rfc-style; } ); } diff --git a/go.mod b/go.mod index 66519155b8..0eca1be551 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( code.gitea.io/actions-proto-go v0.4.0 code.gitea.io/sdk/gitea v0.21.0 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 - connectrpc.com/connect v1.17.0 + connectrpc.com/connect v1.18.1 github.com/42wim/httpsig v1.2.3 github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 @@ -89,7 +89,6 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/sergi/go-diff v1.4.0 - github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 github.com/ulikunitz/xz v0.5.12 @@ -222,7 +221,6 @@ require ( github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect @@ -246,8 +244,6 @@ require ( replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 - replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.26.0 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 diff --git a/go.sum b/go.sum index 994dad4aac..18e37ae5f6 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= -connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk= -connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= @@ -384,8 +384,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= -github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI= -github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= @@ -504,8 +502,6 @@ github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCw github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs= -github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index 8d54ae5e4a..48c6728f43 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -5,10 +5,10 @@ package assetfs import ( "context" + "errors" "fmt" "io" "io/fs" - "net/http" "os" "path/filepath" "slices" @@ -25,7 +25,7 @@ import ( // Layer represents a layer in a layered asset file-system. It has a name and works like http.FileSystem type Layer struct { name string - fs http.FileSystem + fs fs.FS localPath string } @@ -34,10 +34,18 @@ func (l *Layer) Name() string { } // Open opens the named file. The caller is responsible for closing the file. -func (l *Layer) Open(name string) (http.File, error) { +func (l *Layer) Open(name string) (fs.File, error) { return l.fs.Open(name) } +func (l *Layer) ReadDir(name string) ([]fs.DirEntry, error) { + dirEntries, err := fs.ReadDir(l.fs, name) + if err != nil && errors.Is(err, fs.ErrNotExist) { + err = nil + } + return dirEntries, err +} + // Local returns a new Layer with the given name, it serves files from the given local path. func Local(name, base string, sub ...string) *Layer { // TODO: the old behavior (StaticRootPath might not be absolute), not ideal, just keep the same as before @@ -48,11 +56,18 @@ func Local(name, base string, sub ...string) *Layer { panic(fmt.Sprintf("Unable to get absolute path for %q: %v", base, err)) } root := util.FilePathJoinAbs(base, sub...) - return &Layer{name: name, fs: http.Dir(root), localPath: root} + fsRoot, err := os.OpenRoot(root) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return nil + } + panic(fmt.Sprintf("Unable to open layer %q", err)) + } + return &Layer{name: name, fs: fsRoot.FS(), localPath: root} } // Bindata returns a new Layer with the given name, it serves files from the given bindata asset. -func Bindata(name string, fs http.FileSystem) *Layer { +func Bindata(name string, fs fs.FS) *Layer { return &Layer{name: name, fs: fs} } @@ -65,11 +80,11 @@ type LayeredFS struct { // Layered returns a new LayeredFS with the given layers. The first layer is the top layer. func Layered(layers ...*Layer) *LayeredFS { - return &LayeredFS{layers: layers} + return &LayeredFS{layers: slices.DeleteFunc(layers, func(layer *Layer) bool { return layer == nil })} } // Open opens the named file. The caller is responsible for closing the file. -func (l *LayeredFS) Open(name string) (http.File, error) { +func (l *LayeredFS) Open(name string) (fs.File, error) { for _, layer := range l.layers { f, err := layer.Open(name) if err == nil || !os.IsNotExist(err) { @@ -102,29 +117,18 @@ func (l *LayeredFS) ReadLayeredFile(elems ...string) ([]byte, string, error) { return nil, "", fs.ErrNotExist } -func shouldInclude(info fs.FileInfo, fileMode ...bool) bool { +func shouldInclude(info fs.DirEntry, fileMode ...bool) bool { if util.CommonSkip(info.Name()) { return false } if len(fileMode) == 0 { return true } else if len(fileMode) == 1 { - return fileMode[0] == !info.Mode().IsDir() + return fileMode[0] == !info.IsDir() } panic("too many arguments for fileMode in shouldInclude") } -func readDir(layer *Layer, name string) ([]fs.FileInfo, error) { - f, err := layer.Open(name) - if os.IsNotExist(err) { - return nil, nil - } else if err != nil { - return nil, err - } - defer f.Close() - return f.Readdir(-1) -} - // ListFiles lists files/directories in the given directory. The fileMode controls the returned files. // * omitted: all files and directories will be returned. // * true: only files will be returned. @@ -133,7 +137,7 @@ func readDir(layer *Layer, name string) ([]fs.FileInfo, error) { func (l *LayeredFS) ListFiles(name string, fileMode ...bool) ([]string, error) { fileSet := make(container.Set[string]) for _, layer := range l.layers { - infos, err := readDir(layer, name) + infos, err := layer.ReadDir(name) if err != nil { return nil, err } @@ -162,7 +166,7 @@ func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, err var list func(dir string) error list = func(dir string) error { for _, layer := range layers { - infos, err := readDir(layer, dir) + infos, err := layer.ReadDir(dir) if err != nil { return err } diff --git a/modules/git/git_test.go b/modules/git/git_test.go index 1f9060247f..01200dba68 100644 --- a/modules/git/git_test.go +++ b/modules/git/git_test.go @@ -105,6 +105,10 @@ func TestSyncConfigGPGFormat(t *testing.T) { }) t.Run("SSH format", func(t *testing.T) { + if CheckGitVersionAtLeast("2.34.0") != nil { + t.SkipNow() + } + r, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) f, err := r.OpenFile("ssh-keygen", os.O_CREATE|os.O_TRUNC, 0o700) diff --git a/modules/public/public.go b/modules/public/public.go index 174936fd4a..a7db5b62e9 100644 --- a/modules/public/public.go +++ b/modules/public/public.go @@ -6,6 +6,7 @@ package public import ( "bytes" "io" + "io/fs" "net/http" "os" "path" @@ -59,7 +60,7 @@ func setWellKnownContentType(w http.ResponseWriter, file string) { } } -func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) { +func handleRequest(w http.ResponseWriter, req *http.Request, fs fs.FS, file string) { // actually, fs (http.FileSystem) is designed to be a safe interface, relative paths won't bypass its parent directory, it's also fine to do a clean here f, err := fs.Open(util.PathJoinRelX(file)) if err != nil { @@ -86,33 +87,31 @@ func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, return } - serveContent(w, req, fi, fi.ModTime(), f) + serveContent(w, req, fi.Name(), fi.ModTime(), f.(io.ReadSeeker)) } -type GzipBytesProvider interface { - GzipBytes() []byte +type ZstdBytesProvider interface { + ZstdBytes() []byte } // serveContent serve http content -func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) { - setWellKnownContentType(w, fi.Name()) +func serveContent(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, content io.ReadSeeker) { + setWellKnownContentType(w, name) encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding")) - if encodings.Contains("gzip") { - // try to provide gzip content directly from bindata (provided by vfsgen۰CompressedFileInfo) - if compressed, ok := fi.(GzipBytesProvider); ok { - rdGzip := bytes.NewReader(compressed.GzipBytes()) - // all gzipped static files (from bindata) are managed by Gitea, so we can make sure every file has the correct ext name - // then we can get the correct Content-Type, we do not need to do http.DetectContentType on the decompressed data + if encodings.Contains("zstd") { + // If the file was compressed, use the bytes directly. + if compressed, ok := content.(ZstdBytesProvider); ok { + rdZstd := bytes.NewReader(compressed.ZstdBytes()) if w.Header().Get("Content-Type") == "" { w.Header().Set("Content-Type", "application/octet-stream") } - w.Header().Set("Content-Encoding", "gzip") - httpcache.ServeContentWithCacheControl(w, req, fi.Name(), modtime, rdGzip) + w.Header().Set("Content-Encoding", "zstd") + httpcache.ServeContentWithCacheControl(w, req, name, modtime, rdZstd) return } } - httpcache.ServeContentWithCacheControl(w, req, fi.Name(), modtime, content) + httpcache.ServeContentWithCacheControl(w, req, name, modtime, content) return } diff --git a/modules/public/serve_static.go b/modules/public/serve_static.go index e19bd976eb..148d789bba 100644 --- a/modules/public/serve_static.go +++ b/modules/public/serve_static.go @@ -12,8 +12,6 @@ import ( "forgejo.org/modules/timeutil" ) -var _ GzipBytesProvider = (*vfsgen۰CompressedFileInfo)(nil) - // GlobalModTime provide a global mod time for embedded asset files func GlobalModTime(filename string) time.Time { return timeutil.GetExecutableModTime() diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 96d0c4bc63..c7e9f65a68 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -93,5 +93,6 @@ "editor.textarea.tab_hint": "Line already indented. Press Tab again or Escape to leave the editor.", "editor.textarea.shift_tab_hint": "No indentation on this line. Press Shift + Tab again or Escape to leave the editor.", "admin.dashboard.cleanup_offline_runners": "Cleanup offline runners", + "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more", "meta.last_line": "Thank you for translating Forgejo! This line isn't seen by the users but it serves other purposes in the translation management. You can place a fun fact in the translation instead of translating it." } diff --git a/package-lock.json b/package-lock.json index cfb4446a3a..473a334c25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "pdfobject": "2.3.0", "postcss": "8.5.4", "postcss-loader": "8.1.1", - "postcss-nesting": "13.0.1", + "postcss-nesting": "13.0.2", "pretty-ms": "9.0.0", "sortablejs": "1.15.6", "swagger-ui-dist": "5.17.14", @@ -62,13 +62,13 @@ "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.52.0", + "@playwright/test": "1.53.0", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin-js": "4.4.1", "@stylistic/stylelint-plugin": "3.1.2", "@vitejs/plugin-vue": "5.2.4", - "@vitest/coverage-v8": "3.2.2", - "@vitest/eslint-plugin": "1.2.1", + "@vitest/coverage-v8": "3.2.3", + "@vitest/eslint-plugin": "1.2.2", "@vue/test-utils": "2.4.6", "eslint": "9.28.0", "eslint-import-resolver-typescript": "4.4.3", @@ -77,7 +77,7 @@ "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", "eslint-plugin-playwright": "2.2.0", - "eslint-plugin-regexp": "2.8.0", + "eslint-plugin-regexp": "2.9.0", "eslint-plugin-sonarjs": "3.0.2", "eslint-plugin-toml": "0.12.0", "eslint-plugin-unicorn": "59.0.1", @@ -86,9 +86,9 @@ "eslint-plugin-vue-scoped-css": "2.10.0", "eslint-plugin-wc": "2.2.1", "globals": "16.1.0", - "happy-dom": "17.6.3", + "happy-dom": "18.0.0", "license-checker-rseidelsohn": "4.4.2", - "markdownlint-cli": "0.44.0", + "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.2", "stylelint": "16.20.0", @@ -97,9 +97,9 @@ "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", "typescript": "5.8.3", - "typescript-eslint": "8.33.1", + "typescript-eslint": "8.34.0", "vite-string-plugin": "1.3.4", - "vitest": "3.2.2" + "vitest": "3.2.3" }, "engines": { "node": ">= 20.0.0" @@ -2131,13 +2131,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", - "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.0.tgz", + "integrity": "sha512-15hjKreZDcp7t6TL/7jkAo6Df5STZN09jGiv5dbP9A6vMVncXRqE7/B2SncsyOwrkZRBH2i6/TPOL8BVmm3c7w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.52.0" + "playwright": "1.53.0" }, "bin": { "playwright": "cli.js" @@ -3448,9 +3448,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", - "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", + "version": "20.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", + "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3493,18 +3493,25 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.1.tgz", - "integrity": "sha512-TDCXj+YxLgtvxvFlAvpoRv9MAncDLBV2oT9Bd7YBGC/b/sEURoOYuIwLI99rjWOfY3QtDzO+mk0n4AmdFExW8A==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", + "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/type-utils": "8.33.1", - "@typescript-eslint/utils": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/type-utils": "8.34.0", + "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3518,7 +3525,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.33.1", + "@typescript-eslint/parser": "^8.34.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -3534,16 +3541,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.1.tgz", - "integrity": "sha512-qwxv6dq682yVvgKKp2qWwLgRbscDAYktPptK4JPojCwwi3R9cwrvIxS4lvBpzmcqzR4bdn54Z0IG1uHFskW4dA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", + "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/typescript-estree": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4" }, "engines": { @@ -3559,14 +3566,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.1.tgz", - "integrity": "sha512-DZR0efeNklDIHHGRpMpR5gJITQpu6tLr9lDJnKdONTC7vvzOlLAG/wcfxcdxEWrbiZApcoBCzXqU/Z458Za5Iw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", + "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.33.1", - "@typescript-eslint/types": "^8.33.1", + "@typescript-eslint/tsconfig-utils": "^8.34.0", + "@typescript-eslint/types": "^8.34.0", "debug": "^4.3.4" }, "engines": { @@ -3581,14 +3588,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.1.tgz", - "integrity": "sha512-dM4UBtgmzHR9bS0Rv09JST0RcHYearoEoo3pG5B6GoTR9XcyeqX87FEhPo+5kTvVfKCvfHaHrcgeJQc6mrDKrA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", + "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1" + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3599,9 +3606,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.1.tgz", - "integrity": "sha512-STAQsGYbHCF0/e+ShUQ4EatXQ7ceh3fBCXkNU7/MZVKulrlq1usH7t2FhxvCpuCi5O5oi1vmVaAjrGeL71OK1g==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", + "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", "dev": true, "license": "MIT", "engines": { @@ -3616,14 +3623,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.1.tgz", - "integrity": "sha512-1cG37d9xOkhlykom55WVwG2QRNC7YXlxMaMzqw2uPeJixBFfKWZgaP/hjAObqMN/u3fr5BrTwTnc31/L9jQ2ww==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", + "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.33.1", - "@typescript-eslint/utils": "8.33.1", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/utils": "8.34.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3640,9 +3647,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.1.tgz", - "integrity": "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "dev": true, "license": "MIT", "engines": { @@ -3654,16 +3661,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.1.tgz", - "integrity": "sha512-+s9LYcT8LWjdYWu7IWs7FvUxpQ/DGkdjZeE/GGulHvv8rvYwQvVaUZ6DE+j5x/prADUgSbbCWZ2nPI3usuVeOA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", + "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.33.1", - "@typescript-eslint/tsconfig-utils": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", + "@typescript-eslint/project-service": "8.34.0", + "@typescript-eslint/tsconfig-utils": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3699,16 +3706,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.1.tgz", - "integrity": "sha512-52HaBiEQUaRYqAXpfzWSR2U3gxk92Kw006+xZpElaPMg3C4PgM+A5LqwoQI1f9E5aZ/qlxAZxzm42WX+vn92SQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", + "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/typescript-estree": "8.33.1" + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3723,13 +3730,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.1.tgz", - "integrity": "sha512-3i8NrFcZeeDHJ+7ZUuDkGT+UHq+XoFGsymNK2jZCOHcfEzRQ0BdpRtdpSx/Iyf3MHLWIcLS0COuOPibKQboIiQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", + "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.33.1", + "@typescript-eslint/types": "8.34.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3996,9 +4003,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.2.tgz", - "integrity": "sha512-RVAi5xnqedSKvaoQyCTWvncMk8eYZcTTOsLK7XmnfOEvdGP/O/upA0/MA8Ss+Qs++mj0GcSRi/whR0S5iBPpTQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.3.tgz", + "integrity": "sha512-D1QKzngg8PcDoCE8FHSZhREDuEy+zcKmMiMafYse41RZpBE5EDJyKOTdqK3RQfsV2S2nyKor5KCs8PyPRFqKPg==", "dev": true, "license": "MIT", "dependencies": { @@ -4020,8 +4027,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.2.2", - "vitest": "3.2.2" + "@vitest/browser": "3.2.3", + "vitest": "3.2.3" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -4040,9 +4047,9 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.2.1.tgz", - "integrity": "sha512-JQr1jdVcrsoS7Sdzn83h9sq4DvREf9Q/onTZbJCqTVlv/76qb+TZrLv/9VhjnjSMHweQH5FdpMDeCR6aDe2fgw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.2.2.tgz", + "integrity": "sha512-R8NwW+VxyKqVGcMfYsUbdThQyMbtNcoeg+jJeTgMHqWdFdcS0nrODAQXhkplvWzgd7jIJ+GQeydGqFLibsxMxg==", "dev": true, "license": "MIT", "dependencies": { @@ -4063,15 +4070,15 @@ } }, "node_modules/@vitest/expect": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.2.tgz", - "integrity": "sha512-ipHw0z669vEMjzz3xQE8nJX1s0rQIb7oEl4jjl35qWTwm/KIHERIg/p/zORrjAaZKXfsv7IybcNGHwhOOAPMwQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.3.tgz", + "integrity": "sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.2", - "@vitest/utils": "3.2.2", + "@vitest/spy": "3.2.3", + "@vitest/utils": "3.2.3", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -4080,13 +4087,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.2.tgz", - "integrity": "sha512-jKojcaRyIYpDEf+s7/dD3LJt53c0dPfp5zCPXz9H/kcGrSlovU/t1yEaNzM9oFME3dcd4ULwRI/x0Po1Zf+LTw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.3.tgz", + "integrity": "sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.2", + "@vitest/spy": "3.2.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -4134,9 +4141,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.2.tgz", - "integrity": "sha512-FY4o4U1UDhO9KMd2Wee5vumwcaHw7Vg4V7yR4Oq6uK34nhEJOmdRYrk3ClburPRUA09lXD/oXWZ8y/Sdma0aUQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.3.tgz", + "integrity": "sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==", "dev": true, "license": "MIT", "dependencies": { @@ -4147,27 +4154,28 @@ } }, "node_modules/@vitest/runner": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.2.tgz", - "integrity": "sha512-GYcHcaS3ejGRZYed2GAkvsjBeXIEerDKdX3orQrBJqLRiea4NSS9qvn9Nxmuy1IwIB+EjFOaxXnX79l8HFaBwg==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.3.tgz", + "integrity": "sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.2", - "pathe": "^2.0.3" + "@vitest/utils": "3.2.3", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.2.tgz", - "integrity": "sha512-aMEI2XFlR1aNECbBs5C5IZopfi5Lb8QJZGGpzS8ZUHML5La5wCbrbhLOVSME68qwpT05ROEEOAZPRXFpxZV2wA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.3.tgz", + "integrity": "sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.2", + "@vitest/pretty-format": "3.2.3", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -4186,9 +4194,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.2.tgz", - "integrity": "sha512-6Utxlx3o7pcTxvp0u8kUiXtRFScMrUg28KjB3R2hon7w4YqOFAEA9QwzPVVS1QNL3smo4xRNOpNZClRVfpMcYg==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.3.tgz", + "integrity": "sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==", "dev": true, "license": "MIT", "dependencies": { @@ -4199,13 +4207,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.2.tgz", - "integrity": "sha512-qJYMllrWpF/OYfWHP32T31QCaLa3BAzT/n/8mNGhPdVcjY+JYazQFO1nsJvXU12Kp1xMpNY4AGuljPTNjQve6A==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.3.tgz", + "integrity": "sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.2", + "@vitest/pretty-format": "3.2.3", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -7373,9 +7381,9 @@ } }, "node_modules/eslint-plugin-regexp": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.8.0.tgz", - "integrity": "sha512-xme90IvkMgdyS+NJC21FM0H6ek4urGsdlIFTXpZRqH2BKJKVSd8hRbyrCpbcqfGBi2jth3eQoLiO3RC1gxZHiw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.9.0.tgz", + "integrity": "sha512-9WqJMnOq8VlE/cK+YAo9C9YHhkOtcEtEk9d12a+H7OSZFwlpI6stiHmYPGa2VE0QhTzodJyhlyprUaXDZLgHBw==", "dev": true, "license": "MIT", "dependencies": { @@ -8428,13 +8436,14 @@ } }, "node_modules/happy-dom": { - "version": "17.6.3", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.6.3.tgz", - "integrity": "sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-18.0.0.tgz", + "integrity": "sha512-o3p2Axi1EdIfMaOUulDzO/5yXzLLV0g/54eLPVrkt3u20r3yOuOenHpyp2clAJ0eHMc+HyE139ulQxl+8pEJIw==", "dev": true, "license": "MIT", "dependencies": { - "webidl-conversions": "^7.0.0", + "@types/node": "^20.0.0", + "@types/whatwg-mimetype": "^3.0.2", "whatwg-mimetype": "^3.0.0" }, "engines": { @@ -10139,52 +10148,52 @@ } }, "node_modules/markdownlint": { - "version": "0.37.4", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.37.4.tgz", - "integrity": "sha512-u00joA/syf3VhWh6/ybVFkib5Zpj2e5KB/cfCei8fkSRuums6nyisTWGqjTWIOFoFwuXoTBQQiqlB4qFKp8ncQ==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz", + "integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==", "dev": true, "license": "MIT", "dependencies": { - "markdown-it": "14.1.0", - "micromark": "4.0.1", - "micromark-core-commonmark": "2.0.2", - "micromark-extension-directive": "3.0.2", + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", "micromark-extension-gfm-autolink-literal": "2.1.0", "micromark-extension-gfm-footnote": "2.1.0", - "micromark-extension-gfm-table": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", "micromark-extension-math": "3.1.0", - "micromark-util-types": "2.0.1" + "micromark-util-types": "2.0.2" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/DavidAnson" } }, "node_modules/markdownlint-cli": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.44.0.tgz", - "integrity": "sha512-ZJTAONlvF9NkrIBltCdW15DxN9UTbPiKMEqAh2EU2gwIFlrCMavyCEPPO121cqfYOrLUJWW8/XKWongstmmTeQ==", + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.45.0.tgz", + "integrity": "sha512-GiWr7GfJLVfcopL3t3pLumXCYs8sgWppjIA1F/Cc3zIMgD3tmkpyZ1xkm1Tej8mw53B93JsDjgA3KOftuYcfOw==", "dev": true, "license": "MIT", "dependencies": { "commander": "~13.1.0", - "glob": "~10.4.5", - "ignore": "~7.0.3", + "glob": "~11.0.2", + "ignore": "~7.0.4", "js-yaml": "~4.1.0", "jsonc-parser": "~3.3.1", "jsonpointer": "~5.0.1", - "markdownlint": "~0.37.4", - "minimatch": "~9.0.5", + "markdown-it": "~14.1.0", + "markdownlint": "~0.38.0", + "minimatch": "~10.0.1", "run-con": "~1.3.2", - "smol-toml": "~1.3.1" + "smol-toml": "~1.3.4" }, "bin": { "markdownlint": "markdownlint.js" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/markdownlint-cli/node_modules/commander": { @@ -10198,22 +10207,25 @@ } }, "node_modules/markdownlint-cli/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", + "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -10228,6 +10240,22 @@ "node": ">= 4" } }, + "node_modules/markdownlint-cli/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/markdownlint-cli/node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -10235,17 +10263,28 @@ "dev": true, "license": "MIT" }, - "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/markdownlint-cli/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "dev": true, "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdownlint-cli/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -10373,9 +10412,9 @@ } }, "node_modules/micromark": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", - "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", "dev": true, "funding": [ { @@ -10409,9 +10448,9 @@ } }, "node_modules/micromark-core-commonmark": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", - "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", "dev": true, "funding": [ { @@ -10444,9 +10483,9 @@ } }, "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", "dev": true, "license": "MIT", "dependencies": { @@ -10502,9 +10541,9 @@ } }, "node_modules/micromark-extension-gfm-table": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", - "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", "dev": true, "license": "MIT", "dependencies": { @@ -10892,9 +10931,9 @@ "license": "MIT" }, "node_modules/micromark-util-types": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", - "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", "dev": true, "funding": [ { @@ -11719,13 +11758,13 @@ } }, "node_modules/playwright": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", - "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.0.tgz", + "integrity": "sha512-ghGNnIEYZC4E+YtclRn4/p6oYbdPiASELBIYkBXfaTVKreQUYbMUYQDwS12a8F0/HtIjr/CkGjtwABeFPGcS4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.52.0" + "playwright-core": "1.53.0" }, "bin": { "playwright": "cli.js" @@ -11738,9 +11777,9 @@ } }, "node_modules/playwright-core": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", - "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.0.tgz", + "integrity": "sha512-mGLg8m0pm4+mmtB7M89Xw/GSqoNC+twivl8ITteqvAndachozYe2ZA7srU6uleV1vEdAHYqjq+SV8SNxRRFYBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -12018,9 +12057,9 @@ } }, "node_modules/postcss-nesting": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", - "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", + "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", "funding": [ { "type": "github", @@ -12033,7 +12072,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/selector-resolve-nested": "^3.0.0", + "@csstools/selector-resolve-nested": "^3.1.0", "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0" }, @@ -13518,6 +13557,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", @@ -14565,15 +14617,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.1.tgz", - "integrity": "sha512-AgRnV4sKkWOiZ0Kjbnf5ytTJXMUZQ0qhSVdQtDNYLPLnjsATEYhaO94GlRQwi4t4gO8FfjM6NnikHeKjUm8D7A==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz", + "integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.33.1", - "@typescript-eslint/parser": "8.33.1", - "@typescript-eslint/utils": "8.33.1" + "@typescript-eslint/eslint-plugin": "8.34.0", + "@typescript-eslint/parser": "8.34.0", + "@typescript-eslint/utils": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14859,9 +14911,9 @@ } }, "node_modules/vite-node": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.2.tgz", - "integrity": "sha512-Xj/jovjZvDXOq2FgLXu8NsY4uHUMWtzVmMC2LkCu9HWdr9Qu1Is5sanX3Z4jOFKdohfaWDnEJWp9pRP0vVpAcA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.3.tgz", + "integrity": "sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14979,20 +15031,20 @@ } }, "node_modules/vitest": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.2.tgz", - "integrity": "sha512-fyNn/Rp016Bt5qvY0OQvIUCwW2vnaEBLxP42PmKbNIoasSYjML+8xyeADOPvBe+Xfl/ubIw4og7Lt9jflRsCNw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.3.tgz", + "integrity": "sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.2", - "@vitest/mocker": "3.2.2", - "@vitest/pretty-format": "^3.2.2", - "@vitest/runner": "3.2.2", - "@vitest/snapshot": "3.2.2", - "@vitest/spy": "3.2.2", - "@vitest/utils": "3.2.2", + "@vitest/expect": "3.2.3", + "@vitest/mocker": "3.2.3", + "@vitest/pretty-format": "^3.2.3", + "@vitest/runner": "3.2.3", + "@vitest/snapshot": "3.2.3", + "@vitest/spy": "3.2.3", + "@vitest/utils": "3.2.3", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", @@ -15006,7 +15058,7 @@ "tinypool": "^1.1.0", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.2", + "vite-node": "3.2.3", "why-is-node-running": "^2.3.0" }, "bin": { @@ -15022,8 +15074,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.2", - "@vitest/ui": "3.2.2", + "@vitest/browser": "3.2.3", + "@vitest/ui": "3.2.3", "happy-dom": "*", "jsdom": "*" }, @@ -15242,16 +15294,6 @@ "node": ">=10.13.0" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, "node_modules/webpack": { "version": "5.99.9", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", diff --git a/package.json b/package.json index 18771090d2..1ab46d0a45 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "pdfobject": "2.3.0", "postcss": "8.5.4", "postcss-loader": "8.1.1", - "postcss-nesting": "13.0.1", + "postcss-nesting": "13.0.2", "pretty-ms": "9.0.0", "sortablejs": "1.15.6", "swagger-ui-dist": "5.17.14", @@ -61,13 +61,13 @@ "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.52.0", + "@playwright/test": "1.53.0", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin-js": "4.4.1", "@stylistic/stylelint-plugin": "3.1.2", "@vitejs/plugin-vue": "5.2.4", - "@vitest/coverage-v8": "3.2.2", - "@vitest/eslint-plugin": "1.2.1", + "@vitest/coverage-v8": "3.2.3", + "@vitest/eslint-plugin": "1.2.2", "@vue/test-utils": "2.4.6", "eslint": "9.28.0", "eslint-import-resolver-typescript": "4.4.3", @@ -76,7 +76,7 @@ "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", "eslint-plugin-playwright": "2.2.0", - "eslint-plugin-regexp": "2.8.0", + "eslint-plugin-regexp": "2.9.0", "eslint-plugin-sonarjs": "3.0.2", "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-toml": "0.12.0", @@ -85,9 +85,9 @@ "eslint-plugin-vue-scoped-css": "2.10.0", "eslint-plugin-wc": "2.2.1", "globals": "16.1.0", - "happy-dom": "17.6.3", + "happy-dom": "18.0.0", "license-checker-rseidelsohn": "4.4.2", - "markdownlint-cli": "0.44.0", + "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.2", "stylelint": "16.20.0", @@ -96,9 +96,9 @@ "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", "typescript": "5.8.3", - "typescript-eslint": "8.33.1", + "typescript-eslint": "8.34.0", "vite-string-plugin": "1.3.4", - "vitest": "3.2.2" + "vitest": "3.2.3" }, "browserslist": [ "defaults" diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index aa599bd252..e8e5d2c54b 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -225,7 +225,7 @@ func newAccessTokenResponse(ctx go_context.Context, grant *auth.OAuth2Grant, ser idToken := &oauth2.OIDCToken{ RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expirationDate.AsTime()), - Issuer: setting.AppURL, + Issuer: strings.TrimSuffix(setting.AppURL, "/"), Audience: []string{app.ClientID}, Subject: fmt.Sprint(grant.UserID), }, @@ -409,7 +409,7 @@ func IntrospectOAuth(ctx *context.Context) { if err == nil && app != nil { response.Active = true response.Scope = grant.Scope - response.Issuer = setting.AppURL + response.Issuer = strings.TrimSuffix(setting.AppURL, "/") response.Audience = []string{app.ClientID} response.Subject = fmt.Sprint(grant.UserID) } @@ -669,6 +669,7 @@ func GrantApplicationOAuth(ctx *context.Context) { // OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities func OIDCWellKnown(ctx *context.Context) { ctx.Data["SigningKey"] = oauth2.DefaultSigningKey + ctx.Data["Issuer"] = strings.TrimSuffix(setting.AppURL, "/") ctx.JSONTemplate("user/auth/oidc_wellknown") } diff --git a/routers/web/auth/oauth_test.go b/routers/web/auth/oauth_test.go index 487b551d6c..9782711dd0 100644 --- a/routers/web/auth/oauth_test.go +++ b/routers/web/auth/oauth_test.go @@ -51,6 +51,7 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { // Scopes: openid oidcToken := createAndParseToken(t, grants[0]) + assert.Equal(t, "https://try.gitea.io", oidcToken.RegisteredClaims.Issuer) assert.Empty(t, oidcToken.Name) assert.Empty(t, oidcToken.PreferredUsername) assert.Empty(t, oidcToken.Profile) @@ -67,6 +68,7 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { // Scopes: openid profile email oidcToken = createAndParseToken(t, grants[0]) + assert.Equal(t, "https://try.gitea.io", oidcToken.RegisteredClaims.Issuer) assert.Equal(t, "User Five", oidcToken.Name) assert.Equal(t, "user5", oidcToken.PreferredUsername) assert.Equal(t, "https://try.gitea.io/user5", oidcToken.Profile) diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000000..cfd555fa37 --- /dev/null +++ b/shell.nix @@ -0,0 +1,28 @@ +{ + pkgs ? import { }, +}: + +pkgs.mkShell { + name = "forgejo"; + nativeBuildInputs = with pkgs; [ + # generic + git + git-lfs + gnumake + gnused + gnutar + gzip + + # frontend + nodejs + + # backend + gofumpt + sqlite + go + gopls + + # tests + openssh + ]; +} diff --git a/templates/user/auth/oidc_wellknown.tmpl b/templates/user/auth/oidc_wellknown.tmpl index 54bb4a763d..d8edcae41d 100644 --- a/templates/user/auth/oidc_wellknown.tmpl +++ b/templates/user/auth/oidc_wellknown.tmpl @@ -1,5 +1,5 @@ { - "issuer": "{{AppUrl | JSEscape}}", + "issuer": "{{.Issuer | JSEscape}}", "authorization_endpoint": "{{AppUrl | JSEscape}}login/oauth/authorize", "token_endpoint": "{{AppUrl | JSEscape}}login/oauth/access_token", "jwks_uri": "{{AppUrl | JSEscape}}login/oauth/keys", diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index f0cbe12049..e4c3e82727 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -83,6 +83,7 @@ {{end}} + {{ctx.Locale.Tr "settings.visibility.description" "https://forgejo.org/docs/latest/user/repo-permissions/#profile-and-visibility"}}