From 6d8a0c783be14d6ffd41cce77bf53246f814943d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:43:04 +0000 Subject: [PATCH] Bump the go-modules-updates group across 1 directory with 11 updates Bumps the go-modules-updates group with 11 updates in the /backend directory: | Package | From | To | | --- | --- | --- | | [github.com/PuerkitoBio/goquery](https://github.com/PuerkitoBio/goquery) | `1.10.2` | `1.11.0` | | [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) | `2.15.0` | `2.20.0` | | [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) | `5.2.1` | `5.2.3` | | [github.com/go-chi/cors](https://github.com/go-chi/cors) | `1.2.1` | `1.2.2` | | [github.com/go-pkgz/jrpc](https://github.com/go-pkgz/jrpc) | `0.3.1` | `0.4.0` | | [github.com/go-pkgz/lgr](https://github.com/go-pkgz/lgr) | `0.12.0` | `0.12.1` | | [github.com/go-pkgz/rest](https://github.com/go-pkgz/rest) | `1.20.3` | `1.20.4` | | [github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) | `5.2.2` | `5.3.0` | | [github.com/stretchr/testify](https://github.com/stretchr/testify) | `1.10.0` | `1.11.1` | | [go.etcd.io/bbolt](https://github.com/etcd-io/bbolt) | `1.4.0` | `1.4.3` | | [golang.org/x/image](https://github.com/golang/image) | `0.26.0` | `0.33.0` | Updates `github.com/PuerkitoBio/goquery` from 1.10.2 to 1.11.0 - [Release notes](https://github.com/PuerkitoBio/goquery/releases) - [Commits](https://github.com/PuerkitoBio/goquery/compare/v1.10.2...v1.11.0) Updates `github.com/alecthomas/chroma/v2` from 2.15.0 to 2.20.0 - [Release notes](https://github.com/alecthomas/chroma/releases) - [Commits](https://github.com/alecthomas/chroma/compare/v2.15.0...v2.20.0) Updates `github.com/go-chi/chi/v5` from 5.2.1 to 5.2.3 - [Release notes](https://github.com/go-chi/chi/releases) - [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md) - [Commits](https://github.com/go-chi/chi/compare/v5.2.1...v5.2.3) Updates `github.com/go-chi/cors` from 1.2.1 to 1.2.2 - [Release notes](https://github.com/go-chi/cors/releases) - [Commits](https://github.com/go-chi/cors/compare/v1.2.1...v1.2.2) Updates `github.com/go-pkgz/jrpc` from 0.3.1 to 0.4.0 - [Release notes](https://github.com/go-pkgz/jrpc/releases) - [Commits](https://github.com/go-pkgz/jrpc/compare/v0.3.1...v0.4.0) Updates `github.com/go-pkgz/lgr` from 0.12.0 to 0.12.1 - [Release notes](https://github.com/go-pkgz/lgr/releases) - [Commits](https://github.com/go-pkgz/lgr/compare/v0.12.0...v0.12.1) Updates `github.com/go-pkgz/rest` from 1.20.3 to 1.20.4 - [Release notes](https://github.com/go-pkgz/rest/releases) - [Commits](https://github.com/go-pkgz/rest/compare/v1.20.3...v1.20.4) Updates `github.com/golang-jwt/jwt/v5` from 5.2.2 to 5.3.0 - [Release notes](https://github.com/golang-jwt/jwt/releases) - [Commits](https://github.com/golang-jwt/jwt/compare/v5.2.2...v5.3.0) Updates `github.com/stretchr/testify` from 1.10.0 to 1.11.1 - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.10.0...v1.11.1) Updates `go.etcd.io/bbolt` from 1.4.0 to 1.4.3 - [Release notes](https://github.com/etcd-io/bbolt/releases) - [Commits](https://github.com/etcd-io/bbolt/compare/v1.4.0...v1.4.3) Updates `golang.org/x/image` from 0.26.0 to 0.33.0 - [Commits](https://github.com/golang/image/compare/v0.26.0...v0.33.0) --- updated-dependencies: - dependency-name: github.com/PuerkitoBio/goquery dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-modules-updates - dependency-name: github.com/alecthomas/chroma/v2 dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-modules-updates - dependency-name: github.com/go-chi/chi/v5 dependency-version: 5.2.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-modules-updates - dependency-name: github.com/go-chi/cors dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-modules-updates - dependency-name: github.com/go-pkgz/jrpc dependency-version: 0.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-modules-updates - dependency-name: github.com/go-pkgz/lgr dependency-version: 0.12.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-modules-updates - dependency-name: github.com/go-pkgz/rest dependency-version: 1.20.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-modules-updates - dependency-name: github.com/golang-jwt/jwt/v5 dependency-version: 5.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-modules-updates - dependency-name: github.com/stretchr/testify dependency-version: 1.11.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-modules-updates - dependency-name: go.etcd.io/bbolt dependency-version: 1.4.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-modules-updates - dependency-name: golang.org/x/image dependency-version: 0.33.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-modules-updates ... Signed-off-by: dependabot[bot] --- backend/_example/memory_store/go.mod | 24 +- backend/_example/memory_store/go.sum | 58 +- backend/app/rest/api/rest_public_test.go | 4 +- backend/app/store/formatter_test.go | 4 +- backend/go.mod | 28 +- backend/go.sum | 58 +- .../github.com/PuerkitoBio/goquery/README.md | 3 + .../PuerkitoBio/goquery/property.go | 20 +- .../PuerkitoBio/goquery/utilities.go | 21 +- .../vendor/github.com/ajg/form/.travis.yml | 25 - backend/vendor/github.com/ajg/form/LICENSE | 27 - backend/vendor/github.com/ajg/form/README.md | 247 ------- backend/vendor/github.com/ajg/form/TODO.md | 4 - backend/vendor/github.com/ajg/form/decode.go | 370 ---------- backend/vendor/github.com/ajg/form/encode.go | 388 ---------- backend/vendor/github.com/ajg/form/form.go | 14 - backend/vendor/github.com/ajg/form/node.go | 152 ---- .../vendor/github.com/ajg/form/pre-commit.sh | 18 - .../alecthomas/chroma/v2/.gitignore | 3 + .../alecthomas/chroma/v2/.golangci.yml | 16 +- .../alecthomas/chroma/v2/.goreleaser.yml | 39 +- .../alecthomas/chroma/v2/Dockerfile | 65 ++ .../github.com/alecthomas/chroma/v2/Makefile | 29 +- .../github.com/alecthomas/chroma/v2/README.md | 72 +- .../alecthomas/chroma/v2/biome.json | 6 + .../alecthomas/chroma/v2/chroma.jpg | Bin 0 -> 80950 bytes .../alecthomas/chroma/v2/delegate.go | 9 + .../alecthomas/chroma/v2/emitters.go | 15 + .../chroma/v2/formatters/html/html.go | 42 +- .../alecthomas/chroma/v2/iterator.go | 11 + .../github.com/alecthomas/chroma/v2/lexer.go | 17 + .../chroma/v2/lexers/embedded/angular2.xml | 95 +-- .../chroma/v2/lexers/embedded/applescript.xml | 135 ++-- .../chroma/v2/lexers/embedded/arduino.xml | 261 +++---- .../chroma/v2/lexers/embedded/armasm.xml | 94 +-- .../chroma/v2/lexers/embedded/core.xml | 79 ++ .../chroma/v2/lexers/embedded/css.xml | 4 +- .../chroma/v2/lexers/embedded/docker.xml | 45 +- .../chroma/v2/lexers/embedded/gleam.xml | 27 +- .../chroma/v2/lexers/embedded/hlb.xml | 106 ++- .../chroma/v2/lexers/embedded/janet.xml | 48 ++ .../chroma/v2/lexers/embedded/kotlin.xml | 1 + .../chroma/v2/lexers/embedded/lean.xml | 56 ++ .../chroma/v2/lexers/embedded/lox.xml | 83 +++ .../chroma/v2/lexers/embedded/lua.xml | 2 + .../chroma/v2/lexers/embedded/mojo.xml | 228 ++++++ .../chroma/v2/lexers/embedded/moonscript.xml | 83 +++ .../embedded/nginx_configuration_file.xml | 3 + .../chroma/v2/lexers/embedded/nu.xml | 121 ++++ .../v2/lexers/embedded/objectpascal.xml | 9 +- .../chroma/v2/lexers/embedded/ocaml.xml | 8 + .../chroma/v2/lexers/embedded/odin.xml | 14 + .../chroma/v2/lexers/embedded/rpgle.xml | 176 +++++ .../chroma/v2/lexers/embedded/sed.xml | 90 ++- .../chroma/v2/lexers/embedded/solidity.xml | 20 +- .../chroma/v2/lexers/embedded/terraform.xml | 203 +++--- .../chroma/v2/lexers/embedded/toml.xml | 1 + .../chroma/v2/lexers/embedded/typescript.xml | 229 +++--- .../chroma/v2/lexers/embedded/vue.xml | 38 +- .../chroma/v2/lexers/embedded/zig.xml | 4 + .../alecthomas/chroma/v2/lexers/gemtext.go | 37 + .../alecthomas/chroma/v2/lexers/go.go | 8 +- .../alecthomas/chroma/v2/lexers/lexers.go | 6 + .../alecthomas/chroma/v2/lexers/markdown.go | 6 +- .../alecthomas/chroma/v2/lexers/raku.go | 25 +- .../alecthomas/chroma/v2/pygments-lexers.txt | 681 +++++++++++++----- .../github.com/alecthomas/chroma/v2/regexp.go | 51 +- .../alecthomas/chroma/v2/registry.go | 18 + .../alecthomas/chroma/v2/renovate.json5 | 20 +- .../chroma/v2/styles/catppuccin-frappe.xml | 6 +- .../chroma/v2/styles/catppuccin-latte.xml | 6 +- .../chroma/v2/styles/catppuccin-macchiato.xml | 6 +- .../chroma/v2/styles/catppuccin-mocha.xml | 6 +- .../alecthomas/chroma/v2/styles/rpgle.xml | 30 + .../alecthomas/chroma/v2/styles/rrt.xml | 6 + .../alecthomas/chroma/v2/tokentype_enumer.go | 234 +++--- .../github.com/alecthomas/chroma/v2/types.go | 22 +- .../github.com/didip/tollbooth/v8/.gitignore | 3 - .../didip/tollbooth/v8/.golangci.yml | 33 - .../github.com/didip/tollbooth/v8/README.md | 217 ------ .../didip/tollbooth/v8/errors/errors.go | 15 - .../didip/tollbooth/v8/internal/time/AUTHORS | 3 - .../tollbooth/v8/internal/time/CONTRIBUTORS | 3 - .../didip/tollbooth/v8/internal/time/LICENSE | 27 - .../didip/tollbooth/v8/internal/time/PATENTS | 22 - .../tollbooth/v8/internal/time/rate/rate.go | 396 ---------- .../didip/tollbooth/v8/libstring/libstring.go | 100 --- .../didip/tollbooth/v8/limiter/limiter.go | 624 ---------------- .../tollbooth/v8/limiter/limiter_options.go | 14 - .../didip/tollbooth/v8/tollbooth.go | 375 ---------- .../github.com/dlclark/regexp2/fastclock.go | 12 + .../vendor/github.com/go-chi/chi/v5/README.md | 6 +- .../vendor/github.com/go-chi/chi/v5/chi.go | 3 +- .../github.com/go-chi/chi/v5/context.go | 9 +- .../go-chi/chi/v5/middleware/compress.go | 24 +- .../chi/v5/middleware/content_charset.go | 9 +- .../go-chi/chi/v5/middleware/content_type.go | 4 +- .../go-chi/chi/v5/middleware/profiler.go | 3 + .../go-chi/chi/v5/middleware/realip.go | 6 +- .../go-chi/chi/v5/middleware/route_headers.go | 8 +- .../go-chi/chi/v5/middleware/strip.go | 10 +- .../go-chi/chi/v5/middleware/throttle.go | 17 +- .../go-chi/chi/v5/middleware/url_format.go | 4 +- .../go-chi/chi/v5/middleware/wrap_writer.go | 7 +- .../vendor/github.com/go-chi/chi/v5/mux.go | 17 +- .../github.com/go-chi/chi/v5/path_value.go | 5 +- .../go-chi/chi/v5/path_value_fallback.go | 4 +- .../github.com/go-chi/chi/v5/pattern.go | 16 + .../go-chi/chi/v5/pattern_fallback.go | 17 + .../vendor/github.com/go-chi/chi/v5/tree.go | 21 +- .../vendor/github.com/go-chi/cors/README.md | 6 +- backend/vendor/github.com/go-chi/cors/cors.go | 28 +- .../github.com/go-chi/render/.gitignore | 1 - .../vendor/github.com/go-chi/render/LICENSE | 20 - .../vendor/github.com/go-chi/render/README.md | 27 - .../github.com/go-chi/render/content_type.go | 84 --- .../github.com/go-chi/render/decoder.go | 57 -- .../vendor/github.com/go-chi/render/render.go | 143 ---- .../github.com/go-chi/render/responder.go | 234 ------ .../github.com/go-pkgz/jrpc/.golangci.yml | 198 +++-- .../vendor/github.com/go-pkgz/jrpc/server.go | 85 ++- .../github.com/go-pkgz/lgr/.golangci.yml | 203 +++--- .../vendor/github.com/go-pkgz/lgr/logger.go | 2 +- backend/vendor/github.com/go-pkgz/lgr/slog.go | 10 +- .../github.com/go-pkgz/rest/logger/logger.go | 2 +- .../github.com/go-pkgz/routegroup/.gitignore | 11 + .../go-pkgz/routegroup/.golangci.yml | 62 ++ .../v8 => go-pkgz/routegroup}/LICENSE | 12 +- .../github.com/go-pkgz/routegroup/README.md | 370 ++++++++++ .../github.com/go-pkgz/routegroup/group.go | 355 +++++++++ .../golang-jwt/jwt/v5/MIGRATION_GUIDE.md | 2 +- .../github.com/golang-jwt/jwt/v5/ecdsa.go | 4 +- .../golang-jwt/jwt/v5/ecdsa_utils.go | 4 +- .../github.com/golang-jwt/jwt/v5/ed25519.go | 4 +- .../golang-jwt/jwt/v5/ed25519_utils.go | 4 +- .../github.com/golang-jwt/jwt/v5/errors.go | 40 + .../golang-jwt/jwt/v5/errors_go1_20.go | 47 -- .../golang-jwt/jwt/v5/errors_go_other.go | 78 -- .../github.com/golang-jwt/jwt/v5/hmac.go | 4 +- .../golang-jwt/jwt/v5/map_claims.go | 8 +- .../github.com/golang-jwt/jwt/v5/none.go | 4 +- .../golang-jwt/jwt/v5/parser_option.go | 25 +- .../github.com/golang-jwt/jwt/v5/rsa.go | 4 +- .../github.com/golang-jwt/jwt/v5/rsa_pss.go | 7 +- .../github.com/golang-jwt/jwt/v5/rsa_utils.go | 6 +- .../golang-jwt/jwt/v5/signing_method.go | 6 +- .../github.com/golang-jwt/jwt/v5/token.go | 20 +- .../github.com/golang-jwt/jwt/v5/types.go | 4 +- .../github.com/golang-jwt/jwt/v5/validator.go | 50 +- .../testify/assert/assertion_compare.go | 22 +- .../testify/assert/assertion_format.go | 51 +- .../testify/assert/assertion_forward.go | 102 ++- .../testify/assert/assertion_order.go | 2 +- .../stretchr/testify/assert/assertions.go | 379 ++++++---- .../github.com/stretchr/testify/assert/doc.go | 4 + .../testify/assert/http_assertions.go | 4 +- .../testify/assert/yaml/yaml_custom.go | 1 - .../testify/assert/yaml/yaml_default.go | 1 - .../stretchr/testify/assert/yaml/yaml_fail.go | 1 - .../stretchr/testify/require/doc.go | 2 + .../stretchr/testify/require/require.go | 108 ++- .../testify/require/require_forward.go | 102 ++- backend/vendor/go.etcd.io/bbolt/.go-version | 2 +- backend/vendor/go.etcd.io/bbolt/bolt_386.go | 7 - backend/vendor/go.etcd.io/bbolt/bolt_aix.go | 4 +- backend/vendor/go.etcd.io/bbolt/bolt_amd64.go | 7 - .../vendor/go.etcd.io/bbolt/bolt_android.go | 4 +- backend/vendor/go.etcd.io/bbolt/bolt_arm.go | 7 - backend/vendor/go.etcd.io/bbolt/bolt_arm64.go | 9 - .../vendor/go.etcd.io/bbolt/bolt_loong64.go | 9 - .../vendor/go.etcd.io/bbolt/bolt_mips64x.go | 9 - backend/vendor/go.etcd.io/bbolt/bolt_mipsx.go | 9 - backend/vendor/go.etcd.io/bbolt/bolt_ppc.go | 9 - backend/vendor/go.etcd.io/bbolt/bolt_ppc64.go | 9 - .../vendor/go.etcd.io/bbolt/bolt_ppc64le.go | 9 - .../vendor/go.etcd.io/bbolt/bolt_riscv64.go | 9 - backend/vendor/go.etcd.io/bbolt/bolt_s390x.go | 9 - .../vendor/go.etcd.io/bbolt/bolt_solaris.go | 4 +- backend/vendor/go.etcd.io/bbolt/bolt_unix.go | 3 +- .../vendor/go.etcd.io/bbolt/bolt_windows.go | 3 +- backend/vendor/go.etcd.io/bbolt/db.go | 16 +- .../bbolt/internal/common/bolt_386.go | 7 + .../bbolt/internal/common/bolt_amd64.go | 7 + .../bbolt/internal/common/bolt_arm.go | 7 + .../bbolt/internal/common/bolt_arm64.go | 9 + .../bbolt/internal/common/bolt_loong64.go | 9 + .../bbolt/internal/common/bolt_mips64x.go | 9 + .../bbolt/internal/common/bolt_mipsx.go | 9 + .../bbolt/internal/common/bolt_ppc.go | 9 + .../bbolt/internal/common/bolt_ppc64.go | 9 + .../bbolt/internal/common/bolt_ppc64le.go | 9 + .../bbolt/internal/common/bolt_riscv64.go | 9 + .../bbolt/internal/common/bolt_s390x.go | 9 + .../go.etcd.io/bbolt/internal/common/types.go | 3 - .../bbolt/internal/common/unsafe.go | 2 +- backend/vendor/go.etcd.io/bbolt/tx.go | 75 +- backend/vendor/modules.txt | 52 +- 197 files changed, 4874 insertions(+), 5762 deletions(-) delete mode 100644 backend/vendor/github.com/ajg/form/.travis.yml delete mode 100644 backend/vendor/github.com/ajg/form/LICENSE delete mode 100644 backend/vendor/github.com/ajg/form/README.md delete mode 100644 backend/vendor/github.com/ajg/form/TODO.md delete mode 100644 backend/vendor/github.com/ajg/form/decode.go delete mode 100644 backend/vendor/github.com/ajg/form/encode.go delete mode 100644 backend/vendor/github.com/ajg/form/form.go delete mode 100644 backend/vendor/github.com/ajg/form/node.go delete mode 100644 backend/vendor/github.com/ajg/form/pre-commit.sh create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/Dockerfile create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/biome.json create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/chroma.jpg create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/core.xml create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/janet.xml create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lean.xml create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lox.xml create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/mojo.xml create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/moonscript.xml create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nu.xml create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/rpgle.xml create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/lexers/gemtext.go create mode 100644 backend/vendor/github.com/alecthomas/chroma/v2/styles/rpgle.xml delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/.gitignore delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/.golangci.yml delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/README.md delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/errors/errors.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/internal/time/AUTHORS delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/internal/time/CONTRIBUTORS delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/internal/time/LICENSE delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/internal/time/PATENTS delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/internal/time/rate/rate.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/libstring/libstring.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/limiter/limiter.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/limiter/limiter_options.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v8/tollbooth.go create mode 100644 backend/vendor/github.com/go-chi/chi/v5/pattern.go create mode 100644 backend/vendor/github.com/go-chi/chi/v5/pattern_fallback.go delete mode 100644 backend/vendor/github.com/go-chi/render/.gitignore delete mode 100644 backend/vendor/github.com/go-chi/render/LICENSE delete mode 100644 backend/vendor/github.com/go-chi/render/README.md delete mode 100644 backend/vendor/github.com/go-chi/render/content_type.go delete mode 100644 backend/vendor/github.com/go-chi/render/decoder.go delete mode 100644 backend/vendor/github.com/go-chi/render/render.go delete mode 100644 backend/vendor/github.com/go-chi/render/responder.go create mode 100644 backend/vendor/github.com/go-pkgz/routegroup/.gitignore create mode 100644 backend/vendor/github.com/go-pkgz/routegroup/.golangci.yml rename backend/vendor/github.com/{didip/tollbooth/v8 => go-pkgz/routegroup}/LICENSE (86%) create mode 100644 backend/vendor/github.com/go-pkgz/routegroup/README.md create mode 100644 backend/vendor/github.com/go-pkgz/routegroup/group.go delete mode 100644 backend/vendor/github.com/golang-jwt/jwt/v5/errors_go1_20.go delete mode 100644 backend/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_386.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_amd64.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_arm.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_arm64.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_loong64.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_mips64x.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_mipsx.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_ppc.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_ppc64.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_ppc64le.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_riscv64.go delete mode 100644 backend/vendor/go.etcd.io/bbolt/bolt_s390x.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_386.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_amd64.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_arm.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_arm64.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_loong64.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_mips64x.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_mipsx.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc64.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc64le.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_riscv64.go create mode 100644 backend/vendor/go.etcd.io/bbolt/internal/common/bolt_s390x.go diff --git a/backend/_example/memory_store/go.mod b/backend/_example/memory_store/go.mod index 5ecc2c72..a5d0c717 100644 --- a/backend/_example/memory_store/go.mod +++ b/backend/_example/memory_store/go.mod @@ -3,27 +3,23 @@ module github.com/umputun/remark42/memory_store go 1.25 require ( - github.com/go-pkgz/jrpc v0.3.1 - github.com/go-pkgz/lgr v0.12.0 + github.com/go-pkgz/jrpc v0.4.0 + github.com/go-pkgz/lgr v0.12.1 github.com/jessevdk/go-flags v1.6.1 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 github.com/umputun/remark42/backend v1.1000.0 ) require ( github.com/Depado/bfchroma/v2 v2.0.0 // indirect - github.com/PuerkitoBio/goquery v1.10.2 // indirect - github.com/ajg/form v1.5.1 // indirect - github.com/alecthomas/chroma/v2 v2.15.0 // indirect + github.com/PuerkitoBio/goquery v1.11.0 // indirect + github.com/alecthomas/chroma/v2 v2.20.0 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/didip/tollbooth/v8 v8.0.1 // indirect - github.com/dlclark/regexp2 v1.11.4 // indirect - github.com/go-chi/chi/v5 v5.2.1 // indirect - github.com/go-chi/render v1.0.3 // indirect - github.com/go-pkgz/expirable-cache/v3 v3.0.0 // indirect - github.com/go-pkgz/rest v1.20.3 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/go-pkgz/rest v1.20.4 // indirect + github.com/go-pkgz/routegroup v1.6.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -33,9 +29,9 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - go.etcd.io/bbolt v1.4.0 // indirect + go.etcd.io/bbolt v1.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect - golang.org/x/image v0.26.0 // indirect + golang.org/x/image v0.33.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sys v0.38.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/backend/_example/memory_store/go.sum b/backend/_example/memory_store/go.sum index 7122359e..3963d35a 100644 --- a/backend/_example/memory_store/go.sum +++ b/backend/_example/memory_store/go.sum @@ -1,15 +1,13 @@ github.com/Depado/bfchroma/v2 v2.0.0 h1:IRpN9BPkNwEpR6w1ectIcNWOuhDSLx+8f1pn83fzxx8= github.com/Depado/bfchroma/v2 v2.0.0/go.mod h1:wFwW/Pw8Tnd0irzgO9Zxtxgzp3aPS8qBWlyadxujxmw= -github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8= -github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU= -github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= +github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= -github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= -github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= -github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= +github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= +github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= @@ -17,22 +15,16 @@ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd3 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/didip/tollbooth/v8 v8.0.1 h1:VAAapTo1t4Bn6bbpcHjuovwoa9u3JH++wgjbpWv+rB8= -github.com/didip/tollbooth/v8 v8.0.1/go.mod h1:oEd9l+ep373d7DmvKLc0a5gasPOev2mTewi6KPQBGJ4= -github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= -github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= -github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-pkgz/expirable-cache/v3 v3.0.0 h1:u3/gcu3sabLYiTCevoRKv+WzjIn5oo7P8XtiXBeRDLw= -github.com/go-pkgz/expirable-cache/v3 v3.0.0/go.mod h1:2OQiDyEGQalYecLWmXprm3maPXeVb5/6/X7yRPYTzec= -github.com/go-pkgz/jrpc v0.3.1 h1:jRxnO5ppOookkL9PuOWw/IloevmzqbsKaGImTR3iALQ= -github.com/go-pkgz/jrpc v0.3.1/go.mod h1:DJg1RfhwvJpVT3YXtk8LxowB7DN5jUoZ3U4MbHnm76M= -github.com/go-pkgz/lgr v0.12.0 h1:uoSCLdiMocZDa+L66DavHG5UIkOJvWKOVqt6sNQllw0= -github.com/go-pkgz/lgr v0.12.0/go.mod h1:A4AxjOthFVFK6jRnVYMeusno5SeDAxcLVHd0kI/lN/Y= -github.com/go-pkgz/rest v1.20.3 h1:oGGfM8XTnvwek29q1OAhcI1nkKKOpurRFApBiYH44Fk= -github.com/go-pkgz/rest v1.20.3/go.mod h1:NC2xNN/y1rIs0PY13FowKoH8rk9RhJNJ0tTbkBg8Yks= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/go-pkgz/jrpc v0.4.0 h1:oD7xiGrzDkndkuCjeHGugQXxbggLSV7O1QmHhoc5pYY= +github.com/go-pkgz/jrpc v0.4.0/go.mod h1:JFoY3bRjRyx4M3CbEVDFQStMB1m2gmQ7OjqFK7q3kOo= +github.com/go-pkgz/lgr v0.12.1 h1:8GVfG2rSARq3Eaj5PP158rtBR2LHVGkwioIkQBGbvKg= +github.com/go-pkgz/lgr v0.12.1/go.mod h1:A4AxjOthFVFK6jRnVYMeusno5SeDAxcLVHd0kI/lN/Y= +github.com/go-pkgz/rest v1.20.4 h1:8ufcP1IqoDhCvIFdXPtvyX4HSS16SM6THBe2a6L0/kg= +github.com/go-pkgz/rest v1.20.4/go.mod h1:2/LEZGndSxpVvExsMn48AjUgiTn6kILqjpoaRnl62JU= +github.com/go-pkgz/routegroup v1.6.0 h1:44XHZgF6JIIldRlv+zjg6SygULASmjifnfIQjwCT0e4= +github.com/go-pkgz/routegroup v1.6.0/go.mod h1:Pmu04fhgWhRtBMIJ8HXppnnzOPjnL/IEPBIdO2zmeqg= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= @@ -41,8 +33,6 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= @@ -61,11 +51,11 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= +go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= @@ -74,8 +64,8 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= -golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= +golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= +golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -99,8 +89,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/backend/app/rest/api/rest_public_test.go b/backend/app/rest/api/rest_public_test.go index 1e05fa90..a70ccf61 100644 --- a/backend/app/rest/api/rest_public_test.go +++ b/backend/app/rest/api/rest_public_test.go @@ -178,8 +178,8 @@ BKT assert.Equal(t, http.StatusOK, resp.StatusCode) b, err := io.ReadAll(resp.Body) assert.NoError(t, err) - assert.Equal(t, `
func main(aa string) int {return 0}
-
`, string(b)) + assert.Equal(t, `
func main(aa string) int {return 0}
+
`, string(b)) assert.NoError(t, resp.Body.Close()) } diff --git a/backend/app/store/formatter_test.go b/backend/app/store/formatter_test.go index 3e11f043..440d07a7 100644 --- a/backend/app/store/formatter_test.go +++ b/backend/app/store/formatter_test.go @@ -43,8 +43,8 @@ func TestFormatter_FormatText(t *testing.T) { {`no_smartpants «quoted» text`, "

no_smartpants «quoted» text

\n!converted", "French guillemets without smartpants"}, {`«quoted» text`, "

«quoted» text

\n!converted", "French guillemets with smartpants"}, {"smth\n```go\nfunc main(aa string) int {return 0}\n```", `

smth

-
func main(aa string) int {return 0}
-
!converted`, "code with language"}, +
func main(aa string) int {return 0}
+
!converted`, "code with language"}, {"```\ntest_code\n```", `
test_code
 
!converted`, "code without language"}, } diff --git a/backend/go.mod b/backend/go.mod index 9146a95c..c0251031 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -4,21 +4,21 @@ go 1.25 require ( github.com/Depado/bfchroma/v2 v2.0.0 - github.com/PuerkitoBio/goquery v1.10.2 - github.com/alecthomas/chroma/v2 v2.15.0 + github.com/PuerkitoBio/goquery v1.11.0 + github.com/alecthomas/chroma/v2 v2.20.0 github.com/didip/tollbooth/v7 v7.0.2 github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f - github.com/go-chi/chi/v5 v5.2.1 - github.com/go-chi/cors v1.2.1 + github.com/go-chi/chi/v5 v5.2.3 + github.com/go-chi/cors v1.2.2 github.com/go-pkgz/auth/v2 v2.0.1-0.20250415030422-4f9f2c5e3b0d - github.com/go-pkgz/jrpc v0.3.1 + github.com/go-pkgz/jrpc v0.4.0 github.com/go-pkgz/lcw/v2 v2.0.0 - github.com/go-pkgz/lgr v0.12.0 + github.com/go-pkgz/lgr v0.12.1 github.com/go-pkgz/notify v1.2.0 github.com/go-pkgz/repeater v1.2.0 - github.com/go-pkgz/rest v1.20.3 + github.com/go-pkgz/rest v1.20.4 github.com/go-pkgz/syncs v1.3.2 - github.com/golang-jwt/jwt/v5 v5.2.2 + github.com/golang-jwt/jwt/v5 v5.3.0 github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 github.com/hashicorp/go-multierror v1.1.1 @@ -28,29 +28,27 @@ require ( github.com/rs/xid v1.6.0 github.com/russross/blackfriday/v2 v2.1.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - github.com/stretchr/testify v1.10.0 - go.etcd.io/bbolt v1.4.0 + github.com/stretchr/testify v1.11.1 + go.etcd.io/bbolt v1.4.3 go.uber.org/goleak v1.3.0 golang.org/x/crypto v0.45.0 - golang.org/x/image v0.26.0 + golang.org/x/image v0.33.0 golang.org/x/net v0.47.0 ) require ( cloud.google.com/go/compute/metadata v0.6.0 // indirect - github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dghubble/oauth1 v0.7.3 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/didip/tollbooth/v8 v8.0.1 // indirect - github.com/dlclark/regexp2 v1.11.4 // indirect - github.com/go-chi/render v1.0.3 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect github.com/go-oauth2/oauth2/v4 v4.5.3 // indirect github.com/go-pkgz/email v0.5.0 // indirect github.com/go-pkgz/expirable-cache/v3 v3.0.0 // indirect + github.com/go-pkgz/routegroup v1.6.0 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/websocket v1.5.3 // indirect diff --git a/backend/go.sum b/backend/go.sum index 6acf9efb..9721cd36 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -3,16 +3,16 @@ cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4 cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= github.com/Depado/bfchroma/v2 v2.0.0 h1:IRpN9BPkNwEpR6w1ectIcNWOuhDSLx+8f1pn83fzxx8= github.com/Depado/bfchroma/v2 v2.0.0/go.mod h1:wFwW/Pw8Tnd0irzgO9Zxtxgzp3aPS8qBWlyadxujxmw= -github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8= -github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU= +github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= +github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= -github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= -github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= -github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= +github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= +github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis/v2 v2.31.1 h1:7XAt0uUg3DtwEKW5ZAGa+K7FZV2DdKQo5K/6TTnfX8Y= @@ -40,12 +40,10 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu github.com/didip/tollbooth/v7 v7.0.0/go.mod h1:VZhDSGl5bDSPj4wPsih3PFa4Uh9Ghv8hgacaTm5PRT4= github.com/didip/tollbooth/v7 v7.0.2 h1:WYEfusYI6g64cN0qbZgekDrYfuYBZjUZd5+RlWi69p4= github.com/didip/tollbooth/v7 v7.0.2/go.mod h1:RtRYfEmFGX70+ike5kSndSvLtQ3+F2EAmTI4Un/VXNc= -github.com/didip/tollbooth/v8 v8.0.1 h1:VAAapTo1t4Bn6bbpcHjuovwoa9u3JH++wgjbpWv+rB8= -github.com/didip/tollbooth/v8 v8.0.1/go.mod h1:oEd9l+ep373d7DmvKLc0a5gasPOev2mTewi6KPQBGJ4= github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f h1:jtKwihcLmUC9BAhoJ9adCUqdSSZcOdH2KL7mPTUm2aw= github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f/go.mod h1:q9C80dnsuVRP2dAskjnXRNWdUJqtGgwG9wNrzt0019s= -github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= -github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= @@ -53,12 +51,10 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= -github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= -github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= +github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= +github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= +github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-oauth2/oauth2/v4 v4.5.3 h1:lQt7O9KOnu/v4awe166FH7+p8tFUXQyR+no6nctAKU0= github.com/go-oauth2/oauth2/v4 v4.5.3/go.mod h1:ryzb7zr8fdQBlciD0+tcnEWeOok5B0J8V/DniwYqQ2k= github.com/go-pkgz/auth/v2 v2.0.1-0.20250415030422-4f9f2c5e3b0d h1:xyTp7JRaHy3/ttpcNiJO/YzxENZcvxcZL8iE3QfssnY= @@ -68,26 +64,28 @@ github.com/go-pkgz/email v0.5.0/go.mod h1:BdxglsQnymzhfdbnncEE72a6DrucZHy6I+42LK github.com/go-pkgz/expirable-cache v0.1.0/go.mod h1:GTrEl0X+q0mPNqN6dtcQXksACnzCBQ5k/k1SwXJsZKs= github.com/go-pkgz/expirable-cache/v3 v3.0.0 h1:u3/gcu3sabLYiTCevoRKv+WzjIn5oo7P8XtiXBeRDLw= github.com/go-pkgz/expirable-cache/v3 v3.0.0/go.mod h1:2OQiDyEGQalYecLWmXprm3maPXeVb5/6/X7yRPYTzec= -github.com/go-pkgz/jrpc v0.3.1 h1:jRxnO5ppOookkL9PuOWw/IloevmzqbsKaGImTR3iALQ= -github.com/go-pkgz/jrpc v0.3.1/go.mod h1:DJg1RfhwvJpVT3YXtk8LxowB7DN5jUoZ3U4MbHnm76M= +github.com/go-pkgz/jrpc v0.4.0 h1:oD7xiGrzDkndkuCjeHGugQXxbggLSV7O1QmHhoc5pYY= +github.com/go-pkgz/jrpc v0.4.0/go.mod h1:JFoY3bRjRyx4M3CbEVDFQStMB1m2gmQ7OjqFK7q3kOo= github.com/go-pkgz/lcw/v2 v2.0.0 h1:gTwXpiJBhQeA1rXuqkRuLcV79uATFna8CckH8ZBBrH0= github.com/go-pkgz/lcw/v2 v2.0.0/go.mod h1:yxJHOn+IbQBQHxUqkCtMrbGjIfdYcsBAZcVCBaL1Va8= -github.com/go-pkgz/lgr v0.12.0 h1:uoSCLdiMocZDa+L66DavHG5UIkOJvWKOVqt6sNQllw0= -github.com/go-pkgz/lgr v0.12.0/go.mod h1:A4AxjOthFVFK6jRnVYMeusno5SeDAxcLVHd0kI/lN/Y= +github.com/go-pkgz/lgr v0.12.1 h1:8GVfG2rSARq3Eaj5PP158rtBR2LHVGkwioIkQBGbvKg= +github.com/go-pkgz/lgr v0.12.1/go.mod h1:A4AxjOthFVFK6jRnVYMeusno5SeDAxcLVHd0kI/lN/Y= github.com/go-pkgz/notify v1.2.0 h1:jqbsbWkodCDB9ffyI9fCG1bTSgidWJRS5UWD/twjU44= github.com/go-pkgz/notify v1.2.0/go.mod h1:BTHj9ly7xZpaxg91lBdxCxSmkpVQ9R6q5QviX/upeYo= github.com/go-pkgz/repeater v1.2.0 h1:oJFvjyKdTDd5RCzpzxlzYIZFFj6Zfl17rE1aUfu6UjQ= github.com/go-pkgz/repeater v1.2.0/go.mod h1:vypP6xamA53MFmafnGUucqOmALKk36xgKu2hSG73LHM= -github.com/go-pkgz/rest v1.20.3 h1:oGGfM8XTnvwek29q1OAhcI1nkKKOpurRFApBiYH44Fk= -github.com/go-pkgz/rest v1.20.3/go.mod h1:NC2xNN/y1rIs0PY13FowKoH8rk9RhJNJ0tTbkBg8Yks= +github.com/go-pkgz/rest v1.20.4 h1:8ufcP1IqoDhCvIFdXPtvyX4HSS16SM6THBe2a6L0/kg= +github.com/go-pkgz/rest v1.20.4/go.mod h1:2/LEZGndSxpVvExsMn48AjUgiTn6kILqjpoaRnl62JU= +github.com/go-pkgz/routegroup v1.6.0 h1:44XHZgF6JIIldRlv+zjg6SygULASmjifnfIQjwCT0e4= +github.com/go-pkgz/routegroup v1.6.0/go.mod h1:Pmu04fhgWhRtBMIJ8HXppnnzOPjnL/IEPBIdO2zmeqg= github.com/go-pkgz/syncs v1.3.2 h1:gmioASlJNy3gNosPlgvWOM2QP0Hdjzn2u+/sUShgd8E= github.com/go-pkgz/syncs v1.3.2/go.mod h1:qjgzpp7OpuhDf7BWsW/FHCu9DLjE32NPy6/vXAXT/Cw= github.com/go-session/session/v3 v3.2.1/go.mod h1:RftEBbyuzqkNCAxIrCLJe+rfBqB/4G11qxq9KYKrx4M= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -192,8 +190,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= @@ -249,8 +247,8 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= +go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -264,8 +262,8 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= -golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= +golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= +golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= diff --git a/backend/vendor/github.com/PuerkitoBio/goquery/README.md b/backend/vendor/github.com/PuerkitoBio/goquery/README.md index 9211fc3b..32147261 100644 --- a/backend/vendor/github.com/PuerkitoBio/goquery/README.md +++ b/backend/vendor/github.com/PuerkitoBio/goquery/README.md @@ -24,6 +24,7 @@ Syntax-wise, it is as close as possible to jQuery, with the same function names Required Go version: +* Starting with version `v1.11.0` of goquery, Go 1.24+ is required due to its dependencies. * Starting with version `v1.10.0` of goquery, Go 1.23+ is required due to the use of function-based iterators. * For `v1.9.0` of goquery, Go 1.18+ is required due to the use of generics. * For previous goquery versions, a Go version of 1.1+ was required because of the `net/html` dependency. @@ -46,6 +47,8 @@ Ongoing goquery development is tested on the latest 2 versions of Go. **Note that goquery's API is now stable, and will not break.** +* **2025-11-16 (v1.11.0)** : Update `go.mod` dependencies, add go1.25 to the test matrix, **goquery now requires Go version 1.24+**. +* **2025-04-11 (v1.10.3)** : Update `go.mod` dependencies, small optimization (thanks [@myxzlpltk](https://github.com/myxzlpltk)). * **2025-02-13 (v1.10.2)** : Update `go.mod` dependencies, add go1.24 to the test matrix. * **2024-12-26 (v1.10.1)** : Update `go.mod` dependencies. * **2024-09-06 (v1.10.0)** : Add `EachIter` which provides an iterator that can be used in `for..range` loops on the `*Selection` object. **goquery now requires Go version 1.23+** (thanks [@amikai](https://github.com/amikai)). diff --git a/backend/vendor/github.com/PuerkitoBio/goquery/property.go b/backend/vendor/github.com/PuerkitoBio/goquery/property.go index 411126db..25afceda 100644 --- a/backend/vendor/github.com/PuerkitoBio/goquery/property.go +++ b/backend/vendor/github.com/PuerkitoBio/goquery/property.go @@ -1,7 +1,6 @@ package goquery import ( - "bytes" "regexp" "strings" @@ -60,14 +59,14 @@ func (s *Selection) SetAttr(attrName, val string) *Selection { // Text gets the combined text contents of each element in the set of matched // elements, including their descendants. func (s *Selection) Text() string { - var buf bytes.Buffer + var builder strings.Builder // Slightly optimized vs calling Each: no single selection object created var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.TextNode { // Keep newlines and spaces, like jQuery - buf.WriteString(n.Data) + builder.WriteString(n.Data) } if n.FirstChild != nil { for c := n.FirstChild; c != nil; c = c.NextSibling { @@ -79,7 +78,7 @@ func (s *Selection) Text() string { f(n) } - return buf.String() + return builder.String() } // Size is an alias for Length. @@ -97,16 +96,16 @@ func (s *Selection) Length() int { func (s *Selection) Html() (ret string, e error) { // Since there is no .innerHtml, the HTML content must be re-created from // the nodes using html.Render. - var buf bytes.Buffer + var builder strings.Builder if len(s.Nodes) > 0 { for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling { - e = html.Render(&buf, c) + e = html.Render(&builder, c) if e != nil { return } } - ret = buf.String() + ret = builder.String() } return @@ -168,7 +167,7 @@ func (s *Selection) RemoveClass(class ...string) *Selection { } else { classes, attr := getClassesAndAttr(n, true) for _, rcl := range rclasses { - classes = strings.Replace(classes, " "+rcl+" ", " ", -1) + classes = strings.ReplaceAll(classes, " "+rcl+" ", " ") } setClasses(n, attr, classes) @@ -192,8 +191,9 @@ func (s *Selection) ToggleClass(class ...string) *Selection { for _, n := range s.Nodes { classes, attr := getClassesAndAttr(n, true) for _, tcl := range tcls { - if strings.Contains(classes, " "+tcl+" ") { - classes = strings.Replace(classes, " "+tcl+" ", " ", -1) + spaceAroundTcl := " " + tcl + " " + if strings.Contains(classes, spaceAroundTcl) { + classes = strings.ReplaceAll(classes, spaceAroundTcl, " ") } else { classes += tcl + " " } diff --git a/backend/vendor/github.com/PuerkitoBio/goquery/utilities.go b/backend/vendor/github.com/PuerkitoBio/goquery/utilities.go index ecd3453f..361795b4 100644 --- a/backend/vendor/github.com/PuerkitoBio/goquery/utilities.go +++ b/backend/vendor/github.com/PuerkitoBio/goquery/utilities.go @@ -1,8 +1,8 @@ package goquery import ( - "bytes" "io" + "strings" "golang.org/x/net/html" ) @@ -26,13 +26,12 @@ var nodeNames = []string{ // Go's net/html package defines the following node types, listed with // the corresponding returned value from this function: // -// ErrorNode : #error -// TextNode : #text -// DocumentNode : #document -// ElementNode : the element's tag name -// CommentNode : #comment -// DoctypeNode : the name of the document type -// +// ErrorNode : #error +// TextNode : #text +// DocumentNode : #document +// ElementNode : the element's tag name +// CommentNode : #comment +// DoctypeNode : the name of the document type func NodeName(s *Selection) string { if s.Length() == 0 { return "" @@ -77,11 +76,11 @@ func Render(w io.Writer, s *Selection) error { // because this is not a jQuery method (in javascript-land, this is // a property provided by the DOM). func OuterHtml(s *Selection) (string, error) { - var buf bytes.Buffer - if err := Render(&buf, s); err != nil { + var builder strings.Builder + if err := Render(&builder, s); err != nil { return "", err } - return buf.String(), nil + return builder.String(), nil } // Loop through all container nodes to search for the target node. diff --git a/backend/vendor/github.com/ajg/form/.travis.yml b/backend/vendor/github.com/ajg/form/.travis.yml deleted file mode 100644 index 14608c76..00000000 --- a/backend/vendor/github.com/ajg/form/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -## Copyright 2014 Alvaro J. Genial. All rights reserved. -## Use of this source code is governed by a BSD-style -## license that can be found in the LICENSE file. - -language: go - -go: - - tip - - 1.6 - - 1.5 - - 1.4 - - 1.3 - # 1.2 - -before_install: - # - go get -v golang.org/x/tools/cmd/cover - # - go get -v golang.org/x/tools/cmd/vet - # - go get -v golang.org/x/lint/golint - - export PATH=$PATH:/home/travis/gopath/bin - -script: - - go build -v ./... - - go test -v -cover ./... - - go vet ./... - # - golint . diff --git a/backend/vendor/github.com/ajg/form/LICENSE b/backend/vendor/github.com/ajg/form/LICENSE deleted file mode 100644 index 9190b165..00000000 --- a/backend/vendor/github.com/ajg/form/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014 Alvaro J. Genial. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/backend/vendor/github.com/ajg/form/README.md b/backend/vendor/github.com/ajg/form/README.md deleted file mode 100644 index ad99be4b..00000000 --- a/backend/vendor/github.com/ajg/form/README.md +++ /dev/null @@ -1,247 +0,0 @@ -form -==== - -A Form Encoding & Decoding Package for Go, written by [Alvaro J. Genial](http://alva.ro). - -[![Build Status](https://travis-ci.org/ajg/form.png?branch=master)](https://travis-ci.org/ajg/form) -[![GoDoc](https://godoc.org/github.com/ajg/form?status.png)](https://godoc.org/github.com/ajg/form) - -Synopsis --------- - -This library is designed to allow seamless, high-fidelity encoding and decoding of arbitrary data in `application/x-www-form-urlencoded` format and as [`url.Values`](http://golang.org/pkg/net/url/#Values). It is intended to be useful primarily in dealing with web forms and URI query strings, both of which natively employ said format. - -Unsurprisingly, `form` is modeled after other Go [`encoding`](http://golang.org/pkg/encoding/) packages, in particular [`encoding/json`](http://golang.org/pkg/encoding/json/), and follows the same conventions (see below for more.) It aims to automatically handle any kind of concrete Go [data value](#values) (i.e., not functions, channels, etc.) while providing mechanisms for custom behavior. - -Status ------- - -The implementation is in usable shape and is fairly well tested with its accompanying test suite. The API is unlikely to change much, but still may. Lastly, the code has not yet undergone a security review to ensure it is free of vulnerabilities. Please file an issue or send a pull request for fixes & improvements. - -Dependencies ------------- - -The only requirement is [Go 1.2](http://golang.org/doc/go1.2) or later. - -Usage ------ - -```go -import "github.com/ajg/form" -// or: "gopkg.in/ajg/form.v1" -``` - -Given a type like the following... - -```go -type User struct { - Name string `form:"name"` - Email string `form:"email"` - Joined time.Time `form:"joined,omitempty"` - Posts []int `form:"posts"` - Preferences map[string]string `form:"prefs"` - Avatar []byte `form:"avatar"` - PasswordHash int64 `form:"-"` -} -``` - -...it is easy to encode data of that type... - - -```go -func PostUser(url string, u User) error { - var c http.Client - _, err := c.PostForm(url, form.EncodeToValues(u)) - return err -} -``` - -...as well as decode it... - - -```go -func Handler(w http.ResponseWriter, r *http.Request) { - var u User - - d := form.NewDecoder(r.Body) - if err := d.Decode(&u); err != nil { - http.Error(w, "Form could not be decoded", http.StatusBadRequest) - return - } - - fmt.Fprintf(w, "Decoded: %#v", u) -} -``` - -...without having to do any grunt work. - -Field Tags ----------- - -Like other encoding packages, `form` supports the following options for fields: - - - `` `form:"-"` ``: Causes the field to be ignored during encoding and decoding. - - `` `form:""` ``: Overrides the field's name; useful especially when dealing with external identifiers in camelCase, as are commonly found on the web. - - `` `form:",omitempty"` ``: Elides the field during encoding if it is empty (typically meaning equal to the type's zero value.) - - `` `form:",omitempty"` ``: The way to combine the two options above. - -Values ------- - -### Simple Values - -Values of the following types are all considered simple: - - - `bool` - - `int`, `int8`, `int16`, `int32`, `int64`, `rune` - - `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `byte` - - `float32`, `float64` - - `complex64`, `complex128` - - `string` - - `[]byte` (see note) - - [`time.Time`](http://golang.org/pkg/time/#Time) - - [`url.URL`](http://golang.org/pkg/net/url/#URL) - - An alias of any of the above - - A pointer to any of the above - -### Composite Values - -A composite value is one that can contain other values. Values of the following kinds... - - - Maps - - Slices; except `[]byte` (see note) - - Structs; except [`time.Time`](http://golang.org/pkg/time/#Time) and [`url.URL`](http://golang.org/pkg/net/url/#URL) - - Arrays - - An alias of any of the above - - A pointer to any of the above - -...are considered composites in general, unless they implement custom marshaling/unmarshaling. Composite values are encoded as a flat mapping of paths to values, where the paths are constructed by joining the parent and child paths with a period (`.`). - -(Note: a byte slice is treated as a `string` by default because it's more efficient, but can also be decoded as a slice—i.e., with indexes.) - -### Untyped Values - -While encouraged, it is not necessary to define a type (e.g. a `struct`) in order to use `form`, since it is able to encode and decode untyped data generically using the following rules: - - - Simple values will be treated as a `string`. - - Composite values will be treated as a `map[string]interface{}`, itself able to contain nested values (both scalar and compound) ad infinitum. - - However, if there is a value (of any supported type) already present in a map for a given key, then it will be used when possible, rather than being replaced with a generic value as specified above; this makes it possible to handle partially typed, dynamic or schema-less values. - -### Zero Values - -By default, and without custom marshaling, zero values (also known as empty/default values) are encoded as the empty string. To disable this behavior, meaning to keep zero values in their literal form (e.g. `0` for integral types), `Encoder` offers a `KeepZeros` setter method, which will do just that when set to `true`. - -### Unsupported Values - -Values of the following kinds aren't supported and, if present, must be ignored. - - - Channel - - Function - - Unsafe pointer - - An alias of any of the above - - A pointer to any of the above - -Custom Marshaling ------------------ - -There is a default (generally lossless) marshaling & unmarshaling scheme for any concrete data value in Go, which is good enough in most cases. However, it is possible to override it and use a custom scheme. For instance, a "binary" field could be marshaled more efficiently using [base64](http://golang.org/pkg/encoding/base64/) to prevent it from being percent-escaped during serialization to `application/x-www-form-urlencoded` format. - -Because `form` provides support for [`encoding.TextMarshaler`](http://golang.org/pkg/encoding/#TextMarshaler) and [`encoding.TextUnmarshaler`](http://golang.org/pkg/encoding/#TextUnmarshaler) it is easy to do that; for instance, like this: - -```go -import "encoding" - -type Binary []byte - -var ( - _ encoding.TextMarshaler = &Binary{} - _ encoding.TextUnmarshaler = &Binary{} -) - -func (b Binary) MarshalText() ([]byte, error) { - return []byte(base64.URLEncoding.EncodeToString([]byte(b))), nil -} - -func (b *Binary) UnmarshalText(text []byte) error { - bs, err := base64.URLEncoding.DecodeString(string(text)) - if err == nil { - *b = Binary(bs) - } - return err -} -``` - -Now any value with type `Binary` will automatically be encoded using the [URL](http://golang.org/pkg/encoding/base64/#URLEncoding) variant of base64. It is left as an exercise to the reader to improve upon this scheme by eliminating the need for padding (which, besides being superfluous, uses `=`, a character that will end up percent-escaped.) - -Keys ----- - -In theory any value can be a key as long as it has a string representation. However, by default, periods have special meaning to `form`, and thus, under the hood (i.e. in encoded form) they are transparently escaped using a preceding backslash (`\`). Backslashes within keys, themselves, are also escaped in this manner (e.g. as `\\`) in order to permit representing `\.` itself (as `\\\.`). - -(Note: it is normally unnecessary to deal with this issue unless keys are being constructed manually—e.g. literally embedded in HTML or in a URI.) - -The default delimiter and escape characters used for encoding and decoding composite keys can be changed using the `DelimitWith` and `EscapeWith` setter methods of `Encoder` and `Decoder`, respectively. For example... - -```go -package main - -import ( - "os" - - "github.com/ajg/form" -) - -func main() { - type B struct { - Qux string `form:"qux"` - } - type A struct { - FooBar B `form:"foo.bar"` - } - a := A{FooBar: B{"XYZ"}} - os.Stdout.WriteString("Default: ") - form.NewEncoder(os.Stdout).Encode(a) - os.Stdout.WriteString("\nCustom: ") - form.NewEncoder(os.Stdout).DelimitWith('/').Encode(a) - os.Stdout.WriteString("\n") -} - -``` - -...will produce... - -``` -Default: foo%5C.bar.qux=XYZ -Custom: foo.bar%2Fqux=XYZ -``` - -(`%5C` and `%2F` represent `\` and `/`, respectively.) - -Limitations ------------ - - - Circular (self-referential) values are untested. - -Future Work ------------ - -The following items would be nice to have in the future—though they are not being worked on yet: - - - An option to treat all values as if they had been tagged with `omitempty`. - - An option to automatically treat all field names in `camelCase` or `underscore_case`. - - Built-in support for the types in [`math/big`](http://golang.org/pkg/math/big/). - - Built-in support for the types in [`image/color`](http://golang.org/pkg/image/color/). - - Improve encoding/decoding by reading/writing directly from/to the `io.Reader`/`io.Writer` when possible, rather than going through an intermediate representation (i.e. `node`) which requires more memory. - -(Feel free to implement any of these and then send a pull request.) - -Related Work ------------- - - - Package [gorilla/schema](https://github.com/gorilla/schema), which only implements decoding. - - Package [google/go-querystring](https://github.com/google/go-querystring), which only implements encoding. - -License -------- - -This library is distributed under a BSD-style [LICENSE](./LICENSE). diff --git a/backend/vendor/github.com/ajg/form/TODO.md b/backend/vendor/github.com/ajg/form/TODO.md deleted file mode 100644 index d3447279..00000000 --- a/backend/vendor/github.com/ajg/form/TODO.md +++ /dev/null @@ -1,4 +0,0 @@ -TODO -==== - - - Document IgnoreCase and IgnoreUnknownKeys in README. diff --git a/backend/vendor/github.com/ajg/form/decode.go b/backend/vendor/github.com/ajg/form/decode.go deleted file mode 100644 index dd8bd4f2..00000000 --- a/backend/vendor/github.com/ajg/form/decode.go +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2014 Alvaro J. Genial. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package form - -import ( - "fmt" - "io" - "io/ioutil" - "net/url" - "reflect" - "strconv" - "time" -) - -// NewDecoder returns a new form Decoder. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{r, defaultDelimiter, defaultEscape, false, false} -} - -// Decoder decodes data from a form (application/x-www-form-urlencoded). -type Decoder struct { - r io.Reader - d rune - e rune - ignoreUnknown bool - ignoreCase bool -} - -// DelimitWith sets r as the delimiter used for composite keys by Decoder d and returns the latter; it is '.' by default. -func (d *Decoder) DelimitWith(r rune) *Decoder { - d.d = r - return d -} - -// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Decoder d and returns the latter; it is '\\' by default. -func (d *Decoder) EscapeWith(r rune) *Decoder { - d.e = r - return d -} - -// Decode reads in and decodes form-encoded data into dst. -func (d Decoder) Decode(dst interface{}) error { - bs, err := ioutil.ReadAll(d.r) - if err != nil { - return err - } - vs, err := url.ParseQuery(string(bs)) - if err != nil { - return err - } - v := reflect.ValueOf(dst) - return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) -} - -// IgnoreUnknownKeys if set to true it will make the Decoder ignore values -// that are not found in the destination object instead of returning an error. -func (d *Decoder) IgnoreUnknownKeys(ignoreUnknown bool) { - d.ignoreUnknown = ignoreUnknown -} - -// IgnoreCase if set to true it will make the Decoder try to set values in the -// destination object even if the case does not match. -func (d *Decoder) IgnoreCase(ignoreCase bool) { - d.ignoreCase = ignoreCase -} - -// DecodeString decodes src into dst. -func (d Decoder) DecodeString(dst interface{}, src string) error { - vs, err := url.ParseQuery(src) - if err != nil { - return err - } - v := reflect.ValueOf(dst) - return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) -} - -// DecodeValues decodes vs into dst. -func (d Decoder) DecodeValues(dst interface{}, vs url.Values) error { - v := reflect.ValueOf(dst) - return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) -} - -// DecodeString decodes src into dst. -func DecodeString(dst interface{}, src string) error { - return NewDecoder(nil).DecodeString(dst, src) -} - -// DecodeValues decodes vs into dst. -func DecodeValues(dst interface{}, vs url.Values) error { - return NewDecoder(nil).DecodeValues(dst, vs) -} - -func (d Decoder) decodeNode(v reflect.Value, n node) (err error) { - defer func() { - if e := recover(); e != nil { - err = fmt.Errorf("%v", e) - } - }() - - if v.Kind() == reflect.Slice { - return fmt.Errorf("could not decode directly into slice; use pointer to slice") - } - d.decodeValue(v, n) - return nil -} - -func (d Decoder) decodeValue(v reflect.Value, x interface{}) { - t := v.Type() - k := v.Kind() - - if k == reflect.Ptr && v.IsNil() { - v.Set(reflect.New(t.Elem())) - } - - if unmarshalValue(v, x) { - return - } - - empty := isEmpty(x) - - switch k { - case reflect.Ptr: - d.decodeValue(v.Elem(), x) - return - case reflect.Interface: - if !v.IsNil() { - d.decodeValue(v.Elem(), x) - return - - } else if empty { - return // Allow nil interfaces only if empty. - } else { - panic("form: cannot decode non-empty value into into nil interface") - } - } - - if empty { - v.Set(reflect.Zero(t)) // Treat the empty string as the zero value. - return - } - - switch k { - case reflect.Struct: - if t.ConvertibleTo(timeType) { - d.decodeTime(v, x) - } else if t.ConvertibleTo(urlType) { - d.decodeURL(v, x) - } else { - d.decodeStruct(v, x) - } - case reflect.Slice: - d.decodeSlice(v, x) - case reflect.Array: - d.decodeArray(v, x) - case reflect.Map: - d.decodeMap(v, x) - case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: - panic(t.String() + " has unsupported kind " + k.String()) - default: - d.decodeBasic(v, x) - } -} - -func (d Decoder) decodeStruct(v reflect.Value, x interface{}) { - t := v.Type() - for k, c := range getNode(x) { - if f, ok := findField(v, k, d.ignoreCase); !ok && k == "" { - panic(getString(x) + " cannot be decoded as " + t.String()) - } else if !ok { - if !d.ignoreUnknown { - panic(k + " doesn't exist in " + t.String()) - } - } else if !f.CanSet() { - panic(k + " cannot be set in " + t.String()) - } else { - d.decodeValue(f, c) - } - } -} - -func (d Decoder) decodeMap(v reflect.Value, x interface{}) { - t := v.Type() - if v.IsNil() { - v.Set(reflect.MakeMap(t)) - } - for k, c := range getNode(x) { - i := reflect.New(t.Key()).Elem() - d.decodeValue(i, k) - - w := v.MapIndex(i) - if w.IsValid() { // We have an actual element value to decode into. - if w.Kind() == reflect.Interface { - w = w.Elem() - } - w = reflect.New(w.Type()).Elem() - } else if t.Elem().Kind() != reflect.Interface { // The map's element type is concrete. - w = reflect.New(t.Elem()).Elem() - } else { - // The best we can do here is to decode as either a string (for scalars) or a map[string]interface {} (for the rest). - // We could try to guess the type based on the string (e.g. true/false => bool) but that'll get ugly fast, - // especially if we have to guess the kind (slice vs. array vs. map) and index type (e.g. string, int, etc.) - switch c.(type) { - case node: - w = reflect.MakeMap(stringMapType) - case string: - w = reflect.New(stringType).Elem() - default: - panic("value is neither node nor string") - } - } - - d.decodeValue(w, c) - v.SetMapIndex(i, w) - } -} - -func (d Decoder) decodeArray(v reflect.Value, x interface{}) { - t := v.Type() - for k, c := range getNode(x) { - i, err := strconv.Atoi(k) - if err != nil { - panic(k + " is not a valid index for type " + t.String()) - } - if l := v.Len(); i >= l { - panic("index is above array size") - } - d.decodeValue(v.Index(i), c) - } -} - -func (d Decoder) decodeSlice(v reflect.Value, x interface{}) { - t := v.Type() - if t.Elem().Kind() == reflect.Uint8 { - // Allow, but don't require, byte slices to be encoded as a single string. - if s, ok := x.(string); ok { - v.SetBytes([]byte(s)) - return - } - } - - // NOTE: Implicit indexing is currently done at the parseValues level, - // so if if an implicitKey reaches here it will always replace the last. - implicit := 0 - for k, c := range getNode(x) { - var i int - if k == implicitKey { - i = implicit - implicit++ - } else { - explicit, err := strconv.Atoi(k) - if err != nil { - panic(k + " is not a valid index for type " + t.String()) - } - i = explicit - implicit = explicit + 1 - } - // "Extend" the slice if it's too short. - if l := v.Len(); i >= l { - delta := i - l + 1 - v.Set(reflect.AppendSlice(v, reflect.MakeSlice(t, delta, delta))) - } - d.decodeValue(v.Index(i), c) - } -} - -func (d Decoder) decodeBasic(v reflect.Value, x interface{}) { - t := v.Type() - switch k, s := t.Kind(), getString(x); k { - case reflect.Bool: - if b, e := strconv.ParseBool(s); e == nil { - v.SetBool(b) - } else { - panic("could not parse bool from " + strconv.Quote(s)) - } - case reflect.Int, - reflect.Int8, - reflect.Int16, - reflect.Int32, - reflect.Int64: - if i, e := strconv.ParseInt(s, 10, 64); e == nil { - v.SetInt(i) - } else { - panic("could not parse int from " + strconv.Quote(s)) - } - case reflect.Uint, - reflect.Uint8, - reflect.Uint16, - reflect.Uint32, - reflect.Uint64: - if u, e := strconv.ParseUint(s, 10, 64); e == nil { - v.SetUint(u) - } else { - panic("could not parse uint from " + strconv.Quote(s)) - } - case reflect.Float32, - reflect.Float64: - if f, e := strconv.ParseFloat(s, 64); e == nil { - v.SetFloat(f) - } else { - panic("could not parse float from " + strconv.Quote(s)) - } - case reflect.Complex64, - reflect.Complex128: - var c complex128 - if n, err := fmt.Sscanf(s, "%g", &c); n == 1 && err == nil { - v.SetComplex(c) - } else { - panic("could not parse complex from " + strconv.Quote(s)) - } - case reflect.String: - v.SetString(s) - default: - panic(t.String() + " has unsupported kind " + k.String()) - } -} - -func (d Decoder) decodeTime(v reflect.Value, x interface{}) { - t := v.Type() - s := getString(x) - // TODO: Find a more efficient way to do this. - for _, f := range allowedTimeFormats { - if p, err := time.Parse(f, s); err == nil { - v.Set(reflect.ValueOf(p).Convert(v.Type())) - return - } - } - panic("cannot decode string `" + s + "` as " + t.String()) -} - -func (d Decoder) decodeURL(v reflect.Value, x interface{}) { - t := v.Type() - s := getString(x) - if u, err := url.Parse(s); err == nil { - v.Set(reflect.ValueOf(*u).Convert(v.Type())) - return - } - panic("cannot decode string `" + s + "` as " + t.String()) -} - -var allowedTimeFormats = []string{ - "2006-01-02T15:04:05.999999999Z07:00", - "2006-01-02T15:04:05.999999999Z07", - "2006-01-02T15:04:05.999999999Z", - "2006-01-02T15:04:05.999999999", - "2006-01-02T15:04:05Z07:00", - "2006-01-02T15:04:05Z07", - "2006-01-02T15:04:05Z", - "2006-01-02T15:04:05", - "2006-01-02T15:04Z", - "2006-01-02T15:04", - "2006-01-02T15Z", - "2006-01-02T15", - "2006-01-02", - "2006-01", - "2006", - "15:04:05.999999999Z07:00", - "15:04:05.999999999Z07", - "15:04:05.999999999Z", - "15:04:05.999999999", - "15:04:05Z07:00", - "15:04:05Z07", - "15:04:05Z", - "15:04:05", - "15:04Z", - "15:04", - "15Z", - "15", -} diff --git a/backend/vendor/github.com/ajg/form/encode.go b/backend/vendor/github.com/ajg/form/encode.go deleted file mode 100644 index 57a0d0a5..00000000 --- a/backend/vendor/github.com/ajg/form/encode.go +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright 2014 Alvaro J. Genial. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package form - -import ( - "encoding" - "errors" - "fmt" - "io" - "net/url" - "reflect" - "strconv" - "strings" - "time" -) - -// NewEncoder returns a new form Encoder. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{w, defaultDelimiter, defaultEscape, false} -} - -// Encoder provides a way to encode to a Writer. -type Encoder struct { - w io.Writer - d rune - e rune - z bool -} - -// DelimitWith sets r as the delimiter used for composite keys by Encoder e and returns the latter; it is '.' by default. -func (e *Encoder) DelimitWith(r rune) *Encoder { - e.d = r - return e -} - -// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Encoder e and returns the latter; it is '\\' by default. -func (e *Encoder) EscapeWith(r rune) *Encoder { - e.e = r - return e -} - -// KeepZeros sets whether Encoder e should keep zero (default) values in their literal form when encoding, and returns the former; by default zero values are not kept, but are rather encoded as the empty string. -func (e *Encoder) KeepZeros(z bool) *Encoder { - e.z = z - return e -} - -// Encode encodes dst as form and writes it out using the Encoder's Writer. -func (e Encoder) Encode(dst interface{}) error { - v := reflect.ValueOf(dst) - n, err := encodeToNode(v, e.z) - if err != nil { - return err - } - s := n.values(e.d, e.e).Encode() - l, err := io.WriteString(e.w, s) - switch { - case err != nil: - return err - case l != len(s): - return errors.New("could not write data completely") - } - return nil -} - -// EncodeToString encodes dst as a form and returns it as a string. -func EncodeToString(dst interface{}) (string, error) { - v := reflect.ValueOf(dst) - n, err := encodeToNode(v, false) - if err != nil { - return "", err - } - vs := n.values(defaultDelimiter, defaultEscape) - return vs.Encode(), nil -} - -// EncodeToValues encodes dst as a form and returns it as Values. -func EncodeToValues(dst interface{}) (url.Values, error) { - v := reflect.ValueOf(dst) - n, err := encodeToNode(v, false) - if err != nil { - return nil, err - } - vs := n.values(defaultDelimiter, defaultEscape) - return vs, nil -} - -func encodeToNode(v reflect.Value, z bool) (n node, err error) { - defer func() { - if e := recover(); e != nil { - err = fmt.Errorf("%v", e) - } - }() - return getNode(encodeValue(v, z)), nil -} - -func encodeValue(v reflect.Value, z bool) interface{} { - t := v.Type() - k := v.Kind() - - if s, ok := marshalValue(v); ok { - return s - } else if !z && isEmptyValue(v) { - return "" // Treat the zero value as the empty string. - } - - switch k { - case reflect.Ptr, reflect.Interface: - return encodeValue(v.Elem(), z) - case reflect.Struct: - if t.ConvertibleTo(timeType) { - return encodeTime(v) - } else if t.ConvertibleTo(urlType) { - return encodeURL(v) - } - return encodeStruct(v, z) - case reflect.Slice: - return encodeSlice(v, z) - case reflect.Array: - return encodeArray(v, z) - case reflect.Map: - return encodeMap(v, z) - case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: - panic(t.String() + " has unsupported kind " + t.Kind().String()) - default: - return encodeBasic(v) - } -} - -func encodeStruct(v reflect.Value, z bool) interface{} { - t := v.Type() - n := node{} - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - k, oe := fieldInfo(f) - - if k == "-" { - continue - } else if fv := v.Field(i); oe && isEmptyValue(fv) { - delete(n, k) - } else { - n[k] = encodeValue(fv, z) - } - } - return n -} - -func encodeMap(v reflect.Value, z bool) interface{} { - n := node{} - for _, i := range v.MapKeys() { - k := getString(encodeValue(i, z)) - n[k] = encodeValue(v.MapIndex(i), z) - } - return n -} - -func encodeArray(v reflect.Value, z bool) interface{} { - n := node{} - for i := 0; i < v.Len(); i++ { - n[strconv.Itoa(i)] = encodeValue(v.Index(i), z) - } - return n -} - -func encodeSlice(v reflect.Value, z bool) interface{} { - t := v.Type() - if t.Elem().Kind() == reflect.Uint8 { - return string(v.Bytes()) // Encode byte slices as a single string by default. - } - n := node{} - for i := 0; i < v.Len(); i++ { - n[strconv.Itoa(i)] = encodeValue(v.Index(i), z) - } - return n -} - -func encodeTime(v reflect.Value) string { - t := v.Convert(timeType).Interface().(time.Time) - if t.Year() == 0 && (t.Month() == 0 || t.Month() == 1) && (t.Day() == 0 || t.Day() == 1) { - return t.Format("15:04:05.999999999Z07:00") - } else if t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 && t.Nanosecond() == 0 { - return t.Format("2006-01-02") - } - return t.Format("2006-01-02T15:04:05.999999999Z07:00") -} - -func encodeURL(v reflect.Value) string { - u := v.Convert(urlType).Interface().(url.URL) - return u.String() -} - -func encodeBasic(v reflect.Value) string { - t := v.Type() - switch k := t.Kind(); k { - case reflect.Bool: - return strconv.FormatBool(v.Bool()) - case reflect.Int, - reflect.Int8, - reflect.Int16, - reflect.Int32, - reflect.Int64: - return strconv.FormatInt(v.Int(), 10) - case reflect.Uint, - reflect.Uint8, - reflect.Uint16, - reflect.Uint32, - reflect.Uint64: - return strconv.FormatUint(v.Uint(), 10) - case reflect.Float32: - return strconv.FormatFloat(v.Float(), 'g', -1, 32) - case reflect.Float64: - return strconv.FormatFloat(v.Float(), 'g', -1, 64) - case reflect.Complex64, reflect.Complex128: - s := fmt.Sprintf("%g", v.Complex()) - return strings.TrimSuffix(strings.TrimPrefix(s, "("), ")") - case reflect.String: - return v.String() - } - panic(t.String() + " has unsupported kind " + t.Kind().String()) -} - -func isEmptyValue(v reflect.Value) bool { - switch t := v.Type(); v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Complex64, reflect.Complex128: - return v.Complex() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - case reflect.Struct: - if t.ConvertibleTo(timeType) { - return v.Convert(timeType).Interface().(time.Time).IsZero() - } - return reflect.DeepEqual(v, reflect.Zero(t)) - } - return false -} - -// canIndexOrdinally returns whether a value contains an ordered sequence of elements. -func canIndexOrdinally(v reflect.Value) bool { - if !v.IsValid() { - return false - } - switch t := v.Type(); t.Kind() { - case reflect.Ptr, reflect.Interface: - return canIndexOrdinally(v.Elem()) - case reflect.Slice, reflect.Array: - return true - } - return false -} - -func fieldInfo(f reflect.StructField) (k string, oe bool) { - if f.PkgPath != "" { // Skip private fields. - return omittedKey, oe - } - - k = f.Name - tag := f.Tag.Get("form") - if tag == "" { - return k, oe - } - - ps := strings.SplitN(tag, ",", 2) - if ps[0] != "" { - k = ps[0] - } - if len(ps) == 2 { - oe = ps[1] == "omitempty" - } - return k, oe -} - -func findField(v reflect.Value, n string, ignoreCase bool) (reflect.Value, bool) { - t := v.Type() - l := v.NumField() - - var lowerN string - caseInsensitiveMatch := -1 - if ignoreCase { - lowerN = strings.ToLower(n) - } - - // First try named fields. - for i := 0; i < l; i++ { - f := t.Field(i) - k, _ := fieldInfo(f) - if k == omittedKey { - continue - } else if n == k { - return v.Field(i), true - } else if ignoreCase && lowerN == strings.ToLower(k) { - caseInsensitiveMatch = i - } - } - - // If no exact match was found try case insensitive match. - if caseInsensitiveMatch != -1 { - return v.Field(caseInsensitiveMatch), true - } - - // Then try anonymous (embedded) fields. - for i := 0; i < l; i++ { - f := t.Field(i) - k, _ := fieldInfo(f) - if k == omittedKey || !f.Anonymous { // || k != "" ? - continue - } - fv := v.Field(i) - fk := fv.Kind() - for fk == reflect.Ptr || fk == reflect.Interface { - fv = fv.Elem() - fk = fv.Kind() - } - - if fk != reflect.Struct { - continue - } - if ev, ok := findField(fv, n, ignoreCase); ok { - return ev, true - } - } - - return reflect.Value{}, false -} - -var ( - stringType = reflect.TypeOf(string("")) - stringMapType = reflect.TypeOf(map[string]interface{}{}) - timeType = reflect.TypeOf(time.Time{}) - timePtrType = reflect.TypeOf(&time.Time{}) - urlType = reflect.TypeOf(url.URL{}) -) - -func skipTextMarshalling(t reflect.Type) bool { - /*// Skip time.Time because its text unmarshaling is overly rigid: - return t == timeType || t == timePtrType*/ - // Skip time.Time & convertibles because its text unmarshaling is overly rigid: - return t.ConvertibleTo(timeType) || t.ConvertibleTo(timePtrType) -} - -func unmarshalValue(v reflect.Value, x interface{}) bool { - if skipTextMarshalling(v.Type()) { - return false - } - - tu, ok := v.Interface().(encoding.TextUnmarshaler) - if !ok && !v.CanAddr() { - return false - } else if !ok { - return unmarshalValue(v.Addr(), x) - } - - s := getString(x) - if err := tu.UnmarshalText([]byte(s)); err != nil { - panic(err) - } - return true -} - -func marshalValue(v reflect.Value) (string, bool) { - if skipTextMarshalling(v.Type()) { - return "", false - } - - tm, ok := v.Interface().(encoding.TextMarshaler) - if !ok && !v.CanAddr() { - return "", false - } else if !ok { - return marshalValue(v.Addr()) - } - - bs, err := tm.MarshalText() - if err != nil { - panic(err) - } - return string(bs), true -} diff --git a/backend/vendor/github.com/ajg/form/form.go b/backend/vendor/github.com/ajg/form/form.go deleted file mode 100644 index 4052369c..00000000 --- a/backend/vendor/github.com/ajg/form/form.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2014 Alvaro J. Genial. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package form implements encoding and decoding of application/x-www-form-urlencoded data. -package form - -const ( - implicitKey = "_" - omittedKey = "-" - - defaultDelimiter = '.' - defaultEscape = '\\' -) diff --git a/backend/vendor/github.com/ajg/form/node.go b/backend/vendor/github.com/ajg/form/node.go deleted file mode 100644 index 567aaafd..00000000 --- a/backend/vendor/github.com/ajg/form/node.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2014 Alvaro J. Genial. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package form - -import ( - "net/url" - "strconv" - "strings" -) - -type node map[string]interface{} - -func (n node) values(d, e rune) url.Values { - vs := url.Values{} - n.merge(d, e, "", &vs) - return vs -} - -func (n node) merge(d, e rune, p string, vs *url.Values) { - for k, x := range n { - switch y := x.(type) { - case string: - vs.Add(p+escape(d, e, k), y) - case node: - y.merge(d, e, p+escape(d, e, k)+string(d), vs) - default: - panic("value is neither string nor node") - } - } -} - -// TODO: Add tests for implicit indexing. -func parseValues(d, e rune, vs url.Values, canIndexFirstLevelOrdinally bool) node { - // NOTE: Because of the flattening of potentially multiple strings to one key, implicit indexing works: - // i. At the first level; e.g. Foo.Bar=A&Foo.Bar=B becomes 0.Foo.Bar=A&1.Foo.Bar=B - // ii. At the last level; e.g. Foo.Bar._=A&Foo.Bar._=B becomes Foo.Bar.0=A&Foo.Bar.1=B - // TODO: At in-between levels; e.g. Foo._.Bar=A&Foo._.Bar=B becomes Foo.0.Bar=A&Foo.1.Bar=B - // (This last one requires that there only be one placeholder in order for it to be unambiguous.) - - m := map[string]string{} - for k, ss := range vs { - indexLastLevelOrdinally := strings.HasSuffix(k, string(d)+implicitKey) - - for i, s := range ss { - if canIndexFirstLevelOrdinally { - k = strconv.Itoa(i) + string(d) + k - } else if indexLastLevelOrdinally { - k = strings.TrimSuffix(k, implicitKey) + strconv.Itoa(i) - } - - m[k] = s - } - } - - n := node{} - for k, s := range m { - n = n.split(d, e, k, s) - } - return n -} - -func splitPath(d, e rune, path string) (k, rest string) { - esc := false - for i, r := range path { - switch { - case !esc && r == e: - esc = true - case !esc && r == d: - return unescape(d, e, path[:i]), path[i+1:] - default: - esc = false - } - } - return unescape(d, e, path), "" -} - -func (n node) split(d, e rune, path, s string) node { - k, rest := splitPath(d, e, path) - if rest == "" { - return add(n, k, s) - } - if _, ok := n[k]; !ok { - n[k] = node{} - } - - c := getNode(n[k]) - n[k] = c.split(d, e, rest, s) - return n -} - -func add(n node, k, s string) node { - if n == nil { - return node{k: s} - } - - if _, ok := n[k]; ok { - panic("key " + k + " already set") - } - - n[k] = s - return n -} - -func isEmpty(x interface{}) bool { - switch y := x.(type) { - case string: - return y == "" - case node: - if s, ok := y[""].(string); ok { - return s == "" - } - return false - } - panic("value is neither string nor node") -} - -func getNode(x interface{}) node { - switch y := x.(type) { - case string: - return node{"": y} - case node: - return y - } - panic("value is neither string nor node") -} - -func getString(x interface{}) string { - switch y := x.(type) { - case string: - return y - case node: - if s, ok := y[""].(string); ok { - return s - } - return "" - } - panic("value is neither string nor node") -} - -func escape(d, e rune, s string) string { - s = strings.Replace(s, string(e), string(e)+string(e), -1) // Escape the escape (\ => \\) - s = strings.Replace(s, string(d), string(e)+string(d), -1) // Escape the delimiter (. => \.) - return s -} - -func unescape(d, e rune, s string) string { - s = strings.Replace(s, string(e)+string(d), string(d), -1) // Unescape the delimiter (\. => .) - s = strings.Replace(s, string(e)+string(e), string(e), -1) // Unescape the escape (\\ => \) - return s -} diff --git a/backend/vendor/github.com/ajg/form/pre-commit.sh b/backend/vendor/github.com/ajg/form/pre-commit.sh deleted file mode 100644 index 29ce311e..00000000 --- a/backend/vendor/github.com/ajg/form/pre-commit.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -eu - -# TODO: Only colorize messages given a suitable terminal. -# FIXME: Handle case in which no stash entry is created due to no changes. - -printf "\e[30m=== PRE-COMMIT STARTING ===\e[m\n" -git stash save --quiet --keep-index --include-untracked - -if go build -v ./... && go test -v -cover ./... && go vet ./... && golint . && travis-lint; then - result=$? - printf "\e[32m=== PRE-COMMIT SUCCEEDED ===\e[m\n" -else - result=$? - printf "\e[31m=== PRE-COMMIT FAILED ===\e[m\n" -fi - -git stash pop --quiet -exit $result diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/.gitignore b/backend/vendor/github.com/alecthomas/chroma/v2/.gitignore index 8cbdd75e..aedf83d9 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/.gitignore +++ b/backend/vendor/github.com/alecthomas/chroma/v2/.gitignore @@ -23,3 +23,6 @@ _models/ _examples/ *.min.* build/ + +cmd/chromad/static/chroma.wasm +cmd/chromad/static/wasm_exec.js diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/.golangci.yml b/backend/vendor/github.com/alecthomas/chroma/v2/.golangci.yml index 7e98a223..91f313b1 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/.golangci.yml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/.golangci.yml @@ -1,7 +1,5 @@ run: tests: true - skip-dirs: - - _examples output: print-issued-lines: false @@ -9,8 +7,6 @@ output: linters: enable-all: true disable: - - maligned - - megacheck - lll - gocyclo - dupl @@ -18,9 +14,7 @@ linters: - funlen - godox - wsl - - gomnd - gocognit - - goerr113 - nolintlint - testpackage - godot @@ -28,12 +22,10 @@ linters: - paralleltest - nlreturn - cyclop - - exhaustivestruct - gci - gofumpt - errorlint - exhaustive - - ifshort - wrapcheck - stylecheck - thelper @@ -46,7 +38,6 @@ linters: - ireturn - maintidx - govet - - nosnakecase - testableexamples - musttag - depguard @@ -54,10 +45,11 @@ linters: - perfsprint - mnd - predeclared + - recvcheck + - tenv + - err113 linters-settings: - govet: - check-shadowing: true gocyclo: min-complexity: 10 dupl: @@ -72,6 +64,8 @@ linters-settings: issues: + exclude-dirs: + - _examples max-per-linter: 0 max-same: 0 exclude-use-default: false diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/.goreleaser.yml b/backend/vendor/github.com/alecthomas/chroma/v2/.goreleaser.yml index 8cd7592d..f7c4f7d2 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/.goreleaser.yml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/.goreleaser.yml @@ -4,34 +4,31 @@ release: owner: alecthomas name: chroma brews: - - - install: bin.install "chroma" + - install: bin.install "chroma" env: - CGO_ENABLED=0 builds: -- goos: - - linux - - darwin - - windows - goarch: - - arm64 - - amd64 - - "386" - goarm: - - "6" - dir: ./cmd/chroma - main: . - ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} - binary: chroma + - goos: + - linux + - darwin + - windows + goarch: + - arm64 + - amd64 + - "386" + goarm: + - "6" + dir: ./cmd/chroma + main: . + ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} + binary: chroma archives: - - - format: tar.gz - name_template: '{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ - .Arm }}{{ end }}' + - format: tar.gz + name_template: "{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" files: - COPYING - README* snapshot: name_template: SNAPSHOT-{{ .Commit }} checksum: - name_template: '{{ .ProjectName }}-{{ .Version }}-checksums.txt' + name_template: "{{ .ProjectName }}-{{ .Version }}-checksums.txt" diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/Dockerfile b/backend/vendor/github.com/alecthomas/chroma/v2/Dockerfile new file mode 100644 index 00000000..e2a15313 --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/Dockerfile @@ -0,0 +1,65 @@ +# Multi-stage Dockerfile for chromad Go application using Hermit-managed tools + +# Build stage +FROM ubuntu:24.04 AS builder + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + curl \ + git \ + make \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /app + +# Copy the entire project (including bin directory with Hermit tools) +COPY . . + +# Make Hermit tools executable and add to PATH +ENV PATH="/app/bin:${PATH}" + +# Set Go environment variables for static compilation +ENV CGO_ENABLED=0 +ENV GOOS=linux +ENV GOARCH=amd64 + +# Build the application using make +RUN make build/chromad + +# Runtime stage +FROM alpine:3.22 AS runtime + +# Install ca-certificates for HTTPS requests +RUN apk --no-cache add ca-certificates curl + +# Create a non-root user +RUN addgroup -g 1001 chromad && \ + adduser -D -s /bin/sh -u 1001 -G chromad chromad + +# Set working directory +WORKDIR /app + +# Copy the binary from build stage +COPY --from=builder /app/build/chromad /app/chromad + +# Change ownership to non-root user +RUN chown chromad:chromad /app/chromad + +# Switch to non-root user +USER chromad + +# Expose port (default is 8080, but can be overridden via PORT env var) +EXPOSE 8080 + +# Set default environment variables +ENV PORT=8080 +ENV CHROMA_CSRF_KEY="testtest" + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -fsSL http://127.0.0.1:8080/ > /dev/null + +# Run the application +CMD ["sh", "-c", "./chromad --csrf-key=$CHROMA_CSRF_KEY --bind=0.0.0.0:$PORT"] diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/Makefile b/backend/vendor/github.com/alecthomas/chroma/v2/Makefile index e2ff762e..ca89f7cb 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/Makefile +++ b/backend/vendor/github.com/alecthomas/chroma/v2/Makefile @@ -6,17 +6,36 @@ export GOARCH ?= amd64 all: README.md tokentype_string.go -README.md: lexers/*/*.go - ./table.py +README.md: lexers/*.go lexers/embedded/*.xml + GOOS= GOARCH= ./table.py tokentype_string.go: types.go go generate -chromad: +.PHONY: format-js +format-js: + biome format --write cmd/chromad/static/{index.js,chroma.js} + +.PHONY: chromad +chromad: build/chromad + +build/chromad: $(shell find cmd/chromad -name '*.go' -o -name '*.html' -o -name '*.css' -o -name '*.js') \ + cmd/chromad/static/wasm_exec.js \ + cmd/chromad/static/chroma.wasm rm -rf build - esbuild --bundle cmd/chromad/static/index.js --minify --outfile=cmd/chromad/static/index.min.js + esbuild --platform=node --bundle cmd/chromad/static/index.js --minify --outfile=cmd/chromad/static/index.min.js esbuild --bundle cmd/chromad/static/index.css --minify --outfile=cmd/chromad/static/index.min.css - (export CGOENABLED=0 ; cd ./cmd/chromad && go build -ldflags="-X 'main.version=$(VERSION)'" -o ../../build/chromad .) + (export CGOENABLED=0 ; go build -C cmd/chromad -ldflags="-X 'main.version=$(VERSION)'" -o ../../build/chromad .) + +cmd/chromad/static/wasm_exec.js: $(shell tinygo env TINYGOROOT)/targets/wasm_exec.js + install -m644 $< $@ + +cmd/chromad/static/chroma.wasm: $(shell git ls-files | grep '\.go|\.xml') + if type tinygo > /dev/null; then \ + tinygo build -no-debug -target wasm -o $@ cmd/libchromawasm/main.go; \ + else \ + GOOS=js GOARCH=wasm go build -o $@ cmd/libchromawasm/main.go; \ + fi upload: build/chromad scp build/chromad root@swapoff.org: && \ diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/README.md b/backend/vendor/github.com/alecthomas/chroma/v2/README.md index 735b5567..b65c3252 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/README.md +++ b/backend/vendor/github.com/alecthomas/chroma/v2/README.md @@ -1,6 +1,9 @@ -# Chroma — A general purpose syntax highlighter in pure Go +![Chroma](chroma.jpg) + +# A general purpose syntax highlighter in pure Go + +[![Go Reference](https://pkg.go.dev/badge/github.com/alecthomas/chroma/v2.svg)](https://pkg.go.dev/github.com/alecthomas/chroma/v2) [![CI](https://github.com/alecthomas/chroma/actions/workflows/ci.yml/badge.svg)](https://github.com/alecthomas/chroma/actions/workflows/ci.yml) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://invite.slack.golangbridge.org/) -[![Golang Documentation](https://godoc.org/github.com/alecthomas/chroma?status.svg)](https://godoc.org/github.com/alecthomas/chroma) [![CI](https://github.com/alecthomas/chroma/actions/workflows/ci.yml/badge.svg)](https://github.com/alecthomas/chroma/actions/workflows/ci.yml) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://invite.slack.golangbridge.org/) Chroma takes source code and other structured text and converts it into syntax highlighted HTML, ANSI-coloured text, etc. @@ -31,33 +34,34 @@ translators for Pygments lexers and styles. ## Supported languages -| Prefix | Language | -| :----: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Agda, AL, Alloy, Angular2, ANTLR, ApacheConf, APL, AppleScript, ArangoDB AQL, Arduino, ArmAsm, AutoHotkey, AutoIt, Awk | -| B | Ballerina, Bash, Bash Session, Batchfile, BibTeX, Bicep, BlitzBasic, BNF, BQN, Brainfuck | -| C | C, C#, C++, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Chapel, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython | -| D | D, Dart, Dax, Desktop Entry, Diff, Django/Jinja, dns, Docker, DTD, Dylan | -| E | EBNF, Elixir, Elm, EmacsLisp, Erlang | -| F | Factor, Fennel, Fish, Forth, Fortran, FortranFixed, FSharp | -| G | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, Gherkin, Gleam, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groff, Groovy | -| H | Handlebars, Hare, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HolyC, HTML, HTTP, Hy | -| I | Idris, Igor, INI, Io, ISCdhcpd | -| J | J, Java, JavaScript, JSON, Jsonnet, Julia, Jungle | -| K | Kotlin | -| L | Lighttpd configuration file, LLVM, Lua | -| M | Makefile, Mako, markdown, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, MiniZinc, MLIR, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL | -| N | NASM, Natural, Newspeak, Nginx configuration file, Nim, Nix, NSIS | -| O | Objective-C, OCaml, Octave, Odin, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode | -| P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Plutus Core, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerQuery, PowerShell, Prolog, PromQL, Promela, properties, Protocol Buffer, PRQL, PSL, Puppet, Python, Python 2 | -| Q | QBasic, QML | -| R | R, Racket, Ragel, Raku, react, ReasonML, reg, Rego, reStructuredText, Rexx, RPMSpec, Ruby, Rust | -| S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Sed, Sieve, Smali, Smalltalk, Smarty, SNBT, Snobol, Solidity, SourcePawn, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog | -| T | TableGen, Tal, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData, Typst | -| V | V, V shell, Vala, VB.net, verilog, VHDL, VHS, VimL, vue | -| W | WDTE, WebGPU Shading Language, Whiley | -| X | XML, Xorg | -| Y | YAML, YANG | -| Z | Z80 Assembly, Zed, Zig | +| Prefix | Language +| :----: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Agda, AL, Alloy, Angular2, ANTLR, ApacheConf, APL, AppleScript, ArangoDB AQL, Arduino, ArmAsm, ATL, AutoHotkey, AutoIt, Awk +| B | Ballerina, Bash, Bash Session, Batchfile, Beef, BibTeX, Bicep, BlitzBasic, BNF, BQN, Brainfuck +| C | C, C#, C++, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Chapel, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Core, Crystal, CSS, CSV, CUE, Cython +| D | D, Dart, Dax, Desktop file, Diff, Django/Jinja, dns, Docker, DTD, Dylan +| E | EBNF, Elixir, Elm, EmacsLisp, Erlang +| F | Factor, Fennel, Fish, Forth, Fortran, FortranFixed, FSharp +| G | GAS, GDScript, GDScript3, Gemtext, Genshi, Genshi HTML, Genshi Text, Gherkin, Gleam, GLSL, Gnuplot, Go, Go HTML Template, Go Template, Go Text Template, GraphQL, Groff, Groovy +| H | Handlebars, Hare, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HolyC, HTML, HTTP, Hy +| I | Idris, Igor, INI, Io, ISCdhcpd +| J | J, Janet, Java, JavaScript, JSON, JSONata, Jsonnet, Julia, Jungle +| K | Kotlin +| L | Lean4, Lighttpd configuration file, LLVM, lox, Lua +| M | Makefile, Mako, markdown, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, MiniZinc, MLIR, Modula-2, Mojo, MonkeyC, MoonScript, MorrowindScript, Myghty, MySQL +| N | NASM, Natural, NDISASM, Newspeak, Nginx configuration file, Nim, Nix, NSIS, Nu +| O | Objective-C, ObjectPascal, OCaml, Octave, Odin, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode +| P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Plutus Core, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerQuery, PowerShell, Prolog, Promela, PromQL, properties, Protocol Buffer, PRQL, PSL, Puppet, Python, Python 2 +| Q | QBasic, QML +| R | R, Racket, Ragel, Raku, react, ReasonML, reg, Rego, reStructuredText, Rexx, RPGLE, RPMSpec, Ruby, Rust +| S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Sed, Sieve, Smali, Smalltalk, Smarty, SNBT, Snobol, Solidity, SourcePawn, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog +| T | TableGen, Tal, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData, Typst +| U | ucode +| V | V, V shell, Vala, VB.net, verilog, VHDL, VHS, VimL, vue +| W | WDTE, WebGPU Shading Language, WebVTT, Whiley +| X | XML, Xorg +| Y | YAML, YANG +| Z | Z80 Assembly, Zed, Zig _I will attempt to keep this section up to date, but an authoritative list can be displayed with `chroma --list`._ @@ -210,8 +214,7 @@ python3 _tools/pygments2chroma_xml.py \ > lexers/embedded/kotlin.xml ``` -See notes in [pygments-lexers.txt](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt) -for a list of lexers, and notes on some of the issues importing them. +A list of all lexers available in Pygments can be found in [pygments-lexers.txt](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt). ### Formatters @@ -267,6 +270,13 @@ on under the hood for easy integration with [lesspipe shipping with Debian and derivatives](https://manpages.debian.org/lesspipe#USER_DEFINED_FILTERS); for that setup the `chroma` executable can be just symlinked to `~/.lessfilter`. +## Projects using Chroma + +* [`moar`](https://github.com/walles/moar) is a full-blown pager that colorizes + its input using Chroma +* [Hugo](https://gohugo.io/) is a static site generator that [uses Chroma for syntax + highlighting code examples](https://gohugo.io/content-management/syntax-highlighting/) + ## Testing lexers If you edit some lexers and want to try it, open a shell in `cmd/chromad` and run: diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/biome.json b/backend/vendor/github.com/alecthomas/chroma/v2/biome.json new file mode 100644 index 00000000..a5bec2e1 --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/biome.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.0.5/schema.json", + "formatter": { + "indentStyle": "space" + } +} diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/chroma.jpg b/backend/vendor/github.com/alecthomas/chroma/v2/chroma.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a747dbf804db96473bffa217fb5aead7a18c3b7f GIT binary patch literal 80950 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

)}55;`dud3IBER-AOu@p^kSE+y`sKT{`CH8iGB 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 - - + + - - + + - - + + - + - - + + - + - - - - - - + + + + + + - + - - - - + + + + - - - - + + + + + - + - - - + + + - + - - + + - + - + - + - + - + - + - - - - - - - - + + + + + + + + - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/applescript.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/applescript.xml index 1de6c67e..5a6224ac 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/applescript.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/applescript.xml @@ -8,123 +8,144 @@ - + - + - + - + - - + + - + - - - + + + - + - - + + - + - - + + - - - - - + + - - - + + + - + - - + + - - + + - - + + - - + + - + - + - + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - - + + - - + + - + - + - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arduino.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arduino.xml index 00399c2c..6a75df5b 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arduino.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arduino.xml @@ -9,301 +9,314 @@ - - + + - - + + - - + + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - + - - - + + + - + - + - - + + - + - + - - + + - - + + - + - - + - + - - - - - - - + + + + + + + - - + + - + - - + + - + - - - - + + + + - + - + - + - + - + - + - + - + - - + + - - + + - + - - + + - + - - + + - - + + - + - + - - + + - + - - - + + + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - + - + - - + + - + - - - - - + + + + + - + - - - - - + + + + + - + - + - + - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/armasm.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/armasm.xml index e5966cf6..340278d3 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/armasm.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/armasm.xml @@ -11,116 +11,116 @@ - + - - + + - - - + + + - + - - + + - + - - + + - + - + - + - - + + - - + + - - + + - - + + - - + + - - - + + + - + - - - + + + - + - - + + - - + + - + - + - + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/core.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/core.xml new file mode 100644 index 00000000..c5aa432c --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/core.xml @@ -0,0 +1,79 @@ + + + Core + core + *.core + text/x-core + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/css.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/css.xml index 6e370c76..5491fe46 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/css.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/css.xml @@ -191,7 +191,7 @@ - + @@ -320,4 +320,4 @@ - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/docker.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/docker.xml index a73c52cd..261834f4 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/docker.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/docker.xml @@ -3,55 +3,66 @@ Docker docker dockerfile + containerfile Dockerfile Dockerfile.* *.Dockerfile *.docker + Containerfile + Containerfile.* + *.Containerfile text/x-dockerfile-config true - + - - + + - + - - + + - - - + + + - - + + - + - - + + + + + + + - + - + - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gleam.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gleam.xml index 6205aa15..39663220 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gleam.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gleam.xml @@ -33,7 +33,6 @@ - @@ -63,12 +62,11 @@ - - - - + + + @@ -76,28 +74,13 @@ - - - - - + + - - - - - - - - - - - - diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/hlb.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/hlb.xml index 64e667dd..9723fd9c 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/hlb.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/hlb.xml @@ -7,143 +7,125 @@ - - - + - - - + - - - + - - - + - - + + - + - - - - + + - + - + - - + + - + - + - - - - + + - - - + - - - + - - - + - - + + - - - + - - - + + + - - - + + + - + - + - + - + - + - + - + - + - + - + - + - + - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/janet.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/janet.xml new file mode 100644 index 00000000..fe139e85 --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/janet.xml @@ -0,0 +1,48 @@ + + + + Janet + janet + *.janet + *.jdn + text/x-janet + application/x-janet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/kotlin.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/kotlin.xml index 09c638a9..ec02c4ef 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/kotlin.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/kotlin.xml @@ -3,6 +3,7 @@ Kotlin kotlin *.kt + *.kts text/x-kotlin true diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lean.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lean.xml new file mode 100644 index 00000000..6ac5151b --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lean.xml @@ -0,0 +1,56 @@ + + + Lean4 + lean4 + lean + *.lean + text/x-lean4 + text/x-lean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lox.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lox.xml new file mode 100644 index 00000000..602885a2 --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lox.xml @@ -0,0 +1,83 @@ + + + lox + *.lox + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lua.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lua.xml index 903d4581..e3d778f1 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lua.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lua.xml @@ -2,8 +2,10 @@ Lua lua + luau *.lua *.wlua + *.luau text/x-lua application/x-lua diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/mojo.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/mojo.xml new file mode 100644 index 00000000..677811eb --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/mojo.xml @@ -0,0 +1,228 @@ + + + Mojo + mojo + 🔥 + *.mojo + *.🔥 + text/x-mojo + application/x-mojo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/moonscript.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/moonscript.xml new file mode 100644 index 00000000..293f538c --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/moonscript.xml @@ -0,0 +1,83 @@ + + + + MoonScript + moonscript + moon + *.moon + text/x-moonscript + application/x-moonscript + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nginx_configuration_file.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nginx_configuration_file.xml index 46bdf573..a80d0499 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nginx_configuration_file.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nginx_configuration_file.xml @@ -58,6 +58,9 @@ + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nu.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nu.xml new file mode 100644 index 00000000..32655808 --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nu.xml @@ -0,0 +1,121 @@ + + + Nu + nu + *.nu + text/plain + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/objectpascal.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/objectpascal.xml index 12af64b9..0b721312 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/objectpascal.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/objectpascal.xml @@ -31,12 +31,9 @@ - - - - - - + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ocaml.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ocaml.xml index 77f67ac9..1770d1d1 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ocaml.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ocaml.xml @@ -41,6 +41,14 @@ + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/odin.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/odin.xml index b9842638..8a529497 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/odin.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/odin.xml @@ -51,6 +51,20 @@ + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/rpgle.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/rpgle.xml new file mode 100644 index 00000000..a74f3c66 --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/rpgle.xml @@ -0,0 +1,176 @@ + + + RPGLE + SQLRPGLE + RPG IV + *.RPGLE + *.rpgle + *.SQLRPGLE + *.sqlrpgle + text/x-rpgle + text/x-sqlrpgle + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/sed.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/sed.xml index 2209aa77..fd77d083 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/sed.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/sed.xml @@ -1,3 +1,4 @@ + Sed @@ -10,19 +11,82 @@ - - - - - - - - - - - None - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/solidity.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/solidity.xml index 04403c84..24c4ccba 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/solidity.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/solidity.xml @@ -157,8 +157,20 @@ - - + + + + + + + + + + + + + + @@ -235,7 +247,7 @@ - + @@ -276,4 +288,4 @@ - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/terraform.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/terraform.xml index 452f211d..f8df6be2 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/terraform.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/terraform.xml @@ -3,138 +3,147 @@ Terraform terraform tf + hcl *.tf + *.hcl application/x-tf application/x-terraform - - - - - + + - - - - - - - - + - + + + + + + + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - + + + - - + + - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/toml.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/toml.xml index 9c98ba53..87bd19df 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/toml.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/toml.xml @@ -5,6 +5,7 @@ *.toml Pipfile poetry.lock + uv.lock text/x-toml diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/typescript.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/typescript.xml index 7d541cb6..a3e3be23 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/typescript.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/typescript.xml @@ -15,280 +15,287 @@ - - + + - - + + - + - - - + + + - - + + - + - - - - + + + + - + - + - + - + - - + + - + - + - - - + + + - + - + - + - - - + + + - + - + - - + + - + - + - - + + - + - + - - + + - - + + - + - + - - + + - + - + - - + + - - + + - - + + - + - - + + - + - + - - + + - - + + - + - + - - + + - + - - + + - - + + - + - - - + + + - - + + - - + + - + - - + + - - - - + + + + - + - + - + - - + + + - + - - + + - - - + + + - + - + - + - + - + - + - - + + - + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/vue.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/vue.xml index ec350835..e2f75e1d 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/vue.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/vue.xml @@ -64,25 +64,13 @@ - + - - - - - - - - - - - - @@ -96,16 +84,7 @@ - - - - - - - - - - + @@ -113,19 +92,28 @@ - + - + + + + + + + + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/zig.xml b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/zig.xml index fb51cc1a..6f17bcaf 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/zig.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/zig.xml @@ -3,6 +3,7 @@ Zig zig *.zig + *.zon text/zig @@ -76,6 +77,9 @@ + + + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/gemtext.go b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/gemtext.go new file mode 100644 index 00000000..2fed8d62 --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/gemtext.go @@ -0,0 +1,37 @@ +package lexers + +import ( + . "github.com/alecthomas/chroma/v2" // nolint +) + +// Gemtext lexer. +var Gemtext = Register(MustNewLexer( + &Config{ + Name: "Gemtext", + Aliases: []string{"gemtext", "gmi", "gmni", "gemini"}, + Filenames: []string{"*.gmi", "*.gmni", "*.gemini"}, + MimeTypes: []string{"text/gemini"}, + }, + gemtextRules, +)) + +func gemtextRules() Rules { + return Rules{ + "root": { + {`^(#[^#].+\r?\n)`, ByGroups(GenericHeading), nil}, + {`^(#{2,3}.+\r?\n)`, ByGroups(GenericSubheading), nil}, + {`^(\* )(.+\r?\n)`, ByGroups(Keyword, Text), nil}, + {`^(>)(.+\r?\n)`, ByGroups(Keyword, GenericEmph), nil}, + {"^(```\\r?\\n)([\\w\\W]*?)(^```)(.+\\r?\\n)?", ByGroups(String, Text, String, Comment), nil}, + { + "^(```)(\\w+)(\\r?\\n)([\\w\\W]*?)(^```)(.+\\r?\\n)?", + UsingByGroup(2, 4, String, String, String, Text, String, Comment), + nil, + }, + {"^(```)(.+\\r?\\n)([\\w\\W]*?)(^```)(.+\\r?\\n)?", ByGroups(String, String, Text, String, Comment), nil}, + {`^(=>)(\s*)([^\s]+)(\s*)$`, ByGroups(Keyword, Text, NameAttribute, Text), nil}, + {`^(=>)(\s*)([^\s]+)(\s+)(.+)$`, ByGroups(Keyword, Text, NameAttribute, Text, NameTag), nil}, + {`(.|(?:\r?\n))`, ByGroups(Text), nil}, + }, + } +} diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/go.go b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/go.go index 60b1f711..c6123b90 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/go.go +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/go.go @@ -28,10 +28,10 @@ var Go = Register(MustNewLexer( func goRules() Rules { return Rules{ "root": { - {`\n`, Text, nil}, - {`\s+`, Text, nil}, - {`\\\n`, Text, nil}, - {`//[^\n\r]*`, CommentSingle, nil}, + {`\n`, TextWhitespace, nil}, + {`\s+`, TextWhitespace, nil}, + {`//[^\s][^\n\r]*`, CommentPreproc, nil}, + {`//\s+[^\n\r]*`, CommentSingle, nil}, {`/(\\\n)?[*](.|\n)*?[*](\\\n)?/`, CommentMultiline, nil}, {`(import|package)\b`, KeywordNamespace, nil}, {`(var|func|struct|map|chan|type|interface|const)\b`, KeywordDeclaration, nil}, diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/lexers.go b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/lexers.go index 4fa35ad2..bef42ede 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/lexers.go +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/lexers.go @@ -29,6 +29,12 @@ func Names(withAliases bool) []string { return GlobalLexerRegistry.Names(withAliases) } +// Aliases of all the lexers, and skip those lexers who do not have any aliases, +// or show their name instead +func Aliases(skipWithoutAliases bool) []string { + return GlobalLexerRegistry.Aliases(skipWithoutAliases) +} + // Get a Lexer by name, alias or file extension. // // Note that this if there isn't an exact match on name or alias, this will diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/markdown.go b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/markdown.go index db5a8948..bcd5b178 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/markdown.go +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/markdown.go @@ -5,7 +5,7 @@ import ( ) // Markdown lexer. -var Markdown = Register(DelegatingLexer(HTML, MustNewLexer( +var Markdown = Register(MustNewLexer( &Config{ Name: "markdown", Aliases: []string{"md", "mkd"}, @@ -13,7 +13,7 @@ var Markdown = Register(DelegatingLexer(HTML, MustNewLexer( MimeTypes: []string{"text/x-markdown"}, }, markdownRules, -))) +)) func markdownRules() Rules { return Rules{ @@ -40,7 +40,7 @@ func markdownRules() Rules { {"`[^`]+`", LiteralStringBacktick, nil}, {`[@#][\w/:]+`, NameEntity, nil}, {`(!?\[)([^]]+)(\])(\()([^)]+)(\))`, ByGroups(Text, NameTag, Text, Text, NameAttribute, Text), nil}, - {`.|\n`, Other, nil}, + {`.|\n`, Text, nil}, }, } } diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/raku.go b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/raku.go index 8085bc7e..da354dce 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/lexers/raku.go +++ b/backend/vendor/github.com/alecthomas/chroma/v2/lexers/raku.go @@ -2,6 +2,7 @@ package lexers import ( "regexp" + "slices" "strings" "unicode/utf8" @@ -1318,21 +1319,11 @@ func indexAt(str []rune, substr []rune, pos int) int { return idx } -// Tells if an array of string contains a string -func contains(s []string, e string) bool { - for _, value := range s { - if value == e { - return true - } - } - return false -} - type rulePosition int const ( - topRule rulePosition = 0 - bottomRule = -1 + topRule rulePosition = 0 - iota + bottomRule ) type ruleMakingConfig struct { @@ -1625,15 +1616,15 @@ func quote(groups []string, state *LexerState) Iterator { var tokenState string switch { - case keyword == "qq" || contains(tokenStates, "qq"): + case keyword == "qq" || slices.Contains(tokenStates, "qq"): tokenState = "qq" - case adverbsStr == "ww" || contains(tokenStates, "ww"): + case adverbsStr == "ww" || slices.Contains(tokenStates, "ww"): tokenState = "ww" - case contains(tokenStates, "Q-closure") && contains(tokenStates, "Q-variable"): + case slices.Contains(tokenStates, "Q-closure") && slices.Contains(tokenStates, "Q-variable"): tokenState = "qq" - case contains(tokenStates, "Q-closure"): + case slices.Contains(tokenStates, "Q-closure"): tokenState = "Q-closure" - case contains(tokenStates, "Q-variable"): + case slices.Contains(tokenStates, "Q-variable"): tokenState = "Q-variable" default: tokenState = "Q" diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/pygments-lexers.txt b/backend/vendor/github.com/alecthomas/chroma/v2/pygments-lexers.txt index bf4d9de1..27ab6ae8 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/pygments-lexers.txt +++ b/backend/vendor/github.com/alecthomas/chroma/v2/pygments-lexers.txt @@ -1,322 +1,605 @@ Generated with: - g 'class.*RegexLexer' | pawk --strict -F: '"pygments.lexers.%s.%s" % (f[0].split(".")[0], f[2].split()[1].split("(")[0])' > lexers.txt + import pygments.lexers # v2.19.1 + + for x in pygments.lexers.get_all_lexers(plugins=False): + if len(x[1]): + lexer = pygments.lexers.get_lexer_by_name(x[1][0]) + print(f"{lexer.__module__}.{lexer.__class__.__name__}") -kotlin: - invalid unicode escape sequences - FIXED: Have to disable wide Unicode characters in unistring.py -pygments.lexers.ambient.AmbientTalkLexer -pygments.lexers.ampl.AmplLexer -pygments.lexers.actionscript.ActionScriptLexer pygments.lexers.actionscript.ActionScript3Lexer +pygments.lexers.actionscript.ActionScriptLexer pygments.lexers.actionscript.MxmlLexer +pygments.lexers.ada.AdaLexer +pygments.lexers.algebra.BCLexer +pygments.lexers.algebra.GAPConsoleLexer pygments.lexers.algebra.GAPLexer pygments.lexers.algebra.MathematicaLexer pygments.lexers.algebra.MuPADLexer -pygments.lexers.algebra.BCLexer +pygments.lexers.ambient.AmbientTalkLexer +pygments.lexers.amdgpu.AMDGPULexer +pygments.lexers.ampl.AmplLexer +pygments.lexers.apdlexer.apdlexer pygments.lexers.apl.APLLexer -pygments.lexers.bibtex.BibTeXLexer -pygments.lexers.bibtex.BSTLexer -pygments.lexers.basic.BlitzMaxLexer -pygments.lexers.basic.BlitzBasicLexer -pygments.lexers.basic.MonkeyLexer -pygments.lexers.basic.CbmBasicV2Lexer -pygments.lexers.basic.QBasicLexer -pygments.lexers.automation.AutohotkeyLexer -pygments.lexers.automation.AutoItLexer -pygments.lexers.archetype.AtomsLexer -pygments.lexers.c_like.ClayLexer -pygments.lexers.c_like.ValaLexer +pygments.lexers.archetype.AdlLexer +pygments.lexers.archetype.CadlLexer +pygments.lexers.archetype.OdinLexer +pygments.lexers.arrow.ArrowLexer +pygments.lexers.arturo.ArturoLexer +pygments.lexers.asc.AscLexer +pygments.lexers.asm.Ca65Lexer +pygments.lexers.asm.CObjdumpLexer +pygments.lexers.asm.CppObjdumpLexer +pygments.lexers.asm.Dasm16Lexer +pygments.lexers.asm.DObjdumpLexer pygments.lexers.asm.GasLexer -pygments.lexers.asm.ObjdumpLexer pygments.lexers.asm.HsailLexer pygments.lexers.asm.LlvmLexer +pygments.lexers.asm.LlvmMirBodyLexer +pygments.lexers.asm.LlvmMirLexer pygments.lexers.asm.NasmLexer +pygments.lexers.asm.NasmObjdumpLexer +pygments.lexers.asm.ObjdumpLexer pygments.lexers.asm.TasmLexer -pygments.lexers.asm.Ca65Lexer -pygments.lexers.business.CobolLexer +pygments.lexers.asn1.Asn1Lexer +pygments.lexers.automation.AutohotkeyLexer +pygments.lexers.automation.AutoItLexer +pygments.lexers.bare.BareLexer +pygments.lexers.basic.BBCBasicLexer +pygments.lexers.basic.BlitzBasicLexer +pygments.lexers.basic.BlitzMaxLexer +pygments.lexers.basic.CbmBasicV2Lexer +pygments.lexers.basic.MonkeyLexer +pygments.lexers.basic.QBasicLexer +pygments.lexers.basic.VBScriptLexer +pygments.lexers.bdd.BddLexer +pygments.lexers.berry.BerryLexer +pygments.lexers.bibtex.BibTeXLexer +pygments.lexers.bibtex.BSTLexer +pygments.lexers.blueprint.BlueprintLexer +pygments.lexers.boa.BoaLexer +pygments.lexers.bqn.BQNLexer pygments.lexers.business.ABAPLexer -pygments.lexers.business.OpenEdgeLexer +pygments.lexers.business.CobolFreeformatLexer +pygments.lexers.business.CobolLexer pygments.lexers.business.GoodDataCLLexer pygments.lexers.business.MaqlLexer +pygments.lexers.business.OpenEdgeLexer +pygments.lexers.c_cpp.CLexer +pygments.lexers.c_cpp.CppLexer +pygments.lexers.c_like.ArduinoLexer +pygments.lexers.c_like.CharmciLexer +pygments.lexers.c_like.ClayLexer +pygments.lexers.c_like.CudaLexer +pygments.lexers.c_like.ECLexer +pygments.lexers.c_like.MqlLexer +pygments.lexers.c_like.NesCLexer +pygments.lexers.c_like.OmgIdlLexer +pygments.lexers.c_like.PikeLexer +pygments.lexers.c_like.PromelaLexer +pygments.lexers.c_like.SwigLexer +pygments.lexers.c_like.ValaLexer pygments.lexers.capnproto.CapnProtoLexer +pygments.lexers.carbon.CarbonLexer +pygments.lexers.cddl.CddlLexer pygments.lexers.chapel.ChapelLexer pygments.lexers.clean.CleanLexer -pygments.lexers.c_cpp.CFamilyLexer -pygments.lexers.console.VCTreeStatusLexer -pygments.lexers.console.PyPyLogLexer -pygments.lexers.csound.CsoundLexer -pygments.lexers.csound.CsoundDocumentLexer -pygments.lexers.csound.CsoundDocumentLexer -pygments.lexers.crystal.CrystalLexer -pygments.lexers.dalvik.SmaliLexer -pygments.lexers.css.CssLexer -pygments.lexers.css.SassLexer -pygments.lexers.css.ScssLexer -pygments.lexers.configs.IniLexer -pygments.lexers.configs.RegeditLexer -pygments.lexers.configs.PropertiesLexer -pygments.lexers.configs.KconfigLexer -pygments.lexers.configs.Cfengine3Lexer +pygments.lexers.codeql.CodeQLLexer +pygments.lexers.comal.Comal80Lexer pygments.lexers.configs.ApacheConfLexer -pygments.lexers.configs.SquidConfLexer -pygments.lexers.configs.NginxConfLexer -pygments.lexers.configs.LighttpdConfLexer +pygments.lexers.configs.AugeasLexer +pygments.lexers.configs.Cfengine3Lexer +pygments.lexers.configs.DesktopLexer pygments.lexers.configs.DockerLexer -pygments.lexers.configs.TerraformLexer +pygments.lexers.configs.IniLexer +pygments.lexers.configs.KconfigLexer +pygments.lexers.configs.LighttpdConfLexer +pygments.lexers.configs.NestedTextLexer +pygments.lexers.configs.NginxConfLexer +pygments.lexers.configs.PacmanConfLexer +pygments.lexers.configs.PkgConfigLexer +pygments.lexers.configs.PropertiesLexer +pygments.lexers.configs.RegeditLexer +pygments.lexers.configs.SingularityLexer +pygments.lexers.configs.SquidConfLexer +pygments.lexers.configs.SystemdLexer pygments.lexers.configs.TermcapLexer pygments.lexers.configs.TerminfoLexer -pygments.lexers.configs.PkgConfigLexer -pygments.lexers.configs.PacmanConfLexer -pygments.lexers.data.YamlLexer +pygments.lexers.configs.TerraformLexer +pygments.lexers.configs.TOMLLexer +pygments.lexers.configs.UnixConfigLexer +pygments.lexers.console.PyPyLogLexer +pygments.lexers.console.VCTreeStatusLexer +pygments.lexers.cplint.CplintLexer +pygments.lexers.crystal.CrystalLexer +pygments.lexers.csound.CsoundDocumentLexer +pygments.lexers.csound.CsoundOrchestraLexer +pygments.lexers.csound.CsoundScoreLexer +pygments.lexers.css.CssLexer +pygments.lexers.css.LessCssLexer +pygments.lexers.css.SassLexer +pygments.lexers.css.ScssLexer +pygments.lexers.d.CrocLexer +pygments.lexers.d.DLexer +pygments.lexers.d.MiniDLexer +pygments.lexers.dalvik.SmaliLexer +pygments.lexers.data.JsonLdLexer pygments.lexers.data.JsonLexer -pygments.lexers.diff.DiffLexer +pygments.lexers.data.YamlLexer +pygments.lexers.dax.DaxLexer +pygments.lexers.devicetree.DevicetreeLexer pygments.lexers.diff.DarcsPatchLexer +pygments.lexers.diff.DiffLexer pygments.lexers.diff.WDiffLexer -pygments.lexers.dotnet.CSharpLexer -pygments.lexers.dotnet.NemerleLexer +pygments.lexers.dns.DnsZoneLexer pygments.lexers.dotnet.BooLexer -pygments.lexers.dotnet.VbNetLexer -pygments.lexers.dotnet.GenericAspxLexer +pygments.lexers.dotnet.CSharpAspxLexer +pygments.lexers.dotnet.CSharpLexer pygments.lexers.dotnet.FSharpLexer +pygments.lexers.dotnet.NemerleLexer +pygments.lexers.dotnet.VbNetAspxLexer +pygments.lexers.dotnet.VbNetLexer +pygments.lexers.dotnet.XppLexer +pygments.lexers.dsls.AlloyLexer +pygments.lexers.dsls.CrmshLexer +pygments.lexers.dsls.FlatlineLexer +pygments.lexers.dsls.MscgenLexer +pygments.lexers.dsls.PanLexer +pygments.lexers.dsls.ProtoBufLexer +pygments.lexers.dsls.PuppetLexer +pygments.lexers.dsls.RslLexer +pygments.lexers.dsls.SnowballLexer +pygments.lexers.dsls.ThriftLexer +pygments.lexers.dsls.VGLLexer +pygments.lexers.dsls.ZeekLexer +pygments.lexers.dylan.DylanConsoleLexer pygments.lexers.dylan.DylanLexer pygments.lexers.dylan.DylanLidLexer pygments.lexers.ecl.ECLLexer pygments.lexers.eiffel.EiffelLexer -pygments.lexers.dsls.ProtoBufLexer -pygments.lexers.dsls.ThriftLexer -pygments.lexers.dsls.BroLexer -pygments.lexers.dsls.PuppetLexer -pygments.lexers.dsls.RslLexer -pygments.lexers.dsls.MscgenLexer -pygments.lexers.dsls.VGLLexer -pygments.lexers.dsls.AlloyLexer -pygments.lexers.dsls.PanLexer -pygments.lexers.dsls.CrmshLexer -pygments.lexers.dsls.FlatlineLexer -pygments.lexers.dsls.SnowballLexer pygments.lexers.elm.ElmLexer -pygments.lexers.erlang.ErlangLexer +pygments.lexers.elpi.ElpiLexer +pygments.lexers.email.EmailLexer +pygments.lexers.erlang.ElixirConsoleLexer pygments.lexers.erlang.ElixirLexer -pygments.lexers.ezhil.EzhilLexer -pygments.lexers.esoteric.BrainfuckLexer +pygments.lexers.erlang.ErlangLexer +pygments.lexers.erlang.ErlangShellLexer +pygments.lexers.esoteric.AheuiLexer pygments.lexers.esoteric.BefungeLexer +pygments.lexers.esoteric.BrainfuckLexer pygments.lexers.esoteric.CAmkESLexer pygments.lexers.esoteric.CapDLLexer pygments.lexers.esoteric.RedcodeLexer -pygments.lexers.esoteric.AheuiLexer +pygments.lexers.ezhil.EzhilLexer pygments.lexers.factor.FactorLexer pygments.lexers.fantom.FantomLexer pygments.lexers.felix.FelixLexer +pygments.lexers.fift.FiftLexer +pygments.lexers.floscript.FloScriptLexer pygments.lexers.forth.ForthLexer -pygments.lexers.fortran.FortranLexer pygments.lexers.fortran.FortranFixedLexer -pygments.lexers.go.GoLexer +pygments.lexers.fortran.FortranLexer pygments.lexers.foxpro.FoxProLexer -pygments.lexers.graph.CypherLexer -pygments.lexers.grammar_notation.BnfLexer +pygments.lexers.freefem.FreeFemLexer +pygments.lexers.func.FuncLexer +pygments.lexers.futhark.FutharkLexer +pygments.lexers.gcodelexer.GcodeLexer +pygments.lexers.gdscript.GDScriptLexer +pygments.lexers.gleam.GleamLexer +pygments.lexers.go.GoLexer pygments.lexers.grammar_notation.AbnfLexer +pygments.lexers.grammar_notation.BnfLexer pygments.lexers.grammar_notation.JsgfLexer -pygments.lexers.graphics.GLShaderLexer -pygments.lexers.graphics.PostScriptLexer +pygments.lexers.grammar_notation.PegLexer +pygments.lexers.graph.CypherLexer pygments.lexers.graphics.AsymptoteLexer +pygments.lexers.graphics.GLShaderLexer pygments.lexers.graphics.GnuplotLexer +pygments.lexers.graphics.HLSLShaderLexer +pygments.lexers.graphics.PostScriptLexer pygments.lexers.graphics.PovrayLexer -pygments.lexers.hexdump.HexdumpLexer -pygments.lexers.haskell.HaskellLexer -pygments.lexers.haskell.IdrisLexer +pygments.lexers.graphql.GraphQLLexer +pygments.lexers.graphviz.GraphvizLexer +pygments.lexers.gsql.GSQLLexer +pygments.lexers.hare.HareLexer pygments.lexers.haskell.AgdaLexer pygments.lexers.haskell.CryptolLexer +pygments.lexers.haskell.HaskellLexer +pygments.lexers.haskell.HspecLexer +pygments.lexers.haskell.IdrisLexer pygments.lexers.haskell.KokaLexer +pygments.lexers.haskell.LiterateAgdaLexer +pygments.lexers.haskell.LiterateCryptolLexer +pygments.lexers.haskell.LiterateHaskellLexer +pygments.lexers.haskell.LiterateIdrisLexer pygments.lexers.haxe.HaxeLexer pygments.lexers.haxe.HxmlLexer -pygments.lexers.hdl.VerilogLexer pygments.lexers.hdl.SystemVerilogLexer +pygments.lexers.hdl.VerilogLexer pygments.lexers.hdl.VhdlLexer -pygments.lexers.idl.IDLLexer -pygments.lexers.inferno.LimboLexer -pygments.lexers.igor.IgorLexer -pygments.lexers.html.HtmlLexer +pygments.lexers.hexdump.HexdumpLexer pygments.lexers.html.DtdLexer -pygments.lexers.html.XmlLexer pygments.lexers.html.HamlLexer -pygments.lexers.html.ScamlLexer +pygments.lexers.html.HtmlLexer pygments.lexers.html.PugLexer +pygments.lexers.html.ScamlLexer +pygments.lexers.html.UrlEncodedLexer +pygments.lexers.html.VueLexer +pygments.lexers.html.XmlLexer +pygments.lexers.html.XsltLexer +pygments.lexers.idl.IDLLexer +pygments.lexers.igor.IgorLexer +pygments.lexers.inferno.LimboLexer +pygments.lexers.installers.DebianControlLexer +pygments.lexers.installers.DebianSourcesLexer pygments.lexers.installers.NSISLexer pygments.lexers.installers.RPMSpecLexer pygments.lexers.installers.SourcesListLexer -pygments.lexers.installers.DebianControlLexer -pygments.lexers.iolang.IoLexer -pygments.lexers.julia.JuliaLexer pygments.lexers.int_fiction.Inform6Lexer +pygments.lexers.int_fiction.Inform6TemplateLexer pygments.lexers.int_fiction.Inform7Lexer pygments.lexers.int_fiction.Tads3Lexer -pygments.lexers.make.BaseMakefileLexer -pygments.lexers.make.CMakeLexer -pygments.lexers.javascript.JavascriptLexer -pygments.lexers.javascript.KalLexer -pygments.lexers.javascript.LiveScriptLexer -pygments.lexers.javascript.DartLexer -pygments.lexers.javascript.TypeScriptLexer -pygments.lexers.javascript.LassoLexer -pygments.lexers.javascript.ObjectiveJLexer +pygments.lexers.iolang.IoLexer +pygments.lexers.j.JLexer pygments.lexers.javascript.CoffeeScriptLexer -pygments.lexers.javascript.MaskLexer +pygments.lexers.javascript.DartLexer pygments.lexers.javascript.EarlGreyLexer +pygments.lexers.javascript.JavascriptLexer pygments.lexers.javascript.JuttleLexer -pygments.lexers.jvm.JavaLexer -pygments.lexers.jvm.ScalaLexer +pygments.lexers.javascript.KalLexer +pygments.lexers.javascript.LassoLexer +pygments.lexers.javascript.LiveScriptLexer +pygments.lexers.javascript.MaskLexer +pygments.lexers.javascript.NodeConsoleLexer +pygments.lexers.javascript.ObjectiveJLexer +pygments.lexers.javascript.TypeScriptLexer +pygments.lexers.jmespath.JMESPathLexer +pygments.lexers.jslt.JSLTLexer +pygments.lexers.json5.Json5Lexer +pygments.lexers.jsonnet.JsonnetLexer +pygments.lexers.jsx.JsxLexer +pygments.lexers.jsx.TsxLexer +pygments.lexers.julia.JuliaConsoleLexer +pygments.lexers.julia.JuliaLexer +pygments.lexers.jvm.AspectJLexer +pygments.lexers.jvm.CeylonLexer +pygments.lexers.jvm.ClojureLexer +pygments.lexers.jvm.ClojureScriptLexer +pygments.lexers.jvm.GoloLexer pygments.lexers.jvm.GosuLexer +pygments.lexers.jvm.GosuTemplateLexer pygments.lexers.jvm.GroovyLexer pygments.lexers.jvm.IokeLexer -pygments.lexers.jvm.ClojureLexer -pygments.lexers.jvm.TeaLangLexer -pygments.lexers.jvm.CeylonLexer -pygments.lexers.jvm.KotlinLexer -pygments.lexers.jvm.XtendLexer -pygments.lexers.jvm.PigLexer -pygments.lexers.jvm.GoloLexer pygments.lexers.jvm.JasminLexer -pygments.lexers.markup.BBCodeLexer -pygments.lexers.markup.MoinWikiLexer -pygments.lexers.markup.RstLexer -pygments.lexers.markup.TexLexer -pygments.lexers.markup.GroffLexer -pygments.lexers.markup.MozPreprocHashLexer -pygments.lexers.markup.MarkdownLexer -pygments.lexers.ml.SMLLexer -pygments.lexers.ml.OcamlLexer -pygments.lexers.ml.OpaLexer -pygments.lexers.modeling.ModelicaLexer -pygments.lexers.modeling.BugsLexer -pygments.lexers.modeling.JagsLexer -pygments.lexers.modeling.StanLexer -pygments.lexers.matlab.MatlabLexer -pygments.lexers.matlab.OctaveLexer -pygments.lexers.matlab.ScilabLexer -pygments.lexers.monte.MonteLexer -pygments.lexers.lisp.SchemeLexer +pygments.lexers.jvm.JavaLexer +pygments.lexers.jvm.KotlinLexer +pygments.lexers.jvm.PigLexer +pygments.lexers.jvm.SarlLexer +pygments.lexers.jvm.ScalaLexer +pygments.lexers.jvm.XtendLexer +pygments.lexers.kuin.KuinLexer +pygments.lexers.kusto.KustoLexer +pygments.lexers.ldap.LdaprcLexer +pygments.lexers.ldap.LdifLexer +pygments.lexers.lean.Lean3Lexer +pygments.lexers.lean.Lean4Lexer +pygments.lexers.lilypond.LilyPondLexer pygments.lexers.lisp.CommonLispLexer -pygments.lexers.lisp.HyLexer -pygments.lexers.lisp.RacketLexer -pygments.lexers.lisp.NewLispLexer +pygments.lexers.lisp.CPSALexer pygments.lexers.lisp.EmacsLispLexer +pygments.lexers.lisp.FennelLexer +pygments.lexers.lisp.HyLexer +pygments.lexers.lisp.JanetLexer +pygments.lexers.lisp.NewLispLexer +pygments.lexers.lisp.RacketLexer +pygments.lexers.lisp.SchemeLexer pygments.lexers.lisp.ShenLexer pygments.lexers.lisp.XtlangLexer +pygments.lexers.macaulay2.Macaulay2Lexer +pygments.lexers.make.BaseMakefileLexer +pygments.lexers.make.CMakeLexer +pygments.lexers.make.MakefileLexer +pygments.lexers.maple.MapleLexer +pygments.lexers.markup.BBCodeLexer +pygments.lexers.markup.GroffLexer +pygments.lexers.markup.MarkdownLexer +pygments.lexers.markup.MoinWikiLexer +pygments.lexers.markup.MozPreprocCssLexer +pygments.lexers.markup.MozPreprocHashLexer +pygments.lexers.markup.MozPreprocJavascriptLexer +pygments.lexers.markup.MozPreprocPercentLexer +pygments.lexers.markup.MozPreprocXulLexer +pygments.lexers.markup.OrgLexer +pygments.lexers.markup.RstLexer +pygments.lexers.markup.TexLexer +pygments.lexers.markup.TiddlyWiki5Lexer +pygments.lexers.markup.WikitextLexer +pygments.lexers.matlab.MatlabLexer +pygments.lexers.matlab.MatlabSessionLexer +pygments.lexers.matlab.OctaveLexer +pygments.lexers.matlab.ScilabLexer +pygments.lexers.maxima.MaximaLexer +pygments.lexers.meson.MesonLexer +pygments.lexers.mime.MIMELexer +pygments.lexers.minecraft.MCFunctionLexer +pygments.lexers.minecraft.MCSchemaLexer +pygments.lexers.minecraft.SNBTLexer +pygments.lexers.mips.MIPSLexer +pygments.lexers.ml.FStarLexer +pygments.lexers.ml.OcamlLexer +pygments.lexers.ml.OpaLexer +pygments.lexers.ml.ReasonLexer +pygments.lexers.ml.SMLLexer +pygments.lexers.modeling.BugsLexer +pygments.lexers.modeling.JagsLexer +pygments.lexers.modeling.ModelicaLexer +pygments.lexers.modeling.StanLexer pygments.lexers.modula2.Modula2Lexer +pygments.lexers.mojo.MojoLexer +pygments.lexers.monte.MonteLexer +pygments.lexers.mosel.MoselLexer pygments.lexers.ncl.NCLLexer -pygments.lexers.nim.NimLexer +pygments.lexers.nimrod.NimrodLexer pygments.lexers.nit.NitLexer pygments.lexers.nix.NixLexer +pygments.lexers.numbair.NumbaIRLexer pygments.lexers.oberon.ComponentPascalLexer -pygments.lexers.ooc.OocLexer +pygments.lexers.objective.LogosLexer +pygments.lexers.objective.ObjectiveCLexer +pygments.lexers.objective.ObjectiveCppLexer pygments.lexers.objective.SwiftLexer +pygments.lexers.ooc.OocLexer +pygments.lexers.openscad.OpenScadLexer pygments.lexers.parasail.ParaSailLexer -pygments.lexers.pawn.SourcePawnLexer -pygments.lexers.pawn.PawnLexer -pygments.lexers.pascal.AdaLexer -pygments.lexers.parsers.RagelLexer -pygments.lexers.parsers.RagelEmbeddedLexer +pygments.lexers.parsers.AntlrActionScriptLexer +pygments.lexers.parsers.AntlrCppLexer +pygments.lexers.parsers.AntlrCSharpLexer +pygments.lexers.parsers.AntlrJavaLexer pygments.lexers.parsers.AntlrLexer -pygments.lexers.parsers.TreetopBaseLexer +pygments.lexers.parsers.AntlrObjectiveCLexer +pygments.lexers.parsers.AntlrPerlLexer +pygments.lexers.parsers.AntlrPythonLexer +pygments.lexers.parsers.AntlrRubyLexer pygments.lexers.parsers.EbnfLexer -pygments.lexers.php.ZephirLexer -pygments.lexers.php.PhpLexer -pygments.lexers.perl.PerlLexer +pygments.lexers.parsers.RagelCLexer +pygments.lexers.parsers.RagelCppLexer +pygments.lexers.parsers.RagelDLexer +pygments.lexers.parsers.RagelEmbeddedLexer +pygments.lexers.parsers.RagelJavaLexer +pygments.lexers.parsers.RagelLexer +pygments.lexers.parsers.RagelObjectiveCLexer +pygments.lexers.parsers.RagelRubyLexer +pygments.lexers.parsers.TreetopLexer +pygments.lexers.pascal.DelphiLexer +pygments.lexers.pascal.PortugolLexer +pygments.lexers.pawn.PawnLexer +pygments.lexers.pawn.SourcePawnLexer +pygments.lexers.pddl.PddlLexer pygments.lexers.perl.Perl6Lexer +pygments.lexers.perl.PerlLexer +pygments.lexers.phix.PhixLexer +pygments.lexers.php.PhpLexer +pygments.lexers.php.PsyshConsoleLexer +pygments.lexers.php.ZephirLexer +pygments.lexers.pointless.PointlessLexer +pygments.lexers.pony.PonyLexer pygments.lexers.praat.PraatLexer -pygments.lexers.prolog.PrologLexer +pygments.lexers.procfile.ProcfileLexer pygments.lexers.prolog.LogtalkLexer -pygments.lexers.qvt.QVToLexer -pygments.lexers.rdf.SparqlLexer -pygments.lexers.rdf.TurtleLexer -pygments.lexers.python.PythonLexer -pygments.lexers.python.Python3Lexer -pygments.lexers.python.PythonTracebackLexer -pygments.lexers.python.Python3TracebackLexer +pygments.lexers.prolog.PrologLexer +pygments.lexers.promql.PromQLLexer +pygments.lexers.prql.PrqlLexer +pygments.lexers.ptx.PtxLexer pygments.lexers.python.CythonLexer pygments.lexers.python.DgLexer +pygments.lexers.python.NumPyLexer +pygments.lexers.python.Python2Lexer +pygments.lexers.python.Python2TracebackLexer +pygments.lexers.python.PythonConsoleLexer +pygments.lexers.python.PythonLexer +pygments.lexers.python.PythonTracebackLexer +pygments.lexers.q.KLexer +pygments.lexers.q.QLexer +pygments.lexers.qlik.QlikLexer +pygments.lexers.qvt.QVToLexer +pygments.lexers.r.RConsoleLexer +pygments.lexers.r.RdLexer +pygments.lexers.r.SLexer +pygments.lexers.rdf.ShExCLexer +pygments.lexers.rdf.SparqlLexer +pygments.lexers.rdf.TurtleLexer pygments.lexers.rebol.RebolLexer pygments.lexers.rebol.RedLexer +pygments.lexers.rego.RegoLexer pygments.lexers.resource.ResourceLexer +pygments.lexers.ride.RideLexer +pygments.lexers.rita.RitaLexer pygments.lexers.rnc.RNCCompactLexer pygments.lexers.roboconf.RoboconfGraphLexer pygments.lexers.roboconf.RoboconfInstancesLexer -pygments.lexers.rust.RustLexer -pygments.lexers.ruby.RubyLexer +pygments.lexers.robotframework.RobotFrameworkLexer pygments.lexers.ruby.FancyLexer +pygments.lexers.ruby.RubyConsoleLexer +pygments.lexers.ruby.RubyLexer +pygments.lexers.rust.RustLexer pygments.lexers.sas.SASLexer -pygments.lexers.smalltalk.SmalltalkLexer -pygments.lexers.smalltalk.NewspeakLexer -pygments.lexers.smv.NuSMVLexer -pygments.lexers.shell.BashLexer -pygments.lexers.shell.BatchLexer -pygments.lexers.shell.TcshLexer -pygments.lexers.shell.PowerShellLexer -pygments.lexers.shell.FishShellLexer -pygments.lexers.snobol.SnobolLexer -pygments.lexers.scripting.LuaLexer -pygments.lexers.scripting.ChaiscriptLexer -pygments.lexers.scripting.LSLLexer +pygments.lexers.savi.SaviLexer +pygments.lexers.scdoc.ScdocLexer pygments.lexers.scripting.AppleScriptLexer -pygments.lexers.scripting.RexxLexer -pygments.lexers.scripting.MOOCodeLexer -pygments.lexers.scripting.HybrisLexer +pygments.lexers.scripting.ChaiscriptLexer pygments.lexers.scripting.EasytrieveLexer +pygments.lexers.scripting.HybrisLexer pygments.lexers.scripting.JclLexer -pygments.lexers.supercollider.SuperColliderLexer -pygments.lexers.stata.StataLexer -pygments.lexers.tcl.TclLexer -pygments.lexers.sql.PostgresLexer +pygments.lexers.scripting.LSLLexer +pygments.lexers.scripting.LuaLexer +pygments.lexers.scripting.LuauLexer +pygments.lexers.scripting.MiniScriptLexer +pygments.lexers.scripting.MOOCodeLexer +pygments.lexers.scripting.MoonScriptLexer +pygments.lexers.scripting.RexxLexer +pygments.lexers.sgf.SmartGameFormatLexer +pygments.lexers.shell.BashLexer +pygments.lexers.shell.BashSessionLexer +pygments.lexers.shell.BatchLexer +pygments.lexers.shell.ExeclineLexer +pygments.lexers.shell.FishShellLexer +pygments.lexers.shell.MSDOSSessionLexer +pygments.lexers.shell.PowerShellLexer +pygments.lexers.shell.PowerShellSessionLexer +pygments.lexers.shell.SlurmBashLexer +pygments.lexers.shell.TcshLexer +pygments.lexers.shell.TcshSessionLexer +pygments.lexers.sieve.SieveLexer +pygments.lexers.slash.SlashLexer +pygments.lexers.smalltalk.NewspeakLexer +pygments.lexers.smalltalk.SmalltalkLexer +pygments.lexers.smithy.SmithyLexer +pygments.lexers.smv.NuSMVLexer +pygments.lexers.snobol.SnobolLexer +pygments.lexers.solidity.SolidityLexer +pygments.lexers.soong.SoongLexer +pygments.lexers.sophia.SophiaLexer +pygments.lexers.special.OutputLexer +pygments.lexers.special.TextLexer +pygments.lexers.spice.SpiceLexer +pygments.lexers.sql.GoogleSqlLexer +pygments.lexers.sql.MySqlLexer pygments.lexers.sql.PlPgsqlLexer -pygments.lexers.sql.PsqlRegexLexer +pygments.lexers.sql.PostgresConsoleLexer +pygments.lexers.sql.PostgresExplainLexer +pygments.lexers.sql.PostgresLexer +pygments.lexers.sql.RqlLexer +pygments.lexers.sql.SqliteConsoleLexer pygments.lexers.sql.SqlLexer pygments.lexers.sql.TransactSqlLexer -pygments.lexers.sql.MySqlLexer -pygments.lexers.sql.RqlLexer +pygments.lexers.srcinfo.SrcinfoLexer +pygments.lexers.stata.StataLexer +pygments.lexers.supercollider.SuperColliderLexer +pygments.lexers.tablegen.TableGenLexer +pygments.lexers.tact.TactLexer +pygments.lexers.tal.TalLexer +pygments.lexers.tcl.TclLexer +pygments.lexers.teal.TealLexer +pygments.lexers.templates.Angular2HtmlLexer +pygments.lexers.templates.Angular2Lexer +pygments.lexers.templates.CheetahHtmlLexer +pygments.lexers.templates.CheetahJavascriptLexer +pygments.lexers.templates.CheetahLexer +pygments.lexers.templates.CheetahXmlLexer +pygments.lexers.templates.ColdfusionCFCLexer +pygments.lexers.templates.ColdfusionHtmlLexer +pygments.lexers.templates.ColdfusionLexer +pygments.lexers.templates.CssDjangoLexer +pygments.lexers.templates.CssErbLexer +pygments.lexers.templates.CssGenshiLexer +pygments.lexers.templates.CssPhpLexer +pygments.lexers.templates.CssSmartyLexer +pygments.lexers.templates.DjangoLexer +pygments.lexers.templates.ErbLexer +pygments.lexers.templates.EvoqueHtmlLexer +pygments.lexers.templates.EvoqueLexer +pygments.lexers.templates.EvoqueXmlLexer +pygments.lexers.templates.GenshiLexer +pygments.lexers.templates.GenshiTextLexer +pygments.lexers.templates.HandlebarsHtmlLexer +pygments.lexers.templates.HandlebarsLexer +pygments.lexers.templates.HtmlDjangoLexer +pygments.lexers.templates.HtmlGenshiLexer +pygments.lexers.templates.HtmlPhpLexer +pygments.lexers.templates.HtmlSmartyLexer +pygments.lexers.templates.JavascriptDjangoLexer +pygments.lexers.templates.JavascriptErbLexer +pygments.lexers.templates.JavascriptGenshiLexer +pygments.lexers.templates.JavascriptPhpLexer +pygments.lexers.templates.JavascriptSmartyLexer +pygments.lexers.templates.JspLexer +pygments.lexers.templates.LassoCssLexer +pygments.lexers.templates.LassoHtmlLexer +pygments.lexers.templates.LassoJavascriptLexer +pygments.lexers.templates.LassoXmlLexer +pygments.lexers.templates.LiquidLexer +pygments.lexers.templates.MakoCssLexer +pygments.lexers.templates.MakoHtmlLexer +pygments.lexers.templates.MakoJavascriptLexer +pygments.lexers.templates.MakoLexer +pygments.lexers.templates.MakoXmlLexer +pygments.lexers.templates.MasonLexer +pygments.lexers.templates.MyghtyCssLexer +pygments.lexers.templates.MyghtyHtmlLexer +pygments.lexers.templates.MyghtyJavascriptLexer +pygments.lexers.templates.MyghtyLexer +pygments.lexers.templates.MyghtyXmlLexer +pygments.lexers.templates.RhtmlLexer +pygments.lexers.templates.SmartyLexer +pygments.lexers.templates.SqlJinjaLexer +pygments.lexers.templates.SspLexer +pygments.lexers.templates.TeaTemplateLexer +pygments.lexers.templates.TwigHtmlLexer +pygments.lexers.templates.TwigLexer +pygments.lexers.templates.VelocityHtmlLexer +pygments.lexers.templates.VelocityLexer +pygments.lexers.templates.VelocityXmlLexer +pygments.lexers.templates.XmlDjangoLexer +pygments.lexers.templates.XmlErbLexer +pygments.lexers.templates.XmlPhpLexer +pygments.lexers.templates.XmlSmartyLexer +pygments.lexers.templates.YamlJinjaLexer +pygments.lexers.teraterm.TeraTermLexer pygments.lexers.testing.GherkinLexer pygments.lexers.testing.TAPLexer pygments.lexers.textedit.AwkLexer +pygments.lexers.textedit.SedLexer pygments.lexers.textedit.VimLexer -pygments.lexers.textfmts.IrcLogsLexer pygments.lexers.textfmts.GettextLexer pygments.lexers.textfmts.HttpLexer +pygments.lexers.textfmts.IrcLogsLexer +pygments.lexers.textfmts.KernelLogLexer +pygments.lexers.textfmts.NotmuchLexer pygments.lexers.textfmts.TodotxtLexer -pygments.lexers.trafficscript.RtsLexer pygments.lexers.theorem.CoqLexer pygments.lexers.theorem.IsabelleLexer -pygments.lexers.theorem.LeanLexer -pygments.lexers.templates.SmartyLexer -pygments.lexers.templates.VelocityLexer -pygments.lexers.templates.DjangoLexer -pygments.lexers.templates.MyghtyLexer -pygments.lexers.templates.MasonLexer -pygments.lexers.templates.MakoLexer -pygments.lexers.templates.CheetahLexer -pygments.lexers.templates.GenshiTextLexer -pygments.lexers.templates.GenshiMarkupLexer -pygments.lexers.templates.JspRootLexer -pygments.lexers.templates.EvoqueLexer -pygments.lexers.templates.ColdfusionLexer -pygments.lexers.templates.ColdfusionMarkupLexer -pygments.lexers.templates.TeaTemplateRootLexer -pygments.lexers.templates.HandlebarsLexer -pygments.lexers.templates.LiquidLexer -pygments.lexers.templates.TwigLexer -pygments.lexers.templates.Angular2Lexer -pygments.lexers.urbi.UrbiscriptLexer +pygments.lexers.thingsdb.ThingsDBLexer +pygments.lexers.tlb.TlbLexer +pygments.lexers.tls.TlsLexer +pygments.lexers.tnt.TNTLexer +pygments.lexers.trafficscript.RtsLexer pygments.lexers.typoscript.TypoScriptCssDataLexer pygments.lexers.typoscript.TypoScriptHtmlDataLexer pygments.lexers.typoscript.TypoScriptLexer +pygments.lexers.typst.TypstLexer +pygments.lexers.ul4.CSSUL4Lexer +pygments.lexers.ul4.HTMLUL4Lexer +pygments.lexers.ul4.JavascriptUL4Lexer +pygments.lexers.ul4.PythonUL4Lexer +pygments.lexers.ul4.UL4Lexer +pygments.lexers.ul4.XMLUL4Lexer +pygments.lexers.unicon.IconLexer +pygments.lexers.unicon.UcodeLexer +pygments.lexers.unicon.UniconLexer +pygments.lexers.urbi.UrbiscriptLexer +pygments.lexers.usd.UsdLexer pygments.lexers.varnish.VCLLexer +pygments.lexers.varnish.VCLSnippetLexer pygments.lexers.verification.BoogieLexer pygments.lexers.verification.SilverLexer -pygments.lexers.x10.X10Lexer -pygments.lexers.whiley.WhileyLexer -pygments.lexers.xorg.XorgLexer -pygments.lexers.webmisc.DuelLexer -pygments.lexers.webmisc.XQueryLexer -pygments.lexers.webmisc.QmlLexer +pygments.lexers.verifpal.VerifpalLexer +pygments.lexers.vip.VisualPrologGrammarLexer +pygments.lexers.vip.VisualPrologLexer +pygments.lexers.vyper.VyperLexer +pygments.lexers.webassembly.WatLexer +pygments.lexers.webidl.WebIDLLexer pygments.lexers.webmisc.CirruLexer +pygments.lexers.webmisc.DuelLexer +pygments.lexers.webmisc.QmlLexer pygments.lexers.webmisc.SlimLexer +pygments.lexers.webmisc.XQueryLexer +pygments.lexers.wgsl.WgslLexer +pygments.lexers.whiley.WhileyLexer +pygments.lexers.wowtoc.WoWTocLexer +pygments.lexers.wren.WrenLexer +pygments.lexers.x10.X10Lexer +pygments.lexers.xorg.XorgLexer +pygments.lexers.yang.YangLexer +pygments.lexers.yara.YaraLexer +pygments.lexers.zig.ZigLexer diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/regexp.go b/backend/vendor/github.com/alecthomas/chroma/v2/regexp.go index 57967268..c0e5e108 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/regexp.go +++ b/backend/vendor/github.com/alecthomas/chroma/v2/regexp.go @@ -1,6 +1,7 @@ package chroma import ( + "encoding/json" "fmt" "os" "path/filepath" @@ -135,11 +136,20 @@ func NewLexer(config *Config, rulesFunc func() Rules) (*RegexLexer, error) { } // Trace enables debug tracing. +// +// Deprecated: Use SetTracing instead. func (r *RegexLexer) Trace(trace bool) *RegexLexer { r.trace = trace return r } +// SetTracing enables debug tracing. +// +// This complies with the [TracingLexer] interface. +func (r *RegexLexer) SetTracing(trace bool) { + r.trace = trace +} + // A CompiledRule is a Rule with a pre-compiled regex. // // Note that regular expressions are lazily compiled on first use of the lexer. @@ -185,6 +195,7 @@ func (l *LexerState) Get(key interface{}) interface{} { // Iterator returns the next Token from the lexer. func (l *LexerState) Iterator() Token { // nolint: gocognit + trace := json.NewEncoder(os.Stderr) end := len(l.Text) if l.newlineAdded { end-- @@ -205,14 +216,33 @@ func (l *LexerState) Iterator() Token { // nolint: gocognit } l.State = l.Stack[len(l.Stack)-1] - if l.Lexer.trace { - fmt.Fprintf(os.Stderr, "%s: pos=%d, text=%q\n", l.State, l.Pos, string(l.Text[l.Pos:])) - } selectedRule, ok := l.Rules[l.State] if !ok { panic("unknown state " + l.State) } + var start time.Time + if l.Lexer.trace { + start = time.Now() + } ruleIndex, rule, groups, namedGroups := matchRules(l.Text, l.Pos, selectedRule) + if l.Lexer.trace { + var length int + if groups != nil { + length = len(groups[0]) + } else { + length = -1 + } + _ = trace.Encode(Trace{ //nolint + Lexer: l.Lexer.config.Name, + State: l.State, + Rule: ruleIndex, + Pattern: rule.Pattern, + Pos: l.Pos, + Length: length, + Elapsed: float64(time.Since(start)) / float64(time.Millisecond), + }) + // fmt.Fprintf(os.Stderr, "%s: pos=%d, text=%q, elapsed=%s\n", l.State, l.Pos, string(l.Text[l.Pos:]), time.Since(start)) + } // No match. if groups == nil { // From Pygments :\ @@ -349,7 +379,7 @@ func (r *RegexLexer) maybeCompile() (err error) { restart: seen := map[LexerMutator]bool{} for state := range r.rules { - for i := 0; i < len(r.rules[state]); i++ { + for i := range len(r.rules[state]) { rule := r.rules[state][i] if compile, ok := rule.Mutator.(LexerMutator); ok { if seen[compile] { @@ -366,6 +396,17 @@ restart: } } } + // Validate emitters + for state := range r.rules { + for i := range len(r.rules[state]) { + rule := r.rules[state][i] + if validate, ok := rule.Type.(ValidatingEmitter); ok { + if err := validate.ValidateEmitter(rule); err != nil { + return fmt.Errorf("%s: %s: %s: %w", r.config.Name, state, rule.Pattern, err) + } + } + } + } r.compiled = true return nil } @@ -474,7 +515,7 @@ func matchRules(text []rune, pos int, rules []*CompiledRule) (int, *CompiledRule func ensureLF(text string) string { buf := make([]byte, len(text)) var j int - for i := 0; i < len(text); i++ { + for i := range len(text) { c := text[i] if c == '\r' { if i < len(text)-1 && text[i+1] == '\n' { diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/registry.go b/backend/vendor/github.com/alecthomas/chroma/v2/registry.go index 4742e8c5..a309af9d 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/registry.go +++ b/backend/vendor/github.com/alecthomas/chroma/v2/registry.go @@ -48,6 +48,24 @@ func (l *LexerRegistry) Names(withAliases bool) []string { return out } +// Aliases of all the lexers, and skip those lexers who do not have any aliases, +// or show their name instead +func (l *LexerRegistry) Aliases(skipWithoutAliases bool) []string { + out := []string{} + for _, lexer := range l.Lexers { + config := lexer.Config() + if len(config.Aliases) == 0 { + if skipWithoutAliases { + continue + } + out = append(out, config.Name) + } + out = append(out, config.Aliases...) + } + sort.Strings(out) + return out +} + // Get a Lexer by name, alias or file extension. func (l *LexerRegistry) Get(name string) Lexer { if lexer := l.byName[name]; lexer != nil { diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/renovate.json5 b/backend/vendor/github.com/alecthomas/chroma/v2/renovate.json5 index 77c7b016..9ade4812 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/renovate.json5 +++ b/backend/vendor/github.com/alecthomas/chroma/v2/renovate.json5 @@ -1,18 +1,24 @@ { - $schema: "https://docs.renovatebot.com/renovate-schema.json", - extends: [ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ "config:recommended", ":semanticCommits", ":semanticCommitTypeAll(chore)", ":semanticCommitScope(deps)", "group:allNonMajor", "schedule:earlyMondays", // Run once a week. + 'helpers:pinGitHubActionDigests', ], - packageRules: [ + "packageRules": [ { - matchPackageNames: ["golangci-lint"], - matchManagers: ["hermit"], - enabled: false, + "matchPackageNames": ["golangci-lint"], + "matchManagers": ["hermit"], + "enabled": false }, - ], + { + "matchPackageNames": ["github.com/gorilla/csrf"], + "matchManagers": ["gomod"], + "enabled": false + } + ] } diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-frappe.xml b/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-frappe.xml index 0adf1ba9..66a361fb 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-frappe.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-frappe.xml @@ -65,12 +65,12 @@ - + - + @@ -80,4 +80,4 @@ - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-latte.xml b/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-latte.xml index 3ea767fd..c87c8765 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-latte.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-latte.xml @@ -65,12 +65,12 @@ - + - + @@ -80,4 +80,4 @@ - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-macchiato.xml b/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-macchiato.xml index 6b500284..5dba9c64 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-macchiato.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-macchiato.xml @@ -65,12 +65,12 @@ - + - + @@ -80,4 +80,4 @@ - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-mocha.xml b/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-mocha.xml index 9a401912..9f9b9152 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-mocha.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-mocha.xml @@ -65,12 +65,12 @@ - + - + @@ -80,4 +80,4 @@ - \ No newline at end of file + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/styles/rpgle.xml b/backend/vendor/github.com/alecthomas/chroma/v2/styles/rpgle.xml new file mode 100644 index 00000000..678fd70f --- /dev/null +++ b/backend/vendor/github.com/alecthomas/chroma/v2/styles/rpgle.xml @@ -0,0 +1,30 @@ + diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/styles/rrt.xml b/backend/vendor/github.com/alecthomas/chroma/v2/styles/rrt.xml index 5f1daaa2..f2c5feb9 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/styles/rrt.xml +++ b/backend/vendor/github.com/alecthomas/chroma/v2/styles/rrt.xml @@ -10,4 +10,10 @@ + + + + + + \ No newline at end of file diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/tokentype_enumer.go b/backend/vendor/github.com/alecthomas/chroma/v2/tokentype_enumer.go index 3024264a..c3b15f03 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/tokentype_enumer.go +++ b/backend/vendor/github.com/alecthomas/chroma/v2/tokentype_enumer.go @@ -7,8 +7,8 @@ import ( "strings" ) -const _TokenTypeName = "IgnoreNoneOtherErrorCodeLineLineLinkLineTableTDLineTableLineHighlightLineNumbersTableLineNumbersLinePreWrapperBackgroundEOFTypeKeywordKeywordConstantKeywordDeclarationKeywordNamespaceKeywordPseudoKeywordReservedKeywordTypeNameNameAttributeNameBuiltinNameBuiltinPseudoNameClassNameConstantNameDecoratorNameEntityNameExceptionNameFunctionNameFunctionMagicNameKeywordNameLabelNameNamespaceNameOperatorNameOtherNamePseudoNamePropertyNameTagNameVariableNameVariableAnonymousNameVariableClassNameVariableGlobalNameVariableInstanceNameVariableMagicLiteralLiteralDateLiteralOtherLiteralStringLiteralStringAffixLiteralStringAtomLiteralStringBacktickLiteralStringBooleanLiteralStringCharLiteralStringDelimiterLiteralStringDocLiteralStringDoubleLiteralStringEscapeLiteralStringHeredocLiteralStringInterpolLiteralStringNameLiteralStringOtherLiteralStringRegexLiteralStringSingleLiteralStringSymbolLiteralNumberLiteralNumberBinLiteralNumberFloatLiteralNumberHexLiteralNumberIntegerLiteralNumberIntegerLongLiteralNumberOctLiteralNumberByteOperatorOperatorWordPunctuationCommentCommentHashbangCommentMultilineCommentSingleCommentSpecialCommentPreprocCommentPreprocFileGenericGenericDeletedGenericEmphGenericErrorGenericHeadingGenericInsertedGenericOutputGenericPromptGenericStrongGenericSubheadingGenericTracebackGenericUnderlineTextTextWhitespaceTextSymbolTextPunctuation" -const _TokenTypeLowerName = "ignorenoneothererrorcodelinelinelinklinetabletdlinetablelinehighlightlinenumberstablelinenumberslineprewrapperbackgroundeoftypekeywordkeywordconstantkeyworddeclarationkeywordnamespacekeywordpseudokeywordreservedkeywordtypenamenameattributenamebuiltinnamebuiltinpseudonameclassnameconstantnamedecoratornameentitynameexceptionnamefunctionnamefunctionmagicnamekeywordnamelabelnamenamespacenameoperatornameothernamepseudonamepropertynametagnamevariablenamevariableanonymousnamevariableclassnamevariableglobalnamevariableinstancenamevariablemagicliteralliteraldateliteralotherliteralstringliteralstringaffixliteralstringatomliteralstringbacktickliteralstringbooleanliteralstringcharliteralstringdelimiterliteralstringdocliteralstringdoubleliteralstringescapeliteralstringheredocliteralstringinterpolliteralstringnameliteralstringotherliteralstringregexliteralstringsingleliteralstringsymbolliteralnumberliteralnumberbinliteralnumberfloatliteralnumberhexliteralnumberintegerliteralnumberintegerlongliteralnumberoctliteralnumberbyteoperatoroperatorwordpunctuationcommentcommenthashbangcommentmultilinecommentsinglecommentspecialcommentpreproccommentpreprocfilegenericgenericdeletedgenericemphgenericerrorgenericheadinggenericinsertedgenericoutputgenericpromptgenericstronggenericsubheadinggenerictracebackgenericunderlinetexttextwhitespacetextsymboltextpunctuation" +const _TokenTypeName = "IgnoreNoneOtherErrorCodeLineLineLinkLineTableTDLineTableLineHighlightLineNumbersTableLineNumbersLinePreWrapperBackgroundEOFTypeKeywordKeywordConstantKeywordDeclarationKeywordNamespaceKeywordPseudoKeywordReservedKeywordTypeNameNameAttributeNameClassNameConstantNameDecoratorNameEntityNameExceptionNameKeywordNameLabelNameNamespaceNameOperatorNameOtherNamePseudoNamePropertyNameTagNameBuiltinNameBuiltinPseudoNameVariableNameVariableAnonymousNameVariableClassNameVariableGlobalNameVariableInstanceNameVariableMagicNameFunctionNameFunctionMagicLiteralLiteralDateLiteralOtherLiteralStringLiteralStringAffixLiteralStringAtomLiteralStringBacktickLiteralStringBooleanLiteralStringCharLiteralStringDelimiterLiteralStringDocLiteralStringDoubleLiteralStringEscapeLiteralStringHeredocLiteralStringInterpolLiteralStringNameLiteralStringOtherLiteralStringRegexLiteralStringSingleLiteralStringSymbolLiteralNumberLiteralNumberBinLiteralNumberFloatLiteralNumberHexLiteralNumberIntegerLiteralNumberIntegerLongLiteralNumberOctLiteralNumberByteOperatorOperatorWordPunctuationCommentCommentHashbangCommentMultilineCommentSingleCommentSpecialCommentPreprocCommentPreprocFileGenericGenericDeletedGenericEmphGenericErrorGenericHeadingGenericInsertedGenericOutputGenericPromptGenericStrongGenericSubheadingGenericTracebackGenericUnderlineTextTextWhitespaceTextSymbolTextPunctuation" +const _TokenTypeLowerName = "ignorenoneothererrorcodelinelinelinklinetabletdlinetablelinehighlightlinenumberstablelinenumberslineprewrapperbackgroundeoftypekeywordkeywordconstantkeyworddeclarationkeywordnamespacekeywordpseudokeywordreservedkeywordtypenamenameattributenameclassnameconstantnamedecoratornameentitynameexceptionnamekeywordnamelabelnamenamespacenameoperatornameothernamepseudonamepropertynametagnamebuiltinnamebuiltinpseudonamevariablenamevariableanonymousnamevariableclassnamevariableglobalnamevariableinstancenamevariablemagicnamefunctionnamefunctionmagicliteralliteraldateliteralotherliteralstringliteralstringaffixliteralstringatomliteralstringbacktickliteralstringbooleanliteralstringcharliteralstringdelimiterliteralstringdocliteralstringdoubleliteralstringescapeliteralstringheredocliteralstringinterpolliteralstringnameliteralstringotherliteralstringregexliteralstringsingleliteralstringsymbolliteralnumberliteralnumberbinliteralnumberfloatliteralnumberhexliteralnumberintegerliteralnumberintegerlongliteralnumberoctliteralnumberbyteoperatoroperatorwordpunctuationcommentcommenthashbangcommentmultilinecommentsinglecommentspecialcommentpreproccommentpreprocfilegenericgenericdeletedgenericemphgenericerrorgenericheadinggenericinsertedgenericoutputgenericpromptgenericstronggenericsubheadinggenerictracebackgenericunderlinetexttextwhitespacetextsymboltextpunctuation" var _TokenTypeMap = map[TokenType]string{ -14: _TokenTypeName[0:6], @@ -35,29 +35,29 @@ var _TokenTypeMap = map[TokenType]string{ 1006: _TokenTypeName[211:222], 2000: _TokenTypeName[222:226], 2001: _TokenTypeName[226:239], - 2002: _TokenTypeName[239:250], - 2003: _TokenTypeName[250:267], - 2004: _TokenTypeName[267:276], - 2005: _TokenTypeName[276:288], - 2006: _TokenTypeName[288:301], - 2007: _TokenTypeName[301:311], - 2008: _TokenTypeName[311:324], - 2009: _TokenTypeName[324:336], - 2010: _TokenTypeName[336:353], - 2011: _TokenTypeName[353:364], - 2012: _TokenTypeName[364:373], - 2013: _TokenTypeName[373:386], - 2014: _TokenTypeName[386:398], - 2015: _TokenTypeName[398:407], - 2016: _TokenTypeName[407:417], - 2017: _TokenTypeName[417:429], - 2018: _TokenTypeName[429:436], - 2019: _TokenTypeName[436:448], - 2020: _TokenTypeName[448:469], - 2021: _TokenTypeName[469:486], - 2022: _TokenTypeName[486:504], - 2023: _TokenTypeName[504:524], - 2024: _TokenTypeName[524:541], + 2002: _TokenTypeName[239:248], + 2003: _TokenTypeName[248:260], + 2004: _TokenTypeName[260:273], + 2005: _TokenTypeName[273:283], + 2006: _TokenTypeName[283:296], + 2007: _TokenTypeName[296:307], + 2008: _TokenTypeName[307:316], + 2009: _TokenTypeName[316:329], + 2010: _TokenTypeName[329:341], + 2011: _TokenTypeName[341:350], + 2012: _TokenTypeName[350:360], + 2013: _TokenTypeName[360:372], + 2014: _TokenTypeName[372:379], + 2100: _TokenTypeName[379:390], + 2101: _TokenTypeName[390:407], + 2200: _TokenTypeName[407:419], + 2201: _TokenTypeName[419:440], + 2202: _TokenTypeName[440:457], + 2203: _TokenTypeName[457:475], + 2204: _TokenTypeName[475:495], + 2205: _TokenTypeName[495:512], + 2300: _TokenTypeName[512:524], + 2301: _TokenTypeName[524:541], 3000: _TokenTypeName[541:548], 3001: _TokenTypeName[548:559], 3002: _TokenTypeName[559:571], @@ -149,29 +149,29 @@ func _TokenTypeNoOp() { _ = x[KeywordType-(1006)] _ = x[Name-(2000)] _ = x[NameAttribute-(2001)] - _ = x[NameBuiltin-(2002)] - _ = x[NameBuiltinPseudo-(2003)] - _ = x[NameClass-(2004)] - _ = x[NameConstant-(2005)] - _ = x[NameDecorator-(2006)] - _ = x[NameEntity-(2007)] - _ = x[NameException-(2008)] - _ = x[NameFunction-(2009)] - _ = x[NameFunctionMagic-(2010)] - _ = x[NameKeyword-(2011)] - _ = x[NameLabel-(2012)] - _ = x[NameNamespace-(2013)] - _ = x[NameOperator-(2014)] - _ = x[NameOther-(2015)] - _ = x[NamePseudo-(2016)] - _ = x[NameProperty-(2017)] - _ = x[NameTag-(2018)] - _ = x[NameVariable-(2019)] - _ = x[NameVariableAnonymous-(2020)] - _ = x[NameVariableClass-(2021)] - _ = x[NameVariableGlobal-(2022)] - _ = x[NameVariableInstance-(2023)] - _ = x[NameVariableMagic-(2024)] + _ = x[NameClass-(2002)] + _ = x[NameConstant-(2003)] + _ = x[NameDecorator-(2004)] + _ = x[NameEntity-(2005)] + _ = x[NameException-(2006)] + _ = x[NameKeyword-(2007)] + _ = x[NameLabel-(2008)] + _ = x[NameNamespace-(2009)] + _ = x[NameOperator-(2010)] + _ = x[NameOther-(2011)] + _ = x[NamePseudo-(2012)] + _ = x[NameProperty-(2013)] + _ = x[NameTag-(2014)] + _ = x[NameBuiltin-(2100)] + _ = x[NameBuiltinPseudo-(2101)] + _ = x[NameVariable-(2200)] + _ = x[NameVariableAnonymous-(2201)] + _ = x[NameVariableClass-(2202)] + _ = x[NameVariableGlobal-(2203)] + _ = x[NameVariableInstance-(2204)] + _ = x[NameVariableMagic-(2205)] + _ = x[NameFunction-(2300)] + _ = x[NameFunctionMagic-(2301)] _ = x[Literal-(3000)] _ = x[LiteralDate-(3001)] _ = x[LiteralOther-(3002)] @@ -228,7 +228,7 @@ func _TokenTypeNoOp() { _ = x[TextPunctuation-(8003)] } -var _TokenTypeValues = []TokenType{Ignore, None, Other, Error, CodeLine, LineLink, LineTableTD, LineTable, LineHighlight, LineNumbersTable, LineNumbers, Line, PreWrapper, Background, EOFType, Keyword, KeywordConstant, KeywordDeclaration, KeywordNamespace, KeywordPseudo, KeywordReserved, KeywordType, Name, NameAttribute, NameBuiltin, NameBuiltinPseudo, NameClass, NameConstant, NameDecorator, NameEntity, NameException, NameFunction, NameFunctionMagic, NameKeyword, NameLabel, NameNamespace, NameOperator, NameOther, NamePseudo, NameProperty, NameTag, NameVariable, NameVariableAnonymous, NameVariableClass, NameVariableGlobal, NameVariableInstance, NameVariableMagic, Literal, LiteralDate, LiteralOther, LiteralString, LiteralStringAffix, LiteralStringAtom, LiteralStringBacktick, LiteralStringBoolean, LiteralStringChar, LiteralStringDelimiter, LiteralStringDoc, LiteralStringDouble, LiteralStringEscape, LiteralStringHeredoc, LiteralStringInterpol, LiteralStringName, LiteralStringOther, LiteralStringRegex, LiteralStringSingle, LiteralStringSymbol, LiteralNumber, LiteralNumberBin, LiteralNumberFloat, LiteralNumberHex, LiteralNumberInteger, LiteralNumberIntegerLong, LiteralNumberOct, LiteralNumberByte, Operator, OperatorWord, Punctuation, Comment, CommentHashbang, CommentMultiline, CommentSingle, CommentSpecial, CommentPreproc, CommentPreprocFile, Generic, GenericDeleted, GenericEmph, GenericError, GenericHeading, GenericInserted, GenericOutput, GenericPrompt, GenericStrong, GenericSubheading, GenericTraceback, GenericUnderline, Text, TextWhitespace, TextSymbol, TextPunctuation} +var _TokenTypeValues = []TokenType{Ignore, None, Other, Error, CodeLine, LineLink, LineTableTD, LineTable, LineHighlight, LineNumbersTable, LineNumbers, Line, PreWrapper, Background, EOFType, Keyword, KeywordConstant, KeywordDeclaration, KeywordNamespace, KeywordPseudo, KeywordReserved, KeywordType, Name, NameAttribute, NameClass, NameConstant, NameDecorator, NameEntity, NameException, NameKeyword, NameLabel, NameNamespace, NameOperator, NameOther, NamePseudo, NameProperty, NameTag, NameBuiltin, NameBuiltinPseudo, NameVariable, NameVariableAnonymous, NameVariableClass, NameVariableGlobal, NameVariableInstance, NameVariableMagic, NameFunction, NameFunctionMagic, Literal, LiteralDate, LiteralOther, LiteralString, LiteralStringAffix, LiteralStringAtom, LiteralStringBacktick, LiteralStringBoolean, LiteralStringChar, LiteralStringDelimiter, LiteralStringDoc, LiteralStringDouble, LiteralStringEscape, LiteralStringHeredoc, LiteralStringInterpol, LiteralStringName, LiteralStringOther, LiteralStringRegex, LiteralStringSingle, LiteralStringSymbol, LiteralNumber, LiteralNumberBin, LiteralNumberFloat, LiteralNumberHex, LiteralNumberInteger, LiteralNumberIntegerLong, LiteralNumberOct, LiteralNumberByte, Operator, OperatorWord, Punctuation, Comment, CommentHashbang, CommentMultiline, CommentSingle, CommentSpecial, CommentPreproc, CommentPreprocFile, Generic, GenericDeleted, GenericEmph, GenericError, GenericHeading, GenericInserted, GenericOutput, GenericPrompt, GenericStrong, GenericSubheading, GenericTraceback, GenericUnderline, Text, TextWhitespace, TextSymbol, TextPunctuation} var _TokenTypeNameToValueMap = map[string]TokenType{ _TokenTypeName[0:6]: Ignore, @@ -279,52 +279,52 @@ var _TokenTypeNameToValueMap = map[string]TokenType{ _TokenTypeLowerName[222:226]: Name, _TokenTypeName[226:239]: NameAttribute, _TokenTypeLowerName[226:239]: NameAttribute, - _TokenTypeName[239:250]: NameBuiltin, - _TokenTypeLowerName[239:250]: NameBuiltin, - _TokenTypeName[250:267]: NameBuiltinPseudo, - _TokenTypeLowerName[250:267]: NameBuiltinPseudo, - _TokenTypeName[267:276]: NameClass, - _TokenTypeLowerName[267:276]: NameClass, - _TokenTypeName[276:288]: NameConstant, - _TokenTypeLowerName[276:288]: NameConstant, - _TokenTypeName[288:301]: NameDecorator, - _TokenTypeLowerName[288:301]: NameDecorator, - _TokenTypeName[301:311]: NameEntity, - _TokenTypeLowerName[301:311]: NameEntity, - _TokenTypeName[311:324]: NameException, - _TokenTypeLowerName[311:324]: NameException, - _TokenTypeName[324:336]: NameFunction, - _TokenTypeLowerName[324:336]: NameFunction, - _TokenTypeName[336:353]: NameFunctionMagic, - _TokenTypeLowerName[336:353]: NameFunctionMagic, - _TokenTypeName[353:364]: NameKeyword, - _TokenTypeLowerName[353:364]: NameKeyword, - _TokenTypeName[364:373]: NameLabel, - _TokenTypeLowerName[364:373]: NameLabel, - _TokenTypeName[373:386]: NameNamespace, - _TokenTypeLowerName[373:386]: NameNamespace, - _TokenTypeName[386:398]: NameOperator, - _TokenTypeLowerName[386:398]: NameOperator, - _TokenTypeName[398:407]: NameOther, - _TokenTypeLowerName[398:407]: NameOther, - _TokenTypeName[407:417]: NamePseudo, - _TokenTypeLowerName[407:417]: NamePseudo, - _TokenTypeName[417:429]: NameProperty, - _TokenTypeLowerName[417:429]: NameProperty, - _TokenTypeName[429:436]: NameTag, - _TokenTypeLowerName[429:436]: NameTag, - _TokenTypeName[436:448]: NameVariable, - _TokenTypeLowerName[436:448]: NameVariable, - _TokenTypeName[448:469]: NameVariableAnonymous, - _TokenTypeLowerName[448:469]: NameVariableAnonymous, - _TokenTypeName[469:486]: NameVariableClass, - _TokenTypeLowerName[469:486]: NameVariableClass, - _TokenTypeName[486:504]: NameVariableGlobal, - _TokenTypeLowerName[486:504]: NameVariableGlobal, - _TokenTypeName[504:524]: NameVariableInstance, - _TokenTypeLowerName[504:524]: NameVariableInstance, - _TokenTypeName[524:541]: NameVariableMagic, - _TokenTypeLowerName[524:541]: NameVariableMagic, + _TokenTypeName[239:248]: NameClass, + _TokenTypeLowerName[239:248]: NameClass, + _TokenTypeName[248:260]: NameConstant, + _TokenTypeLowerName[248:260]: NameConstant, + _TokenTypeName[260:273]: NameDecorator, + _TokenTypeLowerName[260:273]: NameDecorator, + _TokenTypeName[273:283]: NameEntity, + _TokenTypeLowerName[273:283]: NameEntity, + _TokenTypeName[283:296]: NameException, + _TokenTypeLowerName[283:296]: NameException, + _TokenTypeName[296:307]: NameKeyword, + _TokenTypeLowerName[296:307]: NameKeyword, + _TokenTypeName[307:316]: NameLabel, + _TokenTypeLowerName[307:316]: NameLabel, + _TokenTypeName[316:329]: NameNamespace, + _TokenTypeLowerName[316:329]: NameNamespace, + _TokenTypeName[329:341]: NameOperator, + _TokenTypeLowerName[329:341]: NameOperator, + _TokenTypeName[341:350]: NameOther, + _TokenTypeLowerName[341:350]: NameOther, + _TokenTypeName[350:360]: NamePseudo, + _TokenTypeLowerName[350:360]: NamePseudo, + _TokenTypeName[360:372]: NameProperty, + _TokenTypeLowerName[360:372]: NameProperty, + _TokenTypeName[372:379]: NameTag, + _TokenTypeLowerName[372:379]: NameTag, + _TokenTypeName[379:390]: NameBuiltin, + _TokenTypeLowerName[379:390]: NameBuiltin, + _TokenTypeName[390:407]: NameBuiltinPseudo, + _TokenTypeLowerName[390:407]: NameBuiltinPseudo, + _TokenTypeName[407:419]: NameVariable, + _TokenTypeLowerName[407:419]: NameVariable, + _TokenTypeName[419:440]: NameVariableAnonymous, + _TokenTypeLowerName[419:440]: NameVariableAnonymous, + _TokenTypeName[440:457]: NameVariableClass, + _TokenTypeLowerName[440:457]: NameVariableClass, + _TokenTypeName[457:475]: NameVariableGlobal, + _TokenTypeLowerName[457:475]: NameVariableGlobal, + _TokenTypeName[475:495]: NameVariableInstance, + _TokenTypeLowerName[475:495]: NameVariableInstance, + _TokenTypeName[495:512]: NameVariableMagic, + _TokenTypeLowerName[495:512]: NameVariableMagic, + _TokenTypeName[512:524]: NameFunction, + _TokenTypeLowerName[512:524]: NameFunction, + _TokenTypeName[524:541]: NameFunctionMagic, + _TokenTypeLowerName[524:541]: NameFunctionMagic, _TokenTypeName[541:548]: Literal, _TokenTypeLowerName[541:548]: Literal, _TokenTypeName[548:559]: LiteralDate, @@ -460,28 +460,28 @@ var _TokenTypeNames = []string{ _TokenTypeName[211:222], _TokenTypeName[222:226], _TokenTypeName[226:239], - _TokenTypeName[239:250], - _TokenTypeName[250:267], - _TokenTypeName[267:276], - _TokenTypeName[276:288], - _TokenTypeName[288:301], - _TokenTypeName[301:311], - _TokenTypeName[311:324], - _TokenTypeName[324:336], - _TokenTypeName[336:353], - _TokenTypeName[353:364], - _TokenTypeName[364:373], - _TokenTypeName[373:386], - _TokenTypeName[386:398], - _TokenTypeName[398:407], - _TokenTypeName[407:417], - _TokenTypeName[417:429], - _TokenTypeName[429:436], - _TokenTypeName[436:448], - _TokenTypeName[448:469], - _TokenTypeName[469:486], - _TokenTypeName[486:504], - _TokenTypeName[504:524], + _TokenTypeName[239:248], + _TokenTypeName[248:260], + _TokenTypeName[260:273], + _TokenTypeName[273:283], + _TokenTypeName[283:296], + _TokenTypeName[296:307], + _TokenTypeName[307:316], + _TokenTypeName[316:329], + _TokenTypeName[329:341], + _TokenTypeName[341:350], + _TokenTypeName[350:360], + _TokenTypeName[360:372], + _TokenTypeName[372:379], + _TokenTypeName[379:390], + _TokenTypeName[390:407], + _TokenTypeName[407:419], + _TokenTypeName[419:440], + _TokenTypeName[440:457], + _TokenTypeName[457:475], + _TokenTypeName[475:495], + _TokenTypeName[495:512], + _TokenTypeName[512:524], _TokenTypeName[524:541], _TokenTypeName[541:548], _TokenTypeName[548:559], diff --git a/backend/vendor/github.com/alecthomas/chroma/v2/types.go b/backend/vendor/github.com/alecthomas/chroma/v2/types.go index a5762937..3009f980 100644 --- a/backend/vendor/github.com/alecthomas/chroma/v2/types.go +++ b/backend/vendor/github.com/alecthomas/chroma/v2/types.go @@ -62,15 +62,11 @@ const ( const ( Name TokenType = 2000 + iota NameAttribute - NameBuiltin - NameBuiltinPseudo NameClass NameConstant NameDecorator NameEntity NameException - NameFunction - NameFunctionMagic NameKeyword NameLabel NameNamespace @@ -79,7 +75,17 @@ const ( NamePseudo NameProperty NameTag - NameVariable +) + +// Builtin names. +const ( + NameBuiltin TokenType = 2100 + iota + NameBuiltinPseudo +) + +// Variable names. +const ( + NameVariable TokenType = 2200 + iota NameVariableAnonymous NameVariableClass NameVariableGlobal @@ -87,6 +93,12 @@ const ( NameVariableMagic ) +// Function names. +const ( + NameFunction TokenType = 2300 + iota + NameFunctionMagic +) + // Literals. const ( Literal TokenType = 3000 + iota diff --git a/backend/vendor/github.com/didip/tollbooth/v8/.gitignore b/backend/vendor/github.com/didip/tollbooth/v8/.gitignore deleted file mode 100644 index 91ea4d71..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/debug -/.vscode -/.idea \ No newline at end of file diff --git a/backend/vendor/github.com/didip/tollbooth/v8/.golangci.yml b/backend/vendor/github.com/didip/tollbooth/v8/.golangci.yml deleted file mode 100644 index 5d0a4b64..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/.golangci.yml +++ /dev/null @@ -1,33 +0,0 @@ -linters: - enable: - - revive - - govet - - unconvert - - megacheck - - gas - - gocyclo - - dupl - - misspell - - unparam - - unused - - typecheck - - ineffassign - - stylecheck - - gochecknoinits - - exportloopref - - gocritic - - nakedret - - gosimple - - prealloc - fast: false - disable-all: true - -issues: - exclude-rules: - - path: _test\.go - linters: - - dupl - - text: "Errors unhandled" - linters: - - gosec - exclude-use-default: false diff --git a/backend/vendor/github.com/didip/tollbooth/v8/README.md b/backend/vendor/github.com/didip/tollbooth/v8/README.md deleted file mode 100644 index e08fe4a6..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/README.md +++ /dev/null @@ -1,217 +0,0 @@ -[![GoDoc](https://godoc.org/github.com/didip/tollbooth?status.svg)](http://godoc.org/github.com/didip/tollbooth) -[![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/didip/tollbooth/master/LICENSE) - -## Tollbooth - -This is a generic middleware to rate-limit HTTP requests. - -**NOTE 1:** This library is considered finished. - -**NOTE 2:** Major version changes are backward-incompatible. `v2.0.0` streamlines the ugliness of the old API. - -## Versions - -**v1.0.0:** This version maintains the old API but all the thirdparty modules are moved to their own repo. - -**v2.x.x:** Brand-new API for the sake of code cleanup, thread safety, & auto-expiring data structures. - -**v3.x.x:** Apparently we have been using golang.org/x/time/rate incorrectly. See issue #48. It always limits X number per 1 second. The time duration is not changeable, so it does not make sense to pass TTL to tollbooth. - -**v4.x.x:** Float64 for max requests per second - -**v5.x.x:** go.mod and go.sum - -**v6.x.x:** Replaced `go-cache` with `github.com/go-pkgz/expirable-cache` because `go-cache` leaks goroutines. - -**v7.x.x:** Replaced `time/rate` with `embedded time/rate` so that we can support more rate limit headers. - -**v8.x.x:** Address `RemoteIP` vulnerability concern by replacing `SetIPLookups` with `SetIPLookup`, an explicit way to pick the IP address. - - -## Five Minute Tutorial - -```go -package main - -import ( - "net/http" - - "github.com/didip/tollbooth/v8" - "github.com/didip/tollbooth/v8/limiter" -) - -func HelloHandler(w http.ResponseWriter, req *http.Request) { - w.Write([]byte("Hello, World!")) -} - -func main() { - // Create a request limiter per handler. - lmt := tollbooth.NewLimiter(1, nil) - - // New in version >= 8, you must explicitly define how to pick the IP address. - lmt.SetIPLookup(limiter.IPLookup{ - Name: "X-Real-IP", - IndexFromRight: 0, - }) - - http.Handle("/", tollbooth.LimitFuncHandler(lmt, HelloHandler)) - http.ListenAndServe(":12345", nil) -} -``` - -## Features - -1. Rate-limit by request's remote IP, path, methods, custom headers, & basic auth usernames. - ```go - import ( - "time" - - "github.com/didip/tollbooth/v8" - "github.com/didip/tollbooth/v8/limiter" - ) - - lmt := tollbooth.NewLimiter(1, nil) - - // or create a limiter with expirable token buckets - // This setting means: - // create a 1 request/second limiter and - // every token bucket in it will expire 1 hour after it was initially set. - lmt = tollbooth.NewLimiter(1, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Hour}) - - // New in version >= 8, you must explicitly define how to pick the IP address. - // If IP address cannot be found, rate limiter will not be activated. - lmt.SetIPLookup(limiter.IPLookup{ - // The name of lookup method. - // Possible options are: RemoteAddr, X-Forwarded-For, X-Real-IP, CF-Connecting-IP - // All other headers are considered unknown and will be ignored. - Name: "X-Real-IP", - - // The index position to pick the ip address from a comma separated list. - // The index goes from right to left. - // - // When there are multiple of the same headers, - // we will concat them together in the order of first to last seen. - // And then we pick the IP using this index position. - IndexFromRight: 0, - }) - - // In version >= 8, lmt.SetIPLookups and lmt.GetIPLookups are removed. - - // Limit only GET and POST requests. - lmt.SetMethods([]string{"GET", "POST"}) - - // Limit based on basic auth usernames. - // You add them on-load, or later as you handle requests. - lmt.SetBasicAuthUsers([]string{"bob", "jane", "didip", "vip"}) - // You can remove them later as well. - lmt.RemoveBasicAuthUsers([]string{"vip"}) - - // Limit request headers containing certain values. - // You add them on-load, or later as you handle requests. - lmt.SetHeader("X-Access-Token", []string{"abc123", "xyz098"}) - // You can remove all entries at once. - lmt.RemoveHeader("X-Access-Token") - // Or remove specific ones. - lmt.RemoveHeaderEntries("X-Access-Token", []string{"limitless-token"}) - - // By the way, the setters are chainable. Example: - lmt.SetMethods([]string{"GET", "POST"}). - SetBasicAuthUsers([]string{"sansa"}). - SetBasicAuthUsers([]string{"tyrion"}) - ``` - -2. Compose your own middleware by using `LimitByKeys()`. - -3. Header entries and basic auth users can expire over time (to conserve memory). - - ```go - import "time" - - lmt := tollbooth.NewLimiter(1, nil) - - // Set a custom expiration TTL for token bucket. - lmt.SetTokenBucketExpirationTTL(time.Hour) - - // Set a custom expiration TTL for basic auth users. - lmt.SetBasicAuthExpirationTTL(time.Hour) - - // Set a custom expiration TTL for header entries. - lmt.SetHeaderEntryExpirationTTL(time.Hour) - ``` - -4. Upon rejection, the following HTTP response headers are available to users: - - * `X-Rate-Limit-Limit` The maximum request limit. - - * `X-Rate-Limit-Duration` The rate-limiter duration. - - * `X-Rate-Limit-Request-Forwarded-For` The rejected request `X-Forwarded-For`. - - * `X-Rate-Limit-Request-Remote-Addr` The rejected request `RemoteAddr`. - - Upon both success and rejection [RateLimit](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers) headers are sent: - - * `RateLimit-Limit` The maximum request limit within the time window (1s). - - * `RateLimit-Reset` The rate-limiter time window duration in seconds (always 1s). - - * `RateLimit-Remaining` The remaining tokens. - -5. Customize your own message or function when limit is reached. - - ```go - lmt := tollbooth.NewLimiter(1, nil) - - // New in version >= 8, you must explicitly define how to pick the IP address. - lmt.SetIPLookup(limiter.IPLookup{ - Name: "X-Forwarded-For", - IndexFromRight: 0, - }) - - // Set a custom message. - lmt.SetMessage("You have reached maximum request limit.") - - // Set a custom content-type. - lmt.SetMessageContentType("text/plain; charset=utf-8") - - // Set a custom function for rejection. - lmt.SetOnLimitReached(func(w http.ResponseWriter, r *http.Request) { fmt.Println("A request was rejected") }) - ``` - -6. Tollbooth does not require external storage since it uses an algorithm called [Token Bucket](http://en.wikipedia.org/wiki/Token_bucket) [(Go library: golang.org/x/time/rate)](https://godoc.org/golang.org/x/time/rate). - -## Other Web Frameworks - -Sometimes, other frameworks require a little bit of shim to use Tollbooth. These shims below are contributed by the community, so I make no promises on how well they work. The one I am familiar with are: Chi, Gin, and Negroni. - -* [Chi](https://github.com/didip/tollbooth_chi) - -* [Echo](https://github.com/didip/tollbooth_echo) - -* [FastHTTP](https://github.com/didip/tollbooth_fasthttp) - -* [Gin](https://github.com/didip/tollbooth_gin) - -* [GoRestful](https://github.com/didip/tollbooth_gorestful) - -* [HTTPRouter](https://github.com/didip/tollbooth_httprouter) - -* [Iris](https://github.com/didip/tollbooth_iris) - -* [Negroni](https://github.com/didip/tollbooth_negroni) - -## My other Go libraries - -* [ErrStack](https://github.com/didip/errstack): A small library to combine errors and also display filename and line number. - -* [Stopwatch](https://github.com/didip/stopwatch): A small library to measure latency of things. Useful if you want to report latency data to Graphite. - -* [LaborUnion](https://github.com/didip/laborunion): A dynamic worker pool library. - -* [Gomet](https://github.com/didip/gomet): Simple HTTP client & server long poll library for Go. Useful for receiving live updates without needing Websocket. - -## Contributions - -Before sending a PR with code changes, please make sure altered code is covered with tests which are passing, and that golangci-lint shows no errors. - -To check the linter output, [install it](https://golangci-lint.run/usage/install/#local-installation) and then run `golangci-lint run` in the root directory of the repository. diff --git a/backend/vendor/github.com/didip/tollbooth/v8/errors/errors.go b/backend/vendor/github.com/didip/tollbooth/v8/errors/errors.go deleted file mode 100644 index 149bc5a1..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/errors/errors.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package errors provide data structure for errors. -package errors - -import "fmt" - -// HTTPError is an error struct that returns both message and status code. -type HTTPError struct { - Message string - StatusCode int -} - -// Error returns error message. -func (httperror *HTTPError) Error() string { - return fmt.Sprintf("%v: %v", httperror.StatusCode, httperror.Message) -} diff --git a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/AUTHORS b/backend/vendor/github.com/didip/tollbooth/v8/internal/time/AUTHORS deleted file mode 100644 index 15167cd7..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/CONTRIBUTORS b/backend/vendor/github.com/didip/tollbooth/v8/internal/time/CONTRIBUTORS deleted file mode 100644 index 1c4577e9..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/LICENSE b/backend/vendor/github.com/didip/tollbooth/v8/internal/time/LICENSE deleted file mode 100644 index 6a66aea5..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/PATENTS b/backend/vendor/github.com/didip/tollbooth/v8/internal/time/PATENTS deleted file mode 100644 index 73309904..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/rate/rate.go b/backend/vendor/github.com/didip/tollbooth/v8/internal/time/rate/rate.go deleted file mode 100644 index 6c3b442d..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/internal/time/rate/rate.go +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package rate provides a rate limiter. -package rate - -import ( - "context" - "fmt" - "math" - "sync" - "time" -) - -// Limit defines the maximum frequency of some events. -// Limit is represented as number of events per second. -// A zero Limit allows no events. -type Limit float64 - -// Inf is the infinite rate limit; it allows all events (even if burst is zero). -const Inf = Limit(math.MaxFloat64) - -// Every converts a minimum time interval between events to a Limit. -func Every(interval time.Duration) Limit { - if interval <= 0 { - return Inf - } - return 1 / Limit(interval.Seconds()) -} - -// A Limiter controls how frequently events are allowed to happen. -// It implements a "token bucket" of size b, initially full and refilled -// at rate r tokens per second. -// Informally, in any large enough time interval, the Limiter limits the -// rate to r tokens per second, with a maximum burst size of b events. -// As a special case, if r == Inf (the infinite rate), b is ignored. -// See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets. -// -// The zero value is a valid Limiter, but it will reject all events. -// Use NewLimiter to create non-zero Limiters. -// -// Limiter has three main methods, Allow, Reserve, and Wait. -// Most callers should use Wait. -// -// Each of the three methods consumes a single token. -// They differ in their behavior when no token is available. -// If no token is available, Allow returns false. -// If no token is available, Reserve returns a reservation for a future token -// and the amount of time the caller must wait before using it. -// If no token is available, Wait blocks until one can be obtained -// or its associated context.Context is canceled. -// -// The methods AllowN, ReserveN, and WaitN consume n tokens. -type Limiter struct { - mu sync.Mutex - limit Limit - burst int - tokens float64 - // last is the last time the limiter's tokens field was updated - last time.Time - // lastEvent is the latest time of a rate-limited event (past or future) - lastEvent time.Time -} - -// Limit returns the maximum overall event rate. -func (lim *Limiter) Limit() Limit { - lim.mu.Lock() - defer lim.mu.Unlock() - return lim.limit -} - -// Burst returns the maximum burst size. Burst is the maximum number of tokens -// that can be consumed in a single call to Allow, Reserve, or Wait, so higher -// Burst values allow more events to happen at once. -// A zero Burst allows no events, unless limit == Inf. -func (lim *Limiter) Burst() int { - lim.mu.Lock() - defer lim.mu.Unlock() - return lim.burst -} - -// NewLimiter returns a new Limiter that allows events up to rate r and permits -// bursts of at most b tokens. -func NewLimiter(r Limit, b int) *Limiter { - return &Limiter{ - limit: r, - burst: b, - } -} - -// Allow is shorthand for AllowN(time.Now(), 1). -func (lim *Limiter) Allow() bool { - return lim.AllowN(time.Now(), 1) -} - -// TokensAt returns the number of tokens available for the given time. -func (lim *Limiter) TokensAt(t time.Time) float64 { - lim.mu.Lock() - _, _, tokens := lim.advance(t) // does not mutate lim - lim.mu.Unlock() - return tokens -} - -// AllowN reports whether n events may happen at time now. -// Use this method if you intend to drop / skip events that exceed the rate limit. -// Otherwise use Reserve or Wait. -func (lim *Limiter) AllowN(now time.Time, n int) bool { - return lim.reserveN(now, n, 0).ok -} - -// A Reservation holds information about events that are permitted by a Limiter to happen after a delay. -// A Reservation may be canceled, which may enable the Limiter to permit additional events. -type Reservation struct { - ok bool - lim *Limiter - tokens int - timeToAct time.Time - // This is the Limit at reservation time, it can change later. - limit Limit -} - -// OK returns whether the limiter can provide the requested number of tokens -// within the maximum wait time. If OK is false, Delay returns InfDuration, and -// Cancel does nothing. -func (r *Reservation) OK() bool { - return r.ok -} - -// Delay is shorthand for DelayFrom(time.Now()). -func (r *Reservation) Delay() time.Duration { - return r.DelayFrom(time.Now()) -} - -// InfDuration is the duration returned by Delay when a Reservation is not OK. -const InfDuration = time.Duration(1<<63 - 1) - -// DelayFrom returns the duration for which the reservation holder must wait -// before taking the reserved action. Zero duration means act immediately. -// InfDuration means the limiter cannot grant the tokens requested in this -// Reservation within the maximum wait time. -func (r *Reservation) DelayFrom(now time.Time) time.Duration { - if !r.ok { - return InfDuration - } - delay := r.timeToAct.Sub(now) - if delay < 0 { - return 0 - } - return delay -} - -// Cancel is shorthand for CancelAt(time.Now()). -func (r *Reservation) Cancel() { - r.CancelAt(time.Now()) -} - -// CancelAt indicates that the reservation holder will not perform the reserved action -// and reverses the effects of this Reservation on the rate limit as much as possible, -// considering that other reservations may have already been made. -func (r *Reservation) CancelAt(now time.Time) { - if !r.ok { - return - } - - r.lim.mu.Lock() - defer r.lim.mu.Unlock() - - if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) { - return - } - - // calculate tokens to restore - // The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved - // after r was obtained. These tokens should not be restored. - restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct)) - if restoreTokens <= 0 { - return - } - // advance time to now - now, _, tokens := r.lim.advance(now) - // calculate new number of tokens - tokens += restoreTokens - if burst := float64(r.lim.burst); tokens > burst { - tokens = burst - } - // update state - r.lim.last = now - r.lim.tokens = tokens - if r.timeToAct == r.lim.lastEvent { - prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens))) - if !prevEvent.Before(now) { - r.lim.lastEvent = prevEvent - } - } -} - -// Reserve is shorthand for ReserveN(time.Now(), 1). -func (lim *Limiter) Reserve() *Reservation { - return lim.ReserveN(time.Now(), 1) -} - -// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen. -// The Limiter takes this Reservation into account when allowing future events. -// The returned Reservation’s OK() method returns false if n exceeds the Limiter's burst size. -// Usage example: -// r := lim.ReserveN(time.Now(), 1) -// if !r.OK() { -// // Not allowed to act! Did you remember to set lim.burst to be > 0 ? -// return -// } -// time.Sleep(r.Delay()) -// Act() -// Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events. -// If you need to respect a deadline or cancel the delay, use Wait instead. -// To drop or skip events exceeding rate limit, use Allow instead. -func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation { - r := lim.reserveN(now, n, InfDuration) - return &r -} - -// Wait is shorthand for WaitN(ctx, 1). -func (lim *Limiter) Wait(ctx context.Context) (err error) { - return lim.WaitN(ctx, 1) -} - -// WaitN blocks until lim permits n events to happen. -// It returns an error if n exceeds the Limiter's burst size, the Context is -// canceled, or the expected wait time exceeds the Context's Deadline. -// The burst limit is ignored if the rate limit is Inf. -func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) { - lim.mu.Lock() - burst := lim.burst - limit := lim.limit - lim.mu.Unlock() - - if n > burst && limit != Inf { - return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, burst) - } - // Check if ctx is already cancelled - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - // Determine wait limit - now := time.Now() - waitLimit := InfDuration - if deadline, ok := ctx.Deadline(); ok { - waitLimit = deadline.Sub(now) - } - // Reserve - r := lim.reserveN(now, n, waitLimit) - if !r.ok { - return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n) - } - // Wait if necessary - delay := r.DelayFrom(now) - if delay == 0 { - return nil - } - t := time.NewTimer(delay) - defer t.Stop() - select { - case <-t.C: - // We can proceed. - return nil - case <-ctx.Done(): - // Context was canceled before we could proceed. Cancel the - // reservation, which may permit other events to proceed sooner. - r.Cancel() - return ctx.Err() - } -} - -// SetLimit is shorthand for SetLimitAt(time.Now(), newLimit). -func (lim *Limiter) SetLimit(newLimit Limit) { - lim.SetLimitAt(time.Now(), newLimit) -} - -// SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated -// or underutilized by those which reserved (using Reserve or Wait) but did not yet act -// before SetLimitAt was called. -func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) { - lim.mu.Lock() - defer lim.mu.Unlock() - - now, _, tokens := lim.advance(now) - - lim.last = now - lim.tokens = tokens - lim.limit = newLimit -} - -// SetBurst is shorthand for SetBurstAt(time.Now(), newBurst). -func (lim *Limiter) SetBurst(newBurst int) { - lim.SetBurstAt(time.Now(), newBurst) -} - -// SetBurstAt sets a new burst size for the limiter. -func (lim *Limiter) SetBurstAt(now time.Time, newBurst int) { - lim.mu.Lock() - defer lim.mu.Unlock() - - now, _, tokens := lim.advance(now) - - lim.last = now - lim.tokens = tokens - lim.burst = newBurst -} - -// reserveN is a helper method for AllowN, ReserveN, and WaitN. -// maxFutureReserve specifies the maximum reservation wait duration allowed. -// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN. -func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation { - lim.mu.Lock() - - if lim.limit == Inf { - lim.mu.Unlock() - return Reservation{ - ok: true, - lim: lim, - tokens: n, - timeToAct: now, - } - } - - now, last, tokens := lim.advance(now) - - // Calculate the remaining number of tokens resulting from the request. - tokens -= float64(n) - - // Calculate the wait duration - var waitDuration time.Duration - if tokens < 0 { - waitDuration = lim.limit.durationFromTokens(-tokens) - } - - // Decide result - ok := n <= lim.burst && waitDuration <= maxFutureReserve - - // Prepare reservation - r := Reservation{ - ok: ok, - lim: lim, - limit: lim.limit, - } - if ok { - r.tokens = n - r.timeToAct = now.Add(waitDuration) - } - - // Update state - if ok { - lim.last = now - lim.tokens = tokens - lim.lastEvent = r.timeToAct - } else { - lim.last = last - } - - lim.mu.Unlock() - return r -} - -// advance calculates and returns an updated state for lim resulting from the passage of time. -// lim is not changed. -// advance requires that lim.mu is held. -func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) { - last := lim.last - if now.Before(last) { - last = now - } - - // Calculate the new number of tokens, due to time that passed. - elapsed := now.Sub(last) - delta := lim.limit.tokensFromDuration(elapsed) - tokens := lim.tokens + delta - if burst := float64(lim.burst); tokens > burst { - tokens = burst - } - return now, last, tokens -} - -// durationFromTokens is a unit conversion function from the number of tokens to the duration -// of time it takes to accumulate them at a rate of limit tokens per second. -func (limit Limit) durationFromTokens(tokens float64) time.Duration { - seconds := tokens / float64(limit) - return time.Duration(float64(time.Second) * seconds) -} - -// tokensFromDuration is a unit conversion function from a time duration to the number of tokens -// which could be accumulated during that duration at a rate of limit tokens per second. -func (limit Limit) tokensFromDuration(d time.Duration) float64 { - return d.Seconds() * float64(limit) -} diff --git a/backend/vendor/github.com/didip/tollbooth/v8/libstring/libstring.go b/backend/vendor/github.com/didip/tollbooth/v8/libstring/libstring.go deleted file mode 100644 index 5ca875f1..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/libstring/libstring.go +++ /dev/null @@ -1,100 +0,0 @@ -// Package libstring provides various string related functions. -package libstring - -import ( - "net" - "net/http" - "strings" - - "github.com/didip/tollbooth/v8/limiter" -) - -// StringInSlice finds needle in a slice of strings. -func StringInSlice(sliceString []string, needle string) bool { - for _, b := range sliceString { - if b == needle { - return true - } - } - return false -} - -// RemoteIPFromIPLookup picks an ip address explicitly from limiter.IPLookup criteria. -// This function is intended to replace RemoteIP function. -func RemoteIPFromIPLookup(ipLookup limiter.IPLookup, r *http.Request) string { - switch ipLookup.Name { - case "RemoteAddr": - // 1. Cover the basic use cases for both ipv4 and ipv6 - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - // 2. Upon error, just return the remote addr. - return r.RemoteAddr - } - return ip - - case "X-Forwarded-For", "X-Real-IP", "CF-Connecting-IP": - ipAddrListCommaSeparated := r.Header.Values(ipLookup.Name) - - ipAddrCommaSeparated := strings.Join(ipAddrListCommaSeparated, ",") - - ips := strings.Split(ipAddrCommaSeparated, ",") - for i, p := range ips { - ips[i] = strings.TrimSpace(p) - } - - ipIndex := len(ips) - 1 - ipLookup.IndexFromRight - if ipIndex < 0 { - ipIndex = 0 - } - - return ips[ipIndex] - } - - return "" -} - -// CanonicalizeIP returns a form of ip suitable for comparison to other IPs. -// For IPv4 addresses, this is simply the whole string. -// For IPv6 addresses, this is the /64 prefix. -func CanonicalizeIP(ip string) string { - isIPv6 := false - // This is how net.ParseIP decides if an address is IPv6 - // https://cs.opensource.google/go/go/+/refs/tags/go1.17.7:src/net/ip.go;l=704 - for i := 0; !isIPv6 && i < len(ip); i++ { - switch ip[i] { - case '.': - // IPv4 - return ip - case ':': - // IPv6 - isIPv6 = true - } - } - if !isIPv6 { - // Not an IP address at all - return ip - } - - // By default, the string representation of a net.IPNet (masked IP address) is just - // "full_address/mask_bits". But using that will result in different addresses with - // the same /64 prefix comparing differently. So we need to zero out the last 64 bits - // so that all IPs in the same prefix will be the same. - // - // Note: When 1.18 is the minimum Go version, this can be written more cleanly like: - // netip.PrefixFrom(netip.MustParseAddr(ipv6), 64).Masked().Addr().String() - // (With appropriate error checking.) - - ipv6 := net.ParseIP(ip) - if ipv6 == nil { - return ip - } - - const bytesToZero = (128 - 64) / 8 - for i := len(ipv6) - bytesToZero; i < len(ipv6); i++ { - ipv6[i] = 0 - } - - // Note that this doesn't have the "/64" suffix customary with a CIDR representation, - // but those three bytes add nothing for us. - return ipv6.String() -} diff --git a/backend/vendor/github.com/didip/tollbooth/v8/limiter/limiter.go b/backend/vendor/github.com/didip/tollbooth/v8/limiter/limiter.go deleted file mode 100644 index b5b56d6f..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/limiter/limiter.go +++ /dev/null @@ -1,624 +0,0 @@ -// Package limiter provides data structure to configure rate-limiter. -package limiter - -import ( - "net/http" - "sync" - "time" - - cache "github.com/go-pkgz/expirable-cache/v3" - - "github.com/didip/tollbooth/v8/internal/time/rate" -) - -// New is a constructor for Limiter. -func New(generalExpirableOptions *ExpirableOptions) *Limiter { - lmt := &Limiter{} - - lmt.SetMessageContentType("text/plain; charset=utf-8"). - SetMessage("You have reached maximum request limit."). - SetStatusCode(429). - SetOnLimitReached(nil). - SetForwardedForIndexFromBehind(0). - SetHeaders(make(map[string][]string)). - SetContextValues(make(map[string][]string)). - SetIgnoreURL(false) - - if generalExpirableOptions != nil { - lmt.generalExpirableOptions = generalExpirableOptions - } else { - lmt.generalExpirableOptions = &ExpirableOptions{} - } - - // Default for DefaultExpirationTTL is 10 years. - if lmt.generalExpirableOptions.DefaultExpirationTTL <= 0 { - lmt.generalExpirableOptions.DefaultExpirationTTL = 87600 * time.Hour - } - - lmt.tokenBuckets = cache.NewCache[string, *rate.Limiter]().WithTTL(lmt.generalExpirableOptions.DefaultExpirationTTL) - - lmt.basicAuthUsers = cache.NewCache[string, bool]().WithTTL(lmt.generalExpirableOptions.DefaultExpirationTTL) - - return lmt -} - -// IPLookup is a config struct to define how users want to pick the remote IP address. -type IPLookup struct { - // The name of lookup method. - // Possible options are: RemoteAddr, X-Forwarded-For, X-Real-IP, CF-Connecting-IP - // All other headers are considered unknown and will be ignored. - Name string - - // The index position to pick the ip address from a comma separated list. - // The index goes from right to left. - IndexFromRight int -} - -// Limiter is a config struct to limit a particular request handler. -type Limiter struct { - // Maximum number of requests to limit per second. - max float64 - - // Limiter burst size - burst int - - // HTTP message when limit is reached. - message string - - // Content-Type for Message - messageContentType string - - // HTTP status code when limit is reached. - statusCode int - - // A function to call when a request is rejected. - onLimitReached func(w http.ResponseWriter, r *http.Request) - - // An option to write back what you want upon reaching a limit. - overrideDefaultResponseWriter bool - - // Explicitly define how to look up IP address. - // This is intended to replace ipLookups - explicitIPLookup IPLookup - - forwardedForIndex int - - // List of HTTP Methods to limit (GET, POST, PUT, etc.). - // Empty means limit all methods. - methods []string - - // Able to configure token bucket expirations. - generalExpirableOptions *ExpirableOptions - - // List of basic auth usernames to limit. - basicAuthUsers cache.Cache[string, bool] - - // Map of HTTP headers to limit. - // Empty means skip headers checking. - headers map[string]cache.Cache[string, bool] - - // Map of Context values to limit. - contextValues map[string]cache.Cache[string, bool] - - // Map of limiters with TTL - tokenBuckets cache.Cache[string, *rate.Limiter] - - // Ignore URL on the rate limiter keys - ignoreURL bool - - tokenBucketExpirationTTL time.Duration - basicAuthExpirationTTL time.Duration - headerEntryExpirationTTL time.Duration - contextEntryExpirationTTL time.Duration - - sync.RWMutex -} - -// SetTokenBucketExpirationTTL is thread-safe way of setting custom token bucket expiration TTL. -func (l *Limiter) SetTokenBucketExpirationTTL(ttl time.Duration) *Limiter { - l.Lock() - l.tokenBucketExpirationTTL = ttl - l.Unlock() - - return l -} - -// GetTokenBucketExpirationTTL is thread-safe way of getting custom token bucket expiration TTL. -func (l *Limiter) GetTokenBucketExpirationTTL() time.Duration { - l.RLock() - defer l.RUnlock() - return l.tokenBucketExpirationTTL -} - -// SetBasicAuthExpirationTTL is thread-safe way of setting custom basic auth expiration TTL. -func (l *Limiter) SetBasicAuthExpirationTTL(ttl time.Duration) *Limiter { - l.Lock() - l.basicAuthExpirationTTL = ttl - l.Unlock() - - return l -} - -// GetBasicAuthExpirationTTL is thread-safe way of getting custom basic auth expiration TTL. -func (l *Limiter) GetBasicAuthExpirationTTL() time.Duration { - l.RLock() - defer l.RUnlock() - return l.basicAuthExpirationTTL -} - -// SetHeaderEntryExpirationTTL is thread-safe way of setting custom basic auth expiration TTL. -func (l *Limiter) SetHeaderEntryExpirationTTL(ttl time.Duration) *Limiter { - l.Lock() - l.headerEntryExpirationTTL = ttl - l.Unlock() - - return l -} - -// GetHeaderEntryExpirationTTL is thread-safe way of getting custom basic auth expiration TTL. -func (l *Limiter) GetHeaderEntryExpirationTTL() time.Duration { - l.RLock() - defer l.RUnlock() - return l.headerEntryExpirationTTL -} - -// SetContextValueEntryExpirationTTL is thread-safe way of setting custom Context value expiration TTL. -func (l *Limiter) SetContextValueEntryExpirationTTL(ttl time.Duration) *Limiter { - l.Lock() - l.contextEntryExpirationTTL = ttl - l.Unlock() - - return l -} - -// GetContextValueEntryExpirationTTL is thread-safe way of getting custom Context value expiration TTL. -func (l *Limiter) GetContextValueEntryExpirationTTL() time.Duration { - l.RLock() - defer l.RUnlock() - return l.contextEntryExpirationTTL -} - -// SetMax is thread-safe way of setting maximum number of requests to limit per second. -func (l *Limiter) SetMax(max float64) *Limiter { - l.Lock() - l.max = max - l.Unlock() - - return l -} - -// GetMax is thread-safe way of getting maximum number of requests to limit per second. -func (l *Limiter) GetMax() float64 { - l.RLock() - defer l.RUnlock() - return l.max -} - -// SetBurst is thread-safe way of setting maximum burst size. -func (l *Limiter) SetBurst(burst int) *Limiter { - l.Lock() - l.burst = burst - l.Unlock() - - return l -} - -// GetBurst is thread-safe way of setting maximum burst size. -func (l *Limiter) GetBurst() int { - l.RLock() - defer l.RUnlock() - - return l.burst -} - -// SetMessage is thread-safe way of setting HTTP message when limit is reached. -func (l *Limiter) SetMessage(msg string) *Limiter { - l.Lock() - l.message = msg - l.Unlock() - - return l -} - -// GetMessage is thread-safe way of getting HTTP message when limit is reached. -func (l *Limiter) GetMessage() string { - l.RLock() - defer l.RUnlock() - return l.message -} - -// SetMessageContentType is thread-safe way of setting HTTP message Content-Type when limit is reached. -func (l *Limiter) SetMessageContentType(contentType string) *Limiter { - l.Lock() - l.messageContentType = contentType - l.Unlock() - - return l -} - -// GetMessageContentType is thread-safe way of getting HTTP message Content-Type when limit is reached. -func (l *Limiter) GetMessageContentType() string { - l.RLock() - defer l.RUnlock() - return l.messageContentType -} - -// SetStatusCode is thread-safe way of setting HTTP status code when limit is reached. -func (l *Limiter) SetStatusCode(statusCode int) *Limiter { - l.Lock() - l.statusCode = statusCode - l.Unlock() - - return l -} - -// GetStatusCode is thread-safe way of getting HTTP status code when limit is reached. -func (l *Limiter) GetStatusCode() int { - l.RLock() - defer l.RUnlock() - return l.statusCode -} - -// SetOnLimitReached is thread-safe way of setting after-rejection function when limit is reached. -func (l *Limiter) SetOnLimitReached(fn func(w http.ResponseWriter, r *http.Request)) *Limiter { - l.Lock() - l.onLimitReached = fn - l.Unlock() - - return l -} - -// ExecOnLimitReached is thread-safe way of executing after-rejection function when limit is reached. -func (l *Limiter) ExecOnLimitReached(w http.ResponseWriter, r *http.Request) { - l.RLock() - fn := l.onLimitReached - l.RUnlock() - - if fn != nil { - fn(w, r) - } -} - -// SetOverrideDefaultResponseWriter is a thread-safe way of setting the response writer override variable. -func (l *Limiter) SetOverrideDefaultResponseWriter(override bool) *Limiter { - l.Lock() - l.overrideDefaultResponseWriter = override - l.Unlock() - - return l -} - -// GetOverrideDefaultResponseWriter is a thread-safe way of getting the response writer override variable. -func (l *Limiter) GetOverrideDefaultResponseWriter() bool { - l.RLock() - defer l.RUnlock() - return l.overrideDefaultResponseWriter -} - -// SetIPLookup is thread-safe way of setting an explicit way to look up IP address. -// This method is intended to replace SetIPLookups (version 6 or older). -func (l *Limiter) SetIPLookup(lookup IPLookup) *Limiter { - l.Lock() - l.explicitIPLookup = lookup - l.Unlock() - - return l -} - -// GetIPLookup is thread-safe way of getting an explicit way to look up IP address. -// This method is intended to replace the old GetIPLookups (version 6 or older). -func (l *Limiter) GetIPLookup() IPLookup { - l.RLock() - defer l.RUnlock() - return l.explicitIPLookup -} - -// SetIgnoreURL is thread-safe way of setting whenever ignore the URL on rate limit keys -func (l *Limiter) SetIgnoreURL(enabled bool) *Limiter { - l.Lock() - l.ignoreURL = enabled - l.Unlock() - - return l -} - -// GetIgnoreURL returns whether the URL is ignored in the rate limit key set -func (l *Limiter) GetIgnoreURL() bool { - l.RLock() - defer l.RUnlock() - return l.ignoreURL -} - -// SetForwardedForIndexFromBehind is thread-safe way of setting which X-Forwarded-For index to choose. -func (l *Limiter) SetForwardedForIndexFromBehind(forwardedForIndex int) *Limiter { - l.Lock() - l.forwardedForIndex = forwardedForIndex - l.Unlock() - - return l -} - -// GetForwardedForIndexFromBehind is thread-safe way of getting which X-Forwarded-For index to choose. -func (l *Limiter) GetForwardedForIndexFromBehind() int { - l.RLock() - defer l.RUnlock() - return l.forwardedForIndex -} - -// SetMethods is thread-safe way of setting list of HTTP Methods to limit (GET, POST, PUT, etc.). -func (l *Limiter) SetMethods(methods []string) *Limiter { - l.Lock() - l.methods = methods - l.Unlock() - - return l -} - -// GetMethods is thread-safe way of getting list of HTTP Methods to limit (GET, POST, PUT, etc.). -func (l *Limiter) GetMethods() []string { - l.RLock() - defer l.RUnlock() - return l.methods -} - -// SetBasicAuthUsers is thread-safe way of setting list of basic auth usernames to limit. -func (l *Limiter) SetBasicAuthUsers(basicAuthUsers []string) *Limiter { - ttl := l.GetBasicAuthExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - for _, basicAuthUser := range basicAuthUsers { - l.basicAuthUsers.Set(basicAuthUser, true, ttl) - } - - return l -} - -// GetBasicAuthUsers is thread-safe way of getting list of basic auth usernames to limit. -func (l *Limiter) GetBasicAuthUsers() []string { - return l.basicAuthUsers.Keys() -} - -// RemoveBasicAuthUsers is thread-safe way of removing basic auth usernames from existing list. -func (l *Limiter) RemoveBasicAuthUsers(basicAuthUsers []string) *Limiter { - for _, toBeRemoved := range basicAuthUsers { - l.basicAuthUsers.Invalidate(toBeRemoved) - } - - return l -} - -// DeleteExpiredTokenBuckets is thread-safe way of deleting expired token buckets -func (l *Limiter) DeleteExpiredTokenBuckets() { - l.tokenBuckets.DeleteExpired() -} - -// SetHeaders is thread-safe way of setting map of HTTP headers to limit. -func (l *Limiter) SetHeaders(headers map[string][]string) *Limiter { - if l.headers == nil { - l.headers = make(map[string]cache.Cache[string, bool]) - } - - for header, entries := range headers { - l.SetHeader(header, entries) - } - - return l -} - -// GetHeaders is thread-safe way of getting map of HTTP headers to limit. -func (l *Limiter) GetHeaders() map[string][]string { - results := make(map[string][]string) - - l.RLock() - defer l.RUnlock() - - for header, entriesAsGoCache := range l.headers { - results[header] = entriesAsGoCache.Keys() - } - - return results -} - -// SetHeader is thread-safe way of setting entries of 1 HTTP header. -func (l *Limiter) SetHeader(header string, entries []string) *Limiter { - l.RLock() - existing, found := l.headers[header] - l.RUnlock() - - ttl := l.GetHeaderEntryExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - if !found { - existing = cache.NewCache[string, bool]().WithTTL(ttl) - } - - for _, entry := range entries { - existing.Set(entry, true, ttl) - } - - l.Lock() - l.headers[header] = existing - l.Unlock() - - return l -} - -// GetHeader is thread-safe way of getting entries of 1 HTTP header. -func (l *Limiter) GetHeader(header string) []string { - l.RLock() - entriesAsGoCache := l.headers[header] - l.RUnlock() - - return entriesAsGoCache.Keys() -} - -// RemoveHeader is thread-safe way of removing entries of 1 HTTP header. -func (l *Limiter) RemoveHeader(header string) *Limiter { - ttl := l.GetHeaderEntryExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - l.Lock() - l.headers[header] = cache.NewCache[string, bool]().WithTTL(ttl) - l.Unlock() - - return l -} - -// RemoveHeaderEntries is thread-safe way of removing new entries to 1 HTTP header rule. -func (l *Limiter) RemoveHeaderEntries(header string, entriesForRemoval []string) *Limiter { - l.RLock() - entries, found := l.headers[header] - l.RUnlock() - - if !found { - return l - } - - for _, toBeRemoved := range entriesForRemoval { - entries.Invalidate(toBeRemoved) - } - - return l -} - -// SetContextValues is thread-safe way of setting map of HTTP headers to limit. -func (l *Limiter) SetContextValues(contextValues map[string][]string) *Limiter { - if l.contextValues == nil { - l.contextValues = make(map[string]cache.Cache[string, bool]) - } - - for contextValue, entries := range contextValues { - l.SetContextValue(contextValue, entries) - } - - return l -} - -// GetContextValues is thread-safe way of getting a map of Context values to limit. -func (l *Limiter) GetContextValues() map[string][]string { - results := make(map[string][]string) - - l.RLock() - defer l.RUnlock() - - for contextValue, entriesAsGoCache := range l.contextValues { - results[contextValue] = entriesAsGoCache.Keys() - } - - return results -} - -// SetContextValue is thread-safe way of setting entries of 1 Context value. -func (l *Limiter) SetContextValue(contextValue string, entries []string) *Limiter { - l.RLock() - existing, found := l.contextValues[contextValue] - l.RUnlock() - - ttl := l.GetContextValueEntryExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - if !found { - existing = cache.NewCache[string, bool]().WithTTL(ttl) - } - - for _, entry := range entries { - existing.Set(entry, true, ttl) - } - - l.Lock() - l.contextValues[contextValue] = existing - l.Unlock() - - return l -} - -// GetContextValue is thread-safe way of getting 1 Context value entry. -func (l *Limiter) GetContextValue(contextValue string) []string { - l.RLock() - entriesAsGoCache := l.contextValues[contextValue] - l.RUnlock() - - return entriesAsGoCache.Keys() -} - -// RemoveContextValue is thread-safe way of removing entries of 1 Context value. -func (l *Limiter) RemoveContextValue(contextValue string) *Limiter { - ttl := l.GetContextValueEntryExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - l.Lock() - l.contextValues[contextValue] = cache.NewCache[string, bool]().WithTTL(ttl) - l.Unlock() - - return l -} - -// RemoveContextValuesEntries is thread-safe way of removing entries to a ContextValue. -func (l *Limiter) RemoveContextValuesEntries(contextValue string, entriesForRemoval []string) *Limiter { - l.RLock() - entries, found := l.contextValues[contextValue] - l.RUnlock() - - if !found { - return l - } - - for _, toBeRemoved := range entriesForRemoval { - entries.Invalidate(toBeRemoved) - } - - return l -} - -func (l *Limiter) limitReachedWithTokenBucketTTL(key string, tokenBucketTTL time.Duration) bool { - lmtMax := l.GetMax() - lmtBurst := l.GetBurst() - l.Lock() - defer l.Unlock() - - if _, found := l.tokenBuckets.Get(key); !found { - l.tokenBuckets.Set( - key, - rate.NewLimiter(rate.Limit(lmtMax), lmtBurst), - tokenBucketTTL, - ) - } - - expiringMap, found := l.tokenBuckets.Get(key) - if !found { - return false - } - - return !expiringMap.Allow() -} - -// LimitReached returns a bool indicating if the Bucket identified by key ran out of tokens. -func (l *Limiter) LimitReached(key string) bool { - ttl := l.GetTokenBucketExpirationTTL() - - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - return l.limitReachedWithTokenBucketTTL(key, ttl) -} - -// Tokens returns current amount of tokens left in the Bucket identified by key. -func (l *Limiter) Tokens(key string) int { - expiringMap, found := l.tokenBuckets.Get(key) - if !found { - return 0 - } - - return int(expiringMap.TokensAt(time.Now())) -} diff --git a/backend/vendor/github.com/didip/tollbooth/v8/limiter/limiter_options.go b/backend/vendor/github.com/didip/tollbooth/v8/limiter/limiter_options.go deleted file mode 100644 index e5f537b5..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/limiter/limiter_options.go +++ /dev/null @@ -1,14 +0,0 @@ -package limiter - -import ( - "time" -) - -// ExpirableOptions are options used for new limiter creation -type ExpirableOptions struct { - DefaultExpirationTTL time.Duration - - // How frequently expire job triggers - // Deprecated: not used anymore - ExpireJobInterval time.Duration -} diff --git a/backend/vendor/github.com/didip/tollbooth/v8/tollbooth.go b/backend/vendor/github.com/didip/tollbooth/v8/tollbooth.go deleted file mode 100644 index 27e008c4..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v8/tollbooth.go +++ /dev/null @@ -1,375 +0,0 @@ -// Package tollbooth provides rate-limiting logic to HTTP request handler. -package tollbooth - -import ( - "fmt" - "math" - "net/http" - "strings" - - "github.com/didip/tollbooth/v8/errors" - "github.com/didip/tollbooth/v8/libstring" - "github.com/didip/tollbooth/v8/limiter" -) - -// setResponseHeaders configures X-Rate-Limit-Limit and X-Rate-Limit-Duration -func setResponseHeaders(lmt *limiter.Limiter, w http.ResponseWriter, r *http.Request) { - w.Header().Add("X-Rate-Limit-Limit", fmt.Sprintf("%.2f", lmt.GetMax())) - w.Header().Add("X-Rate-Limit-Duration", "1") - - xForwardedFor := r.Header.Get("X-Forwarded-For") - if strings.TrimSpace(xForwardedFor) != "" { - w.Header().Add("X-Rate-Limit-Request-Forwarded-For", xForwardedFor) - } - - w.Header().Add("X-Rate-Limit-Request-Remote-Addr", r.RemoteAddr) -} - -// setRateLimitResponseHeaders configures RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset -// as seen at https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers -func setRateLimitResponseHeaders(lmt *limiter.Limiter, w http.ResponseWriter, tokensLeft int) { - w.Header().Add("RateLimit-Limit", fmt.Sprintf("%d", int(math.Round(lmt.GetMax())))) - w.Header().Add("RateLimit-Reset", "1") - w.Header().Add("RateLimit-Remaining", fmt.Sprintf("%d", tokensLeft)) -} - -// NewLimiter is a convenience function to limiter.New. -func NewLimiter(max float64, tbOptions *limiter.ExpirableOptions) *limiter.Limiter { - return limiter.New(tbOptions). - SetMax(max). - SetBurst(int(math.Max(1, max))) -} - -// LimitByKeys keeps track number of request made by keys separated by pipe. -// It returns HTTPError when limit is exceeded. -func LimitByKeys(lmt *limiter.Limiter, keys []string) *errors.HTTPError { - err, _ := LimitByKeysAndReturn(lmt, keys) - return err -} - -// LimitByKeysAndReturn keeps track number of request made by keys separated by pipe. -// It returns HTTPError when limit is exceeded, and also returns the current limit value. -func LimitByKeysAndReturn(lmt *limiter.Limiter, keys []string) (*errors.HTTPError, int) { - if lmt.LimitReached(strings.Join(keys, "|")) { - return &errors.HTTPError{Message: lmt.GetMessage(), StatusCode: lmt.GetStatusCode()}, 0 - } - - return nil, lmt.Tokens(strings.Join(keys, "|")) -} - -// ShouldSkipLimiter is a series of filter that decides if request should be limited or not. -func ShouldSkipLimiter(lmt *limiter.Limiter, r *http.Request) bool { - // --------------------------------- - // Filter by remote ip - // If we are unable to find remoteIP, skip limiter - remoteIP := libstring.RemoteIPFromIPLookup(lmt.GetIPLookup(), r) - remoteIP = libstring.CanonicalizeIP(remoteIP) - if remoteIP == "" { - return true - } - - // --------------------------------- - // Filter by request method - lmtMethods := lmt.GetMethods() - lmtMethodsIsSet := len(lmtMethods) > 0 - - if lmtMethodsIsSet { - // If request does not contain all of the methods in limiter, - // skip limiter - requestMethodDefinedInLimiter := libstring.StringInSlice(lmtMethods, r.Method) - - if !requestMethodDefinedInLimiter { - return true - } - } - - // --------------------------------- - // Filter by request headers - lmtHeaders := lmt.GetHeaders() - lmtHeadersIsSet := len(lmtHeaders) > 0 - - if lmtHeadersIsSet { - // If request does not contain all of the headers in limiter, - // skip limiter - requestHeadersDefinedInLimiter := false - - for headerKey := range lmtHeaders { - reqHeaderValue := r.Header.Get(headerKey) - if reqHeaderValue != "" { - requestHeadersDefinedInLimiter = true - break - } - } - - if !requestHeadersDefinedInLimiter { - return true - } - - // ------------------------------ - // If request contains the header key but not the values, - // skip limiter - requestHeadersDefinedInLimiter = false - - for headerKey, headerValues := range lmtHeaders { - if len(headerValues) == 0 { - requestHeadersDefinedInLimiter = true - continue - } - for _, headerValue := range headerValues { - if r.Header.Get(headerKey) == headerValue { - requestHeadersDefinedInLimiter = true - break - } - } - } - - if !requestHeadersDefinedInLimiter { - return true - } - } - - // --------------------------------- - // Filter by context values - lmtContextValues := lmt.GetContextValues() - lmtContextValuesIsSet := len(lmtContextValues) > 0 - - if lmtContextValuesIsSet { - // If request does not contain all of the contexts in limiter, - // skip limiter - requestContextValuesDefinedInLimiter := false - - for contextKey := range lmtContextValues { - reqContextValue := fmt.Sprintf("%v", r.Context().Value(contextKey)) - if reqContextValue != "" { - requestContextValuesDefinedInLimiter = true - break - } - } - - if !requestContextValuesDefinedInLimiter { - return true - } - - // ------------------------------ - // If request contains the context key but not the values, - // skip limiter - requestContextValuesDefinedInLimiter = false - - for contextKey, contextValues := range lmtContextValues { - for _, contextValue := range contextValues { - if r.Header.Get(contextKey) == contextValue { - requestContextValuesDefinedInLimiter = true - break - } - } - } - - if !requestContextValuesDefinedInLimiter { - return true - } - } - - // --------------------------------- - // Filter by basic auth usernames - lmtBasicAuthUsers := lmt.GetBasicAuthUsers() - lmtBasicAuthUsersIsSet := len(lmtBasicAuthUsers) > 0 - - if lmtBasicAuthUsersIsSet { - // If request does not contain all of the basic auth users in limiter, - // skip limiter - requestAuthUsernameDefinedInLimiter := false - - username, _, ok := r.BasicAuth() - if ok && libstring.StringInSlice(lmtBasicAuthUsers, username) { - requestAuthUsernameDefinedInLimiter = true - } - - if !requestAuthUsernameDefinedInLimiter { - return true - } - } - - return false -} - -// BuildKeys generates a slice of keys to rate-limit by given limiter and request structs. -func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string { - remoteIP := libstring.RemoteIPFromIPLookup(lmt.GetIPLookup(), r) - remoteIP = libstring.CanonicalizeIP(remoteIP) - path := r.URL.Path - sliceKeys := make([][]string, 0) - - lmtMethods := lmt.GetMethods() - lmtHeaders := lmt.GetHeaders() - lmtContextValues := lmt.GetContextValues() - lmtBasicAuthUsers := lmt.GetBasicAuthUsers() - lmtIgnoreURL := lmt.GetIgnoreURL() - - lmtHeadersIsSet := len(lmtHeaders) > 0 - lmtContextValuesIsSet := len(lmtContextValues) > 0 - lmtBasicAuthUsersIsSet := len(lmtBasicAuthUsers) > 0 - - usernameToLimit := "" - if lmtBasicAuthUsersIsSet { - username, _, ok := r.BasicAuth() - if ok && libstring.StringInSlice(lmtBasicAuthUsers, username) { - usernameToLimit = username - } - } - - headerValuesToLimit := [][]string{} - if lmtHeadersIsSet { - for headerKey, headerValues := range lmtHeaders { - reqHeaderValue := r.Header.Get(headerKey) - if reqHeaderValue == "" { - continue - } - - if len(headerValues) == 0 { - // If header values are empty, rate-limit all request containing headerKey. - headerValuesToLimit = append(headerValuesToLimit, []string{headerKey, reqHeaderValue}) - - } else { - // If header values are not empty, rate-limit all request with headerKey and headerValues. - for _, headerValue := range headerValues { - if r.Header.Get(headerKey) == headerValue { - headerValuesToLimit = append(headerValuesToLimit, []string{headerKey, headerValue}) - break - } - } - } - } - } - - contextValuesToLimit := [][]string{} - if lmtContextValuesIsSet { - for contextKey, contextValues := range lmtContextValues { - reqContextValue := fmt.Sprintf("%v", r.Context().Value(contextKey)) - if reqContextValue == "" { - continue - } - - if len(contextValues) == 0 { - // If context values are empty, rate-limit all request containing contextKey. - contextValuesToLimit = append(contextValuesToLimit, []string{contextKey, reqContextValue}) - - } else { - // If context values are not empty, rate-limit all request with contextKey and contextValues. - for _, contextValue := range contextValues { - if reqContextValue == contextValue { - contextValuesToLimit = append(contextValuesToLimit, []string{contextKey, contextValue}) - break - } - } - } - } - } - - sliceKey := []string{remoteIP} - if !lmtIgnoreURL { - sliceKey = append(sliceKey, path) - } - - sliceKey = append(sliceKey, lmtMethods...) - - for _, header := range headerValuesToLimit { - sliceKey = append(sliceKey, header[0], header[1]) - } - - for _, contextValue := range contextValuesToLimit { - sliceKey = append(sliceKey, contextValue[0], contextValue[1]) - } - - sliceKey = append(sliceKey, usernameToLimit) - - sliceKeys = append(sliceKeys, sliceKey) - - return sliceKeys -} - -// LimitByRequest builds keys based on http.Request struct, -// loops through all the keys, and check if any one of them returns HTTPError. -func LimitByRequest(lmt *limiter.Limiter, w http.ResponseWriter, r *http.Request) *errors.HTTPError { - setResponseHeaders(lmt, w, r) - - shouldSkip := ShouldSkipLimiter(lmt, r) - if shouldSkip { - return nil - } - - sliceKeys := BuildKeys(lmt, r) - - // Get the lowest value over all keys to return in headers. - // Start with high arbitrary number so that any limit returned would be lower and would - // overwrite the value we start with. - var tokensLeft = math.MaxInt32 - - // Loop sliceKeys and check if one of them has error. - for _, keys := range sliceKeys { - httpError, keysLimit := LimitByKeysAndReturn(lmt, keys) - if tokensLeft > keysLimit { - tokensLeft = keysLimit - } - if httpError != nil { - setRateLimitResponseHeaders(lmt, w, tokensLeft) - return httpError - } - } - - setRateLimitResponseHeaders(lmt, w, tokensLeft) - return nil -} - -// LimitHandler is a middleware that performs rate-limiting given http.Handler struct. -func LimitHandler(lmt *limiter.Limiter, next http.Handler) http.Handler { - middle := func(w http.ResponseWriter, r *http.Request) { - httpError := LimitByRequest(lmt, w, r) - if httpError != nil { - lmt.ExecOnLimitReached(w, r) - if lmt.GetOverrideDefaultResponseWriter() { - return - } - w.Header().Add("Content-Type", lmt.GetMessageContentType()) - w.WriteHeader(httpError.StatusCode) - w.Write([]byte(httpError.Message)) - return - } - - // There's no rate-limit error, serve the next handler. - next.ServeHTTP(w, r) - } - - return http.HandlerFunc(middle) -} - -// LimitFuncHandler is a middleware that performs rate-limiting given request handler function. -func LimitFuncHandler(lmt *limiter.Limiter, nextFunc func(http.ResponseWriter, *http.Request)) http.Handler { - return LimitHandler(lmt, http.HandlerFunc(nextFunc)) -} - -// HTTPMiddleware wraps http.Handler with tollbooth limiter -func HTTPMiddleware(lmt *limiter.Limiter) func(http.Handler) http.Handler { - // // set IP lookup only if not set - if lmt.GetIPLookup().Name == "" { - lmt.SetIPLookup(limiter.IPLookup{Name: "RemoteAddr"}) - } - - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - select { - case <-r.Context().Done(): - http.Error(w, "Context was canceled", http.StatusServiceUnavailable) - return - default: - if httpError := LimitByRequest(lmt, w, r); httpError != nil { - lmt.ExecOnLimitReached(w, r) - w.Header().Add("Content-Type", lmt.GetMessageContentType()) - w.WriteHeader(httpError.StatusCode) - w.Write([]byte(httpError.Message)) //nolint:gosec // not much we can do here with failed write - return - } - next.ServeHTTP(w, r) - } - }) - } -} diff --git a/backend/vendor/github.com/dlclark/regexp2/fastclock.go b/backend/vendor/github.com/dlclark/regexp2/fastclock.go index caf2c9d8..d256e63c 100644 --- a/backend/vendor/github.com/dlclark/regexp2/fastclock.go +++ b/backend/vendor/github.com/dlclark/regexp2/fastclock.go @@ -50,8 +50,20 @@ func makeDeadline(d time.Duration) fasttime { // Start or extend clock if necessary. if end > fast.clockEnd.read() { + // If time.Since(last use) > timeout, there's a chance that + // fast.current will no longer be updated, which can lead to + // incorrect 'end' calculations that can trigger a false timeout + fast.mu.Lock() + if !fast.running && !fast.start.IsZero() { + // update fast.current + fast.current.write(durationToTicks(time.Since(fast.start))) + // recalculate our end value + end = fast.current.read() + durationToTicks(d+clockPeriod) + } + fast.mu.Unlock() extendClock(end) } + return end } diff --git a/backend/vendor/github.com/go-chi/chi/v5/README.md b/backend/vendor/github.com/go-chi/chi/v5/README.md index 7f662ab4..c58a0e20 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/README.md +++ b/backend/vendor/github.com/go-chi/chi/v5/README.md @@ -20,7 +20,9 @@ and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too! ## Install -`go get -u github.com/go-chi/chi/v5` +```sh +go get -u github.com/go-chi/chi/v5 +``` ## Features @@ -194,7 +196,7 @@ type Router interface { // path, with a fresh middleware stack for the inline-Router. Group(fn func(r Router)) Router - // Route mounts a sub-Router along a `pattern`` string. + // Route mounts a sub-Router along a `pattern` string. Route(pattern string, fn func(r Router)) Router // Mount attaches another http.Handler along ./pattern/* diff --git a/backend/vendor/github.com/go-chi/chi/v5/chi.go b/backend/vendor/github.com/go-chi/chi/v5/chi.go index fc32c4ef..2b6ebd33 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/chi.go +++ b/backend/vendor/github.com/go-chi/chi/v5/chi.go @@ -37,8 +37,7 @@ // // A placeholder with a name followed by a colon allows a regular // expression match, for example {number:\\d+}. The regular expression -// syntax is Go's normal regexp RE2 syntax, except that regular expressions -// including { or } are not supported, and / will never be +// syntax is Go's normal regexp RE2 syntax, except that / will never be // matched. An anonymous regexp pattern is allowed, using an empty string // before the colon in the placeholder, such as {:\\d+} // diff --git a/backend/vendor/github.com/go-chi/chi/v5/context.go b/backend/vendor/github.com/go-chi/chi/v5/context.go index aacf6eff..82220730 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/context.go +++ b/backend/vendor/github.com/go-chi/chi/v5/context.go @@ -133,11 +133,12 @@ func (x *Context) RoutePattern() string { return routePattern } -// replaceWildcards takes a route pattern and recursively replaces all -// occurrences of "/*/" to "/". +// replaceWildcards takes a route pattern and replaces all occurrences of +// "/*/" with "/". It iteratively runs until no wildcards remain to +// correctly handle consecutive wildcards. func replaceWildcards(p string) string { - if strings.Contains(p, "/*/") { - return replaceWildcards(strings.Replace(p, "/*/", "/", -1)) + for strings.Contains(p, "/*/") { + p = strings.ReplaceAll(p, "/*/", "/") } return p } diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go index 28240c4b..9c64bd48 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net" "net/http" "strings" @@ -160,16 +159,14 @@ func (c *Compressor) SetEncoder(encoding string, fn EncoderFunc) { delete(c.encoders, encoding) // If the encoder supports Resetting (IoReseterWriter), then it can be pooled. - encoder := fn(ioutil.Discard, c.level) - if encoder != nil { - if _, ok := encoder.(ioResetterWriter); ok { - pool := &sync.Pool{ - New: func() interface{} { - return fn(ioutil.Discard, c.level) - }, - } - c.pooledEncoders[encoding] = pool + encoder := fn(io.Discard, c.level) + if _, ok := encoder.(ioResetterWriter); ok { + pool := &sync.Pool{ + New: func() interface{} { + return fn(io.Discard, c.level) + }, } + c.pooledEncoders[encoding] = pool } // If the encoder is not in the pooledEncoders, add it to the normal encoders. if _, ok := c.pooledEncoders[encoding]; !ok { @@ -277,16 +274,13 @@ type compressResponseWriter struct { func (cw *compressResponseWriter) isCompressible() bool { // Parse the first part of the Content-Type response header. contentType := cw.Header().Get("Content-Type") - if idx := strings.Index(contentType, ";"); idx >= 0 { - contentType = contentType[0:idx] - } + contentType, _, _ = strings.Cut(contentType, ";") // Is the content type compressible? if _, ok := cw.contentTypes[contentType]; ok { return true } - if idx := strings.Index(contentType, "/"); idx > 0 { - contentType = contentType[0:idx] + if contentType, _, hadSlash := strings.Cut(contentType, "/"); hadSlash { _, ok := cw.contentWildcards[contentType] return ok } diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go index 07b5ce6f..07bff9f2 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go @@ -40,11 +40,10 @@ func contentEncoding(ce string, charsets ...string) bool { // Split a string in two parts, cleaning any whitespace. func split(str, sep string) (string, string) { - var a, b string - var parts = strings.SplitN(str, sep, 2) - a = strings.TrimSpace(parts[0]) - if len(parts) == 2 { - b = strings.TrimSpace(parts[1]) + a, b, found := strings.Cut(str, sep) + a = strings.TrimSpace(a) + if found { + b = strings.TrimSpace(b) } return a, b diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go index e61ff264..cdfc21ee 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go @@ -31,7 +31,8 @@ func AllowContentType(contentTypes ...string) func(http.Handler) http.Handler { return } - s := strings.ToLower(strings.TrimSpace(strings.Split(r.Header.Get("Content-Type"), ";")[0])) + s, _, _ := strings.Cut(r.Header.Get("Content-Type"), ";") + s = strings.ToLower(strings.TrimSpace(s)) if _, ok := allowedContentTypes[s]; ok { next.ServeHTTP(w, r) @@ -42,4 +43,3 @@ func AllowContentType(contentTypes ...string) func(http.Handler) http.Handler { }) } } - diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go index ebd81ee4..0ad6a996 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go @@ -1,3 +1,6 @@ +//go:build !tinygo +// +build !tinygo + package middleware import ( diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go index 55c95a89..afcb79e2 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go @@ -47,11 +47,7 @@ func realIP(r *http.Request) string { } else if xrip := r.Header.Get(xRealIP); xrip != "" { ip = xrip } else if xff := r.Header.Get(xForwardedFor); xff != "" { - i := strings.Index(xff, ",") - if i == -1 { - i = len(xff) - } - ip = xff[:i] + ip, _, _ = strings.Cut(xff, ",") } if ip == "" || net.ParseIP(ip) == nil { return "" diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go index 997bed56..88743769 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go @@ -133,13 +133,7 @@ type Pattern struct { func NewPattern(value string) Pattern { p := Pattern{} - if i := strings.IndexByte(value, '*'); i >= 0 { - p.wildcard = true - p.prefix = value[0:i] - p.suffix = value[i+1:] - } else { - p.prefix = value - } + p.prefix, p.suffix, p.wildcard = strings.Cut(value, "*") return p } diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go index 1368fa7a..17aa9bf3 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go @@ -3,6 +3,7 @@ package middleware import ( "fmt" "net/http" + "strings" "github.com/go-chi/chi/v5" ) @@ -47,13 +48,12 @@ func RedirectSlashes(next http.Handler) http.Handler { path = r.URL.Path } if len(path) > 1 && path[len(path)-1] == '/' { + // Trim all leading and trailing slashes (e.g., "//evil.com", "/some/path//") + path = "/" + strings.Trim(path, "/") if r.URL.RawQuery != "" { - path = fmt.Sprintf("%s?%s", path[:len(path)-1], r.URL.RawQuery) - } else { - path = path[:len(path)-1] + path = fmt.Sprintf("%s?%s", path, r.URL.RawQuery) } - redirectURL := fmt.Sprintf("//%s%s", r.Host, path) - http.Redirect(w, r, redirectURL, 301) + http.Redirect(w, r, path, 301) return } next.ServeHTTP(w, r) diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go index 9a870d88..7ea482b9 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go @@ -83,12 +83,23 @@ func ThrottleWithOpts(opts ThrottleOpts) func(http.Handler) http.Handler { return case btok := <-t.backlogTokens: - timer := time.NewTimer(t.backlogTimeout) - defer func() { t.backlogTokens <- btok }() + // Try to get a processing token immediately first + select { + case tok := <-t.tokens: + defer func() { + t.tokens <- tok + }() + next.ServeHTTP(w, r) + return + default: + // No immediate token available, need to wait with timer + } + + timer := time.NewTimer(t.backlogTimeout) select { case <-timer.C: t.setRetryAfterHeaderIfNeeded(w, false) @@ -126,9 +137,9 @@ type token struct{} type throttler struct { tokens chan token backlogTokens chan token + retryAfterFn func(ctxDone bool) time.Duration backlogTimeout time.Duration statusCode int - retryAfterFn func(ctxDone bool) time.Duration } // setRetryAfterHeaderIfNeeded sets Retry-After HTTP header if corresponding retryAfterFn option of throttler is initialized. diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go index d8a651e4..2ec6657e 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go @@ -63,7 +63,9 @@ func URLFormat(next http.Handler) http.Handler { idx += base format = path[idx+1:] - rctx.RoutePath = path[:idx] + if rctx != nil { + rctx.RoutePath = path[:idx] + } } } diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go index 12d4faf0..367e0fcd 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go +++ b/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go @@ -6,7 +6,6 @@ package middleware import ( "bufio" "io" - "io/ioutil" "net" "net/http" ) @@ -73,10 +72,10 @@ type WrapResponseWriter interface { // http.ResponseWriter interface. type basicWriter struct { http.ResponseWriter - wroteHeader bool + tee io.Writer code int bytes int - tee io.Writer + wroteHeader bool discard bool } @@ -108,7 +107,7 @@ func (b *basicWriter) Write(buf []byte) (n int, err error) { } else if b.tee != nil { n, err = b.tee.Write(buf) } else { - n, err = ioutil.Discard.Write(buf) + n, err = io.Discard.Write(buf) } b.bytes += n return n, err diff --git a/backend/vendor/github.com/go-chi/chi/v5/mux.go b/backend/vendor/github.com/go-chi/chi/v5/mux.go index 91daf691..ad66bba9 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/mux.go +++ b/backend/vendor/github.com/go-chi/chi/v5/mux.go @@ -107,9 +107,9 @@ func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) { // Handle adds the route `pattern` that matches any http method to // execute the `handler` http.Handler. func (mx *Mux) Handle(pattern string, handler http.Handler) { - parts := strings.SplitN(pattern, " ", 2) - if len(parts) == 2 { - mx.Method(parts[0], parts[1], handler) + if i := strings.IndexAny(pattern, " \t"); i >= 0 { + method, rest := pattern[:i], strings.TrimLeft(pattern[i+1:], " \t") + mx.Method(method, rest, handler) return } @@ -119,13 +119,7 @@ func (mx *Mux) Handle(pattern string, handler http.Handler) { // HandleFunc adds the route `pattern` that matches any http method to // execute the `handlerFn` http.HandlerFunc. func (mx *Mux) HandleFunc(pattern string, handlerFn http.HandlerFunc) { - parts := strings.SplitN(pattern, " ", 2) - if len(parts) == 2 { - mx.Method(parts[0], parts[1], handlerFn) - return - } - - mx.handle(mALL, pattern, handlerFn) + mx.Handle(pattern, handlerFn) } // Method adds the route `pattern` that matches `method` http method to @@ -476,6 +470,9 @@ func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) { if supportsPathValue { setPathValue(rctx, r) } + if supportsPattern { + setPattern(rctx, r) + } h.ServeHTTP(w, r) return diff --git a/backend/vendor/github.com/go-chi/chi/v5/path_value.go b/backend/vendor/github.com/go-chi/chi/v5/path_value.go index 7e78171e..77c840f0 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/path_value.go +++ b/backend/vendor/github.com/go-chi/chi/v5/path_value.go @@ -1,5 +1,6 @@ -//go:build go1.22 -// +build go1.22 +//go:build go1.22 && !tinygo +// +build go1.22,!tinygo + package chi diff --git a/backend/vendor/github.com/go-chi/chi/v5/path_value_fallback.go b/backend/vendor/github.com/go-chi/chi/v5/path_value_fallback.go index f551781a..749a8520 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/path_value_fallback.go +++ b/backend/vendor/github.com/go-chi/chi/v5/path_value_fallback.go @@ -1,5 +1,5 @@ -//go:build !go1.22 -// +build !go1.22 +//go:build !go1.22 || tinygo +// +build !go1.22 tinygo package chi diff --git a/backend/vendor/github.com/go-chi/chi/v5/pattern.go b/backend/vendor/github.com/go-chi/chi/v5/pattern.go new file mode 100644 index 00000000..890a2c21 --- /dev/null +++ b/backend/vendor/github.com/go-chi/chi/v5/pattern.go @@ -0,0 +1,16 @@ +//go:build go1.23 && !tinygo +// +build go1.23,!tinygo + +package chi + +import "net/http" + +// supportsPattern is true if the Go version is 1.23 and above. +// +// If this is true, `net/http.Request` has field `Pattern`. +const supportsPattern = true + +// setPattern sets the mux matched pattern in the http Request. +func setPattern(rctx *Context, r *http.Request) { + r.Pattern = rctx.routePattern +} diff --git a/backend/vendor/github.com/go-chi/chi/v5/pattern_fallback.go b/backend/vendor/github.com/go-chi/chi/v5/pattern_fallback.go new file mode 100644 index 00000000..48a94ef8 --- /dev/null +++ b/backend/vendor/github.com/go-chi/chi/v5/pattern_fallback.go @@ -0,0 +1,17 @@ +//go:build !go1.23 || tinygo +// +build !go1.23 tinygo + +package chi + +import "net/http" + +// supportsPattern is true if the Go version is 1.23 and above. +// +// If this is true, `net/http.Request` has field `Pattern`. +const supportsPattern = false + +// setPattern sets the mux matched pattern in the http Request. +// +// setPattern is only supported in Go 1.23 and above so +// this is just a blank function so that it compiles. +func setPattern(rctx *Context, r *http.Request) {} diff --git a/backend/vendor/github.com/go-chi/chi/v5/tree.go b/backend/vendor/github.com/go-chi/chi/v5/tree.go index c7d3bc57..bcb86b6f 100644 --- a/backend/vendor/github.com/go-chi/chi/v5/tree.go +++ b/backend/vendor/github.com/go-chi/chi/v5/tree.go @@ -650,11 +650,9 @@ func (n *node) routes() []Route { if h.handler == nil { continue } - m := methodTypString(mt) - if m == "" { - continue + if m, ok := reverseMethodMap[mt]; ok { + hs[m] = h.handler } - hs[m] = h.handler } rt := Route{subroutes, hs, p} @@ -730,11 +728,9 @@ func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) { tail = pattern[pe] } - var rexpat string - if idx := strings.Index(key, ":"); idx >= 0 { + key, rexpat, isRegexp := strings.Cut(key, ":") + if isRegexp { nt = ntRegexp - rexpat = key[idx+1:] - key = key[:idx] } if len(rexpat) > 0 { @@ -790,15 +786,6 @@ func longestPrefix(k1, k2 string) int { return i } -func methodTypString(method methodTyp) string { - for s, t := range methodMap { - if method == t { - return s - } - } - return "" -} - type nodes []*node // Sort the list of nodes by label diff --git a/backend/vendor/github.com/go-chi/cors/README.md b/backend/vendor/github.com/go-chi/cors/README.md index b41686b6..95dfa027 100644 --- a/backend/vendor/github.com/go-chi/cors/README.md +++ b/backend/vendor/github.com/go-chi/cors/README.md @@ -7,6 +7,10 @@ are required for using the browser native [Fetch API](https://developer.mozilla. This middleware is designed to be used as a top-level middleware on the [chi](https://github.com/go-chi/chi) router. Applying with within a `r.Group()` or using `With()` will not work without routes matching `OPTIONS` added. +## Install + +`go get github.com/go-chi/cors` + ## Usage ```go @@ -36,4 +40,4 @@ func main() { ## Credits -All credit for the original work of this middleware goes out to [github.com/rs](github.com/rs). +All credit for the original work of this middleware goes out to [github.com/rs](https://github.com/rs). diff --git a/backend/vendor/github.com/go-chi/cors/cors.go b/backend/vendor/github.com/go-chi/cors/cors.go index 8df81636..7005755d 100644 --- a/backend/vendor/github.com/go-chi/cors/cors.go +++ b/backend/vendor/github.com/go-chi/cors/cors.go @@ -3,15 +3,15 @@ // // You can configure it by passing an option struct to cors.New: // -// c := cors.New(cors.Options{ -// AllowedOrigins: []string{"foo.com"}, -// AllowedMethods: []string{"GET", "POST", "DELETE"}, -// AllowCredentials: true, -// }) +// c := cors.New(cors.Options{ +// AllowedOrigins: []string{"foo.com"}, +// AllowedMethods: []string{"GET", "POST", "DELETE"}, +// AllowCredentials: true, +// }) // // Then insert the handler in the chain: // -// handler = c.Handler(handler) +// handler = c.Handler(handler) // // See Options documentation for more options. // @@ -210,7 +210,10 @@ func AllowAll() *Cors { // as necessary. func (c *Cors) Handler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { + // null or empty Origin header value is acceptable and it is considered having that header + _, hasOriginHeader := r.Header["Origin"] + + if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" && hasOriginHeader { c.logf("Handler: Preflight request") c.handlePreflight(w, r) // Preflight requests are standalone and should stop the chain as some other @@ -246,10 +249,6 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { headers.Add("Vary", "Access-Control-Request-Method") headers.Add("Vary", "Access-Control-Request-Headers") - if origin == "" { - c.logf("Preflight aborted: empty origin") - return - } if !c.isOriginAllowed(r, origin) { c.logf("Preflight aborted: origin '%s' not allowed", origin) return @@ -291,14 +290,17 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { // handleActualRequest handles simple cross-origin requests, actual request or redirects func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { headers := w.Header() - origin := r.Header.Get("Origin") + // null Origin header value is acceptable and it is considered having that header + _, hasOriginHeader := r.Header["Origin"] // Always set Vary, see https://github.com/rs/cors/issues/10 headers.Add("Vary", "Origin") - if origin == "" { + + if !hasOriginHeader { c.logf("Actual request no headers added: missing origin") return } + origin := r.Header.Get("Origin") if !c.isOriginAllowed(r, origin) { c.logf("Actual request no headers added: origin '%s' not allowed", origin) return diff --git a/backend/vendor/github.com/go-chi/render/.gitignore b/backend/vendor/github.com/go-chi/render/.gitignore deleted file mode 100644 index 22d0d82f..00000000 --- a/backend/vendor/github.com/go-chi/render/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vendor diff --git a/backend/vendor/github.com/go-chi/render/LICENSE b/backend/vendor/github.com/go-chi/render/LICENSE deleted file mode 100644 index 4344db78..00000000 --- a/backend/vendor/github.com/go-chi/render/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2016-Present https://github.com/go-chi authors - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/vendor/github.com/go-chi/render/README.md b/backend/vendor/github.com/go-chi/render/README.md deleted file mode 100644 index 4d371538..00000000 --- a/backend/vendor/github.com/go-chi/render/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# render - -![tests](https://github.com/go-chi/render/actions/workflows/test.yml/badge.svg) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-chi/render)](https://goreportcard.com/report/github.com/go-chi/render) -[![Go Reference](https://pkg.go.dev/badge/github.com/go-chi/render.svg)](https://pkg.go.dev/github.com/go-chi/render) - -The `render` package helps manage HTTP request / response payloads. - -Every well-designed, robust and maintainable Web Service / REST API also needs -well-*defined* request and response payloads. Together with the endpoint handlers, -the request and response payloads make up the contract between your server and the -clients calling on it. - -Typically in a REST API application, you will have your data models (objects/structs) -that hold lower-level runtime application state, and at times you need to assemble, -decorate, hide or transform the representation before responding to a client. That -server output (response payload) structure, is also likely the input structure to -another handler on the server. - -This is where `render` comes in - offering a few simple helpers and interfaces to -provide a simple pattern for managing payload encoding and decoding. - -We've also combined it with some helpers for responding to content types and parsing -request bodies. Please have a look at the [rest](https://github.com/go-chi/chi/blob/master/_examples/rest/main.go) -example which uses the latest chi/render sub-pkg. - -All feedback is welcome, thank you! diff --git a/backend/vendor/github.com/go-chi/render/content_type.go b/backend/vendor/github.com/go-chi/render/content_type.go deleted file mode 100644 index b11a680c..00000000 --- a/backend/vendor/github.com/go-chi/render/content_type.go +++ /dev/null @@ -1,84 +0,0 @@ -package render - -import ( - "context" - "net/http" - "strings" -) - -var ( - ContentTypeCtxKey = &contextKey{"ContentType"} -) - -// ContentType is an enumeration of common HTTP content types. -type ContentType int - -// ContentTypes handled by this package. -const ( - ContentTypeUnknown ContentType = iota - ContentTypePlainText - ContentTypeHTML - ContentTypeJSON - ContentTypeXML - ContentTypeForm - ContentTypeEventStream -) - -func GetContentType(s string) ContentType { - s = strings.TrimSpace(strings.Split(s, ";")[0]) - switch s { - case "text/plain": - return ContentTypePlainText - case "text/html", "application/xhtml+xml": - return ContentTypeHTML - case "application/json", "text/javascript": - return ContentTypeJSON - case "text/xml", "application/xml": - return ContentTypeXML - case "application/x-www-form-urlencoded": - return ContentTypeForm - case "text/event-stream": - return ContentTypeEventStream - default: - return ContentTypeUnknown - } -} - -// SetContentType is a middleware that forces response Content-Type. -func SetContentType(contentType ContentType) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - r = r.WithContext(context.WithValue(r.Context(), ContentTypeCtxKey, contentType)) - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } -} - -// GetRequestContentType is a helper function that returns ContentType based on -// context or request headers. -func GetRequestContentType(r *http.Request) ContentType { - if contentType, ok := r.Context().Value(ContentTypeCtxKey).(ContentType); ok { - return contentType - } - return GetContentType(r.Header.Get("Content-Type")) -} - -func GetAcceptedContentType(r *http.Request) ContentType { - if contentType, ok := r.Context().Value(ContentTypeCtxKey).(ContentType); ok { - return contentType - } - - var contentType ContentType - - // Parse request Accept header. - fields := strings.Split(r.Header.Get("Accept"), ",") - if len(fields) > 0 { - contentType = GetContentType(strings.TrimSpace(fields[0])) - } - - if contentType == ContentTypeUnknown { - contentType = ContentTypePlainText - } - return contentType -} diff --git a/backend/vendor/github.com/go-chi/render/decoder.go b/backend/vendor/github.com/go-chi/render/decoder.go deleted file mode 100644 index 883b2f84..00000000 --- a/backend/vendor/github.com/go-chi/render/decoder.go +++ /dev/null @@ -1,57 +0,0 @@ -package render - -import ( - "encoding/json" - "encoding/xml" - "errors" - "io" - "io/ioutil" - "net/http" - - "github.com/ajg/form" -) - -// Decode is a package-level variable set to our default Decoder. We do this -// because it allows you to set render.Decode to another function with the -// same function signature, while also utilizing the render.Decoder() function -// itself. Effectively, allowing you to easily add your own logic to the package -// defaults. For example, maybe you want to impose a limit on the number of -// bytes allowed to be read from the request body. -var Decode = DefaultDecoder - -// DefaultDecoder detects the correct decoder for use on an HTTP request and -// marshals into a given interface. -func DefaultDecoder(r *http.Request, v interface{}) error { - var err error - - switch GetRequestContentType(r) { - case ContentTypeJSON: - err = DecodeJSON(r.Body, v) - case ContentTypeXML: - err = DecodeXML(r.Body, v) - case ContentTypeForm: - err = DecodeForm(r.Body, v) - default: - err = errors.New("render: unable to automatically decode the request content type") - } - - return err -} - -// DecodeJSON decodes a given reader into an interface using the json decoder. -func DecodeJSON(r io.Reader, v interface{}) error { - defer io.Copy(ioutil.Discard, r) //nolint:errcheck - return json.NewDecoder(r).Decode(v) -} - -// DecodeXML decodes a given reader into an interface using the xml decoder. -func DecodeXML(r io.Reader, v interface{}) error { - defer io.Copy(ioutil.Discard, r) //nolint:errcheck - return xml.NewDecoder(r).Decode(v) -} - -// DecodeForm decodes a given reader into an interface using the form decoder. -func DecodeForm(r io.Reader, v interface{}) error { - decoder := form.NewDecoder(r) //nolint:errcheck - return decoder.Decode(v) -} diff --git a/backend/vendor/github.com/go-chi/render/render.go b/backend/vendor/github.com/go-chi/render/render.go deleted file mode 100644 index 75a90e23..00000000 --- a/backend/vendor/github.com/go-chi/render/render.go +++ /dev/null @@ -1,143 +0,0 @@ -package render - -import ( - "net/http" - "reflect" -) - -// Renderer interface for managing response payloads. -type Renderer interface { - Render(w http.ResponseWriter, r *http.Request) error -} - -// Binder interface for managing request payloads. -type Binder interface { - Bind(r *http.Request) error -} - -// Bind decodes a request body and executes the Binder method of the -// payload structure. -func Bind(r *http.Request, v Binder) error { - if err := Decode(r, v); err != nil { - return err - } - return binder(r, v) -} - -// Render renders a single payload and respond to the client request. -func Render(w http.ResponseWriter, r *http.Request, v Renderer) error { - if err := renderer(w, r, v); err != nil { - return err - } - Respond(w, r, v) - return nil -} - -// RenderList renders a slice of payloads and responds to the client request. -func RenderList(w http.ResponseWriter, r *http.Request, l []Renderer) error { - for _, v := range l { - if err := renderer(w, r, v); err != nil { - return err - } - } - Respond(w, r, l) - return nil -} - -func isNil(f reflect.Value) bool { - switch f.Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return f.IsNil() - default: - return false - } -} - -// Executed top-down -func renderer(w http.ResponseWriter, r *http.Request, v Renderer) error { - rv := reflect.ValueOf(v) - if rv.Kind() == reflect.Ptr { - rv = rv.Elem() - } - - // We call it top-down. - if err := v.Render(w, r); err != nil { - return err - } - - // We're done if the Renderer isn't a struct object - if rv.Kind() != reflect.Struct { - return nil - } - - // For structs, we call Render on each field that implements Renderer - for i := 0; i < rv.NumField(); i++ { - f := rv.Field(i) - if f.Type().Implements(rendererType) { - - if isNil(f) { - continue - } - - fv := f.Interface().(Renderer) - if err := renderer(w, r, fv); err != nil { - return err - } - - } - } - - return nil -} - -// Executed bottom-up -func binder(r *http.Request, v Binder) error { - rv := reflect.ValueOf(v) - if rv.Kind() == reflect.Ptr { - rv = rv.Elem() - } - - // Call Binder on non-struct types right away - if rv.Kind() != reflect.Struct { - return v.Bind(r) - } - - // For structs, we call Bind on each field that implements Binder - for i := 0; i < rv.NumField(); i++ { - f := rv.Field(i) - if f.Type().Implements(binderType) { - - if isNil(f) { - continue - } - - fv := f.Interface().(Binder) - if err := binder(r, fv); err != nil { - return err - } - } - } - - // We call it bottom-up - if err := v.Bind(r); err != nil { - return err - } - - return nil -} - -var ( - rendererType = reflect.TypeOf(new(Renderer)).Elem() - binderType = reflect.TypeOf(new(Binder)).Elem() -) - -// contextKey is a value for use with context.WithValue. It's used as -// a pointer so it fits in an interface{} without allocation. This technique -// for defining context keys was copied from Go 1.7's new use of context in net/http. -type contextKey struct { - name string -} - -func (k *contextKey) String() string { - return "chi render context value " + k.name -} diff --git a/backend/vendor/github.com/go-chi/render/responder.go b/backend/vendor/github.com/go-chi/render/responder.go deleted file mode 100644 index f38807d1..00000000 --- a/backend/vendor/github.com/go-chi/render/responder.go +++ /dev/null @@ -1,234 +0,0 @@ -package render - -import ( - "bytes" - "context" - "encoding/json" - "encoding/xml" - "fmt" - "net/http" - "reflect" -) - -// M is a convenience alias for quickly building a map structure that is going -// out to a responder. Just a short-hand. -type M map[string]interface{} - -// Respond is a package-level variable set to our default Responder. We do this -// because it allows you to set render.Respond to another function with the -// same function signature, while also utilizing the render.Responder() function -// itself. Effectively, allowing you to easily add your own logic to the package -// defaults. For example, maybe you want to test if v is an error and respond -// differently, or log something before you respond. -var Respond = DefaultResponder - -// StatusCtxKey is a context key to record a future HTTP response status code. -var StatusCtxKey = &contextKey{"Status"} - -// Status sets a HTTP response status code hint into request context at any point -// during the request life-cycle. Before the Responder sends its response header -// it will check the StatusCtxKey -func Status(r *http.Request, status int) { - *r = *r.WithContext(context.WithValue(r.Context(), StatusCtxKey, status)) -} - -// Respond handles streaming JSON and XML responses, automatically setting the -// Content-Type based on request headers. It will default to a JSON response. -func DefaultResponder(w http.ResponseWriter, r *http.Request, v interface{}) { - if v != nil { - switch reflect.TypeOf(v).Kind() { - case reflect.Chan: - switch GetAcceptedContentType(r) { - case ContentTypeEventStream: - channelEventStream(w, r, v) - return - default: - v = channelIntoSlice(w, r, v) - } - } - } - - // Format response based on request Accept header. - switch GetAcceptedContentType(r) { - case ContentTypeJSON: - JSON(w, r, v) - case ContentTypeXML: - XML(w, r, v) - default: - JSON(w, r, v) - } -} - -// PlainText writes a string to the response, setting the Content-Type as -// text/plain. -func PlainText(w http.ResponseWriter, r *http.Request, v string) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - w.Write([]byte(v)) //nolint:errcheck -} - -// Data writes raw bytes to the response, setting the Content-Type as -// application/octet-stream. -func Data(w http.ResponseWriter, r *http.Request, v []byte) { - w.Header().Set("Content-Type", "application/octet-stream") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - w.Write(v) //nolint:errcheck -} - -// HTML writes a string to the response, setting the Content-Type as text/html. -func HTML(w http.ResponseWriter, r *http.Request, v string) { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - w.Write([]byte(v)) //nolint:errcheck -} - -// JSON marshals 'v' to JSON, automatically escaping HTML and setting the -// Content-Type as application/json. -func JSON(w http.ResponseWriter, r *http.Request, v interface{}) { - buf := &bytes.Buffer{} - enc := json.NewEncoder(buf) - enc.SetEscapeHTML(true) - if err := enc.Encode(v); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - w.Write(buf.Bytes()) //nolint:errcheck -} - -// XML marshals 'v' to XML, setting the Content-Type as application/xml. It -// will automatically prepend a generic XML header (see encoding/xml.Header) if -// one is not found in the first 100 bytes of 'v'. -func XML(w http.ResponseWriter, r *http.Request, v interface{}) { - b, err := xml.Marshal(v) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/xml; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - - // Try to find 100 { - findHeaderUntil = 100 - } - if !bytes.Contains(b[:findHeaderUntil], []byte(" 0 { - router.Use(middleware.Throttle(s.limits.serverThrottle)) + router.Use(rest.Throttle(int64(s.limits.serverThrottle))) } - router.Use(middleware.RealIP, rest.Ping, rest.Recoverer(s.logger)) + router.Use(rest.RealIP, rest.Ping, rest.Recoverer(s.logger)) if s.signature.version != "" || s.signature.author != "" || s.signature.appName != "" { router.Use(rest.AppInfo(s.signature.appName, s.signature.author, s.signature.version)) } if s.timeouts.CallTimeout > 0 { - router.Use(middleware.Timeout(s.timeouts.CallTimeout)) + router.Use(timeout(s.timeouts.CallTimeout)) } logInfoWithBody := logger.New(logger.Log(s.logger), logger.WithBody, logger.Prefix("[DEBUG]")).Handler router.Use(logInfoWithBody) if s.limits.clientLimit > 0 { - lmt := tollbooth.NewLimiter(s.limits.clientLimit, nil) - lmt.SetIPLookup(limiter.IPLookup{Name: "X-Real-IP"}) - router.Use(tollbooth.HTTPMiddleware(lmt)) + router.Use(rateLimitByIP(s.limits.clientLimit)) } - router.Use(middleware.NoCache) + router.Use(rest.NoCache) router.Use(s.basicAuth) - router.Use(s.customMiddlewares...) - router.Post(s.api, s.handler) + for _, mw := range s.customMiddlewares { + router.Use(mw) + } + router.HandleFunc("POST "+s.api, s.handler) s.httpServer.Lock() s.httpServer.Server = &http.Server{ @@ -203,7 +199,7 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) { params = *req.Params } - render.JSON(w, r, fn(req.ID, params)) + rest.RenderJSON(w, fn(req.ID, params)) } // basicAuth middleware. enabled only if both AuthUser and AuthPasswd defined. @@ -233,6 +229,17 @@ func getDefaultTimeouts() Timeouts { } } +// timeout middleware cancels context after given duration +func timeout(dt time.Duration) func(http.Handler) http.Handler { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, cancel := context.WithTimeout(r.Context(), dt) + defer cancel() + h.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} + // L defined logger interface used for an optional rest logging type L interface { Logf(format string, args ...interface{}) @@ -246,3 +253,49 @@ func (f LoggerFunc) Logf(format string, args ...interface{}) { f(format, args... // NoOpLogger logger does nothing var NoOpLogger = LoggerFunc(func(format string, args ...interface{}) {}) //nolint + +// rateLimitByIP returns middleware that limits requests per second for each client IP. +// Uses X-Real-IP header (set by rest.RealIP middleware) for client identification. +func rateLimitByIP(reqPerSec float64) func(http.Handler) http.Handler { + type clientState struct { + sync.Mutex + tokens float64 + lastCheck time.Time + } + var ( + clients sync.Map + maxReq = reqPerSec + ) + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ip := r.Header.Get("X-Real-IP") + if ip == "" { + ip = r.RemoteAddr + } + + now := time.Now() + val, _ := clients.LoadOrStore(ip, &clientState{tokens: maxReq, lastCheck: now}) + state := val.(*clientState) + + state.Lock() + // token bucket algorithm: refill tokens based on elapsed time + elapsed := now.Sub(state.lastCheck).Seconds() + state.tokens += elapsed * reqPerSec + if state.tokens > maxReq { + state.tokens = maxReq + } + state.lastCheck = now + + if state.tokens < 1 { + state.Unlock() + http.Error(w, "rate limit exceeded", http.StatusTooManyRequests) + return + } + state.tokens-- + state.Unlock() + + next.ServeHTTP(w, r) + }) + } +} diff --git a/backend/vendor/github.com/go-pkgz/lgr/.golangci.yml b/backend/vendor/github.com/go-pkgz/lgr/.golangci.yml index c90a0281..2ad5ea81 100644 --- a/backend/vendor/github.com/go-pkgz/lgr/.golangci.yml +++ b/backend/vendor/github.com/go-pkgz/lgr/.golangci.yml @@ -1,103 +1,104 @@ -linters-settings: - govet: - shadow: true - golint: - min-confidence: 0.6 - gocyclo: - min-complexity: 15 - maligned: - suggest-new: true - dupl: - threshold: 100 - goconst: - min-len: 2 - min-occurrences: 2 - misspell: - locale: US - lll: - line-length: 140 - gocritic: - enabled-tags: - - performance - - style - - experimental - disabled-checks: - - wrapperFunc - - hugeParam - - rangeValCopy - -linters: - disable-all: true - enable: - - revive - - govet - - unconvert - - gosec - - unparam - - unused - - typecheck - - ineffassign - - stylecheck - - gochecknoinits - - gocritic - - nakedret - - gosimple - - prealloc - - fast: false - - +version: "2" run: concurrency: 4 - -issues: - exclude-dirs: - - vendor - exclude-rules: - - text: "should have a package comment, unless it's in another file for this package" - linters: - - golint - - text: "exitAfterDefer:" - linters: - - gocritic - - text: "whyNoLint: include an explanation for nolint directive" - linters: - - gocritic - - text: "go.mongodb.org/mongo-driver/bson/primitive.E" - linters: - - govet - - text: "weak cryptographic primitive" - linters: - - gosec - - text: "integer overflow conversion" - linters: - - gosec - - text: "should have a package comment" - linters: - - revive - - text: "at least one file in a package should have a package comment" - linters: - - stylecheck - - text: "commentedOutCode: may want to remove commented-out code" - linters: - - gocritic - - text: "unnamedResult: consider giving a name to these results" - linters: - - gocritic - - text: "var-naming: don't use an underscore in package name" - linters: - - revive - - text: "should not use underscores in package names" - linters: - - stylecheck - - text: "struct literal uses unkeyed fields" - linters: - - govet - - linters: - - unparam - - unused - - revive - path: _test\.go$ - text: "unused-parameter" - exclude-use-default: false - +linters: + default: none + enable: + - gochecknoinits + - gocritic + - gosec + - govet + - ineffassign + - nakedret + - prealloc + - revive + - staticcheck + - unconvert + - unparam + - unused + - nestif + - testifylint + settings: + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 2 + gocritic: + disabled-checks: + - wrapperFunc + - hugeParam + - rangeValCopy + enabled-tags: + - performance + - style + - experimental + gocyclo: + min-complexity: 15 + govet: + enable: + - shadow + lll: + line-length: 140 + misspell: + locale: US + exclusions: + generated: lax + rules: + - linters: + - golint + text: should have a package comment, unless it's in another file for this package + - linters: + - gocritic + text: 'exitAfterDefer:' + - linters: + - gocritic + text: 'whyNoLint: include an explanation for nolint directive' + - linters: + - govet + text: go.mongodb.org/mongo-driver/bson/primitive.E + - linters: + - gosec + text: weak cryptographic primitive + - linters: + - gosec + text: integer overflow conversion + - linters: + - revive + text: should have a package comment + - linters: + - staticcheck + text: at least one file in a package should have a package comment + - linters: + - gocritic + text: 'commentedOutCode: may want to remove commented-out code' + - linters: + - gocritic + text: 'unnamedResult: consider giving a name to these results' + - linters: + - revive + text: 'var-naming: don''t use an underscore in package name' + - linters: + - staticcheck + text: should not use underscores in package names + - linters: + - govet + text: struct literal uses unkeyed fields + - linters: + - revive + - unparam + - unused + path: _test\.go$ + text: unused-parameter + paths: + - vendor + - third_party$ + - builtin$ + - examples$ +formatters: + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/backend/vendor/github.com/go-pkgz/lgr/logger.go b/backend/vendor/github.com/go-pkgz/lgr/logger.go index 00cc0d9e..2d8471ca 100644 --- a/backend/vendor/github.com/go-pkgz/lgr/logger.go +++ b/backend/vendor/github.com/go-pkgz/lgr/logger.go @@ -253,7 +253,7 @@ func (l *Logger) logf(format string, args ...interface{}) { func (l *Logger) hideSecrets(data []byte) []byte { for _, h := range l.secrets { - data = bytes.Replace(data, h, secretReplacement, -1) + data = bytes.ReplaceAll(data, h, secretReplacement) } return data } diff --git a/backend/vendor/github.com/go-pkgz/lgr/slog.go b/backend/vendor/github.com/go-pkgz/lgr/slog.go index 0f930814..b68af07a 100644 --- a/backend/vendor/github.com/go-pkgz/lgr/slog.go +++ b/backend/vendor/github.com/go-pkgz/lgr/slog.go @@ -22,7 +22,15 @@ func FromSlogHandler(h slog.Handler) L { // SetupWithSlog sets up the global logger with a slog logger func SetupWithSlog(logger *slog.Logger) { - Setup(SlogHandler(logger.Handler())) + options := []Option{SlogHandler(logger.Handler())} + + // check if the slog handler is enabled for debug level + // if so, enable debug mode in lgr to prevent filtering + if logger.Handler().Enabled(context.Background(), slog.LevelDebug) { + options = append(options, Debug) + } + + Setup(options...) } // lgrSlogHandler implements slog.Handler using lgr.L diff --git a/backend/vendor/github.com/go-pkgz/rest/logger/logger.go b/backend/vendor/github.com/go-pkgz/rest/logger/logger.go index 9be2e778..e814663d 100644 --- a/backend/vendor/github.com/go-pkgz/rest/logger/logger.go +++ b/backend/vendor/github.com/go-pkgz/rest/logger/logger.go @@ -227,7 +227,7 @@ func (l *Middleware) getBody(r *http.Request) string { r.Body = io.NopCloser(reader) if body != "" { - body = strings.Replace(body, "\n", " ", -1) + body = strings.ReplaceAll(body, "\n", " ") body = reMultWhtsp.ReplaceAllString(body, " ") } diff --git a/backend/vendor/github.com/go-pkgz/routegroup/.gitignore b/backend/vendor/github.com/go-pkgz/routegroup/.gitignore new file mode 100644 index 00000000..5395afeb --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/routegroup/.gitignore @@ -0,0 +1,11 @@ +# Coverage files +coverage.out +coverage.html +*.cover + +# Test binaries +*.test + +# Go workspace files +go.work +go.work.sum \ No newline at end of file diff --git a/backend/vendor/github.com/go-pkgz/routegroup/.golangci.yml b/backend/vendor/github.com/go-pkgz/routegroup/.golangci.yml new file mode 100644 index 00000000..f8002ab0 --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/routegroup/.golangci.yml @@ -0,0 +1,62 @@ +version: "2" +run: + concurrency: 4 +linters: + default: none + enable: + - copyloopvar + - gochecknoinits + - gocritic + - gosec + - govet + - ineffassign + - misspell + - nakedret + - prealloc + - revive + - staticcheck + - unconvert + - unparam + - unused + settings: + goconst: + min-len: 2 + min-occurrences: 2 + gocritic: + disabled-checks: + - wrapperFunc + enabled-tags: + - performance + - style + - experimental + gocyclo: + min-complexity: 15 + govet: + enable: + - shadow + lll: + line-length: 140 + misspell: + locale: US + exclusions: + generated: lax + rules: + - linters: + - gosec + text: 'G114: Use of net/http serve function that has no support for setting timeouts' + - linters: + - revive + - unparam + path: _test\.go$ + text: unused-parameter + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/backend/vendor/github.com/didip/tollbooth/v8/LICENSE b/backend/vendor/github.com/go-pkgz/routegroup/LICENSE similarity index 86% rename from backend/vendor/github.com/didip/tollbooth/v8/LICENSE rename to backend/vendor/github.com/go-pkgz/routegroup/LICENSE index 349ee1c2..e47db563 100644 --- a/backend/vendor/github.com/didip/tollbooth/v8/LICENSE +++ b/backend/vendor/github.com/go-pkgz/routegroup/LICENSE @@ -1,6 +1,6 @@ -The MIT License (MIT) +MIT License -Copyright (c) 2015 Didip Kerabat +Copyright (c) 2024 Umputun Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/backend/vendor/github.com/go-pkgz/routegroup/README.md b/backend/vendor/github.com/go-pkgz/routegroup/README.md new file mode 100644 index 00000000..174d712b --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/routegroup/README.md @@ -0,0 +1,370 @@ +## routegroup [![Build Status](https://github.com/go-pkgz/routegroup/workflows/build/badge.svg)](https://github.com/go-pkgz/routegroup/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/go-pkgz/routegroup)](https://goreportcard.com/report/github.com/go-pkgz/routegroup) [![Coverage Status](https://coveralls.io/repos/github/go-pkgz/routegroup/badge.svg?branch=master)](https://coveralls.io/github/go-pkgz/routegroup?branch=master) [![godoc](https://godoc.org/github.com/go-pkgz/routegroup?status.svg)](https://godoc.org/github.com/go-pkgz/routegroup) + + +`routegroup` is a tiny Go package providing a lightweight wrapper for efficient route grouping and middleware integration with the standard `http.ServeMux`. + +## Features + +- Simple and intuitive API for route grouping and route mounting. +- Lightweight, just about 100 LOC +- Easy middleware integration for individual routes or groups of routes. +- Seamless integration with Go's standard `http.ServeMux`. +- Fully compatible with the `http.Handler` interface and can be used as a drop-in replacement for `http.ServeMux`. +- No external dependencies. + +## Requirements + +- Go 1.23 or higher + *(This library uses `http.Request.Pattern` to make route patterns available to global middlewares and relies on the enhanced `http.ServeMux` routing behavior introduced in Go 1.22/1.23)* + +## Install and update + +`go get -u github.com/go-pkgz/routegroup` + +## Usage + +**Creating a New Route Group** + +To start, create a new route group without a base path: + +```go +func main() { + mux := http.NewServeMux() + group := routegroup.New(mux) +} +``` + +**Adding Routes with Middleware** + +Add routes to your group, optionally with middleware: + +```go + group.Use(loggingMiddleware, corsMiddleware) + group.Handle("/hello", helloHandler) + group.Handle("/bye", byeHandler) +``` +**Creating a Nested Route Group** + +For routes under a specific path prefix `Mount` method can be used to create a nested group: + +```go + apiGroup := routegroup.Mount(mux, "/api") + apiGroup.Use(loggingMiddleware, corsMiddleware) + apiGroup.Handle("/v1", apiV1Handler) + apiGroup.Handle("/v2", apiV2Handler) + +``` + +**Complete Example** + +Here's a complete example demonstrating route grouping and middleware usage: + +```go +package main + +import ( + "net/http" + + "github.com/go-pkgz/routegroup" +) + +func main() { + router := routegroup.New(http.NewServeMux()) + router.Use(loggingMiddleware) + + // handle the /hello route + router.Handle("GET /hello", helloHandler) + + // create a new group for the /api path + apiRouter := router.Mount("/api") + // add middleware + apiRouter.Use(loggingMiddleware, corsMiddleware) + + // route handling + apiRouter.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hello, API!")) + }) + + // add another group with its own set of middlewares + protectedGroup := router.Group() + protectedGroup.Use(authMiddleware) + protectedGroup.HandleFunc("GET /protected", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Protected API!")) + }) + + http.ListenAndServe(":8080", router) +} +``` + +**Applying Middleware to Specific Routes** + +You can also apply middleware to specific routes inside the group without modifying the group's middleware stack: + +```go +apiGroup.With(corsMiddleware, apiMiddleware).Handle("GET /hello", helloHandler) +``` + +**Alternative Usage with `Route`** + +You can also use the `Route` method to add routes and middleware in a single function call: + +```go +router := routegroup.New(http.NewServeMux()) +router.Route(func(b *routegroup.Bundle) { + b.Use(loggingMiddleware, corsMiddleware) + b.Handle("GET /hello", helloHandler) + b.Handle("GET /bye", byeHandler) +}) +http.ListenAndServe(":8080", router) +``` + +When called on the root bundle, `Route` automatically creates a new group to avoid accidentally modifying the root bundle's middleware stack. This means the middleware and routes defined inside the `Route` function are isolated from other routes on the root bundle. + +The `Route` method can also be chained after `Mount` or `Group` for a more functional style: + +```go +router := routegroup.New(http.NewServeMux()) +router.Group().Route(func(b *routegroup.Bundle) { + b.Use(loggingMiddleware, corsMiddleware) + b.Handle("GET /hello", helloHandler) + b.Handle("GET /bye", byeHandler) +}) +``` + +**Setting optional `NotFoundHandler`** + +It is possible to set a custom `NotFoundHandler` for the group. This handler will be called when no other route matches the request: + +```go +group.NotFoundHandler(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "404 page not found, something is wrong!", http.StatusNotFound) +} +``` + +If a custom `NotFoundHandler` is not configured, `routegroup` will default to using the standard library behavior. + +Note on 405: In the current design, `routegroup` applies root-level middlewares to all requests at the top level without installing a catch‑all route. This preserves native `405 Method Not Allowed` responses from `http.ServeMux` when a path exists but a wrong method is used. A configured `NotFoundHandler` is only invoked when no route matches; it does not interfere with 405 handling. The custom `NotFoundHandler` will have the root bundle's global middlewares applied to it. + +Legacy note: `DisableNotFoundHandler()` is now a no‑op and preserved only for API compatibility. + +### Middleware Ordering + +- Call `Use(...)` before registering routes on the same bundle. Calling `Use` after any handler has been registered on that bundle will panic with a descriptive error. +- Root bundle middlewares (added via `router.Use(...)`) are applied globally to all requests at serve time. +- Group/bundle middlewares (added via `group.Use(...)`) apply to the routes registered on that bundle and its descendants, provided they are added before those routes. +- `With(...)` returns a new bundle; you can add middlewares there first, then register routes. This is the preferred way to add scoped middlewares without affecting previously defined routes. + +**Important**: Route registration (HandleFunc, Handle, HandleFiles, etc.) should be done during initialization and not performed concurrently. The library is designed for typical usage where routes are registered at startup time in a single goroutine. + +Examples + +Incorrect: calling `Use` after routes on the same bundle (will panic) + +```go +mux := http.NewServeMux() +router := routegroup.New(mux) + +router.HandleFunc("/r", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }) + +// This will panic: Use called after routes were registered on this bundle +router.Use(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // global header + w.Header().Set("X-Global", "true") + next.ServeHTTP(w, r) + }) +}) +``` + +Allowed: parent/root `Use` after child bundle routes + +```go +mux := http.NewServeMux() +router := routegroup.New(mux) + +child := router.Group() +child.HandleFunc("/child", func(w http.ResponseWriter, _ *http.Request) { w.Write([]byte("ok")) }) + +// Parent has not registered its own routes yet; this is allowed and will apply globally +router.Use(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Parent", "true") + next.ServeHTTP(w, r) + }) +}) +``` + +Preferred: use `With` (or `Group`+`Use`) to attach scoped middleware before routes + +```go +mux := http.NewServeMux() +router := routegroup.New(mux) + +// Global middleware (optional), add before any root routes +router.Use(loggingMiddleware) + +// Scoped middleware using With: returns a new bundle on which we can add routes +api := router.With(authMiddleware) +api.HandleFunc("GET /items", itemsHandler) +api.HandleFunc("POST /items", createItem) + +// Or using Group + Use before routes +admin := router.Group() +admin.Use(adminOnly) +admin.HandleFunc("GET /dashboard", dashboardHandler) +``` + + +**Handling Root Paths Without Trailing Slashes** + +When working with mounted groups, you often need to handle requests to the group's root path without a trailing slash. For this purpose, `routegroup` provides the `HandleRoot` or `HandleRootFunc` methods: + +```go +// Create mounted groups +apiGroup := router.Mount("/api") +v1Group := apiGroup.Mount("/v1") +usersGroup := v1Group.Mount("/users") + +// Handle the root paths (no trailing slashes) +apiGroup.HandleRoot("GET", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // This handles requests to "/api" (without trailing slash) + w.Write([]byte("API Documentation")) +})) + +usersGroup.HandleRoot("GET", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // This handles requests to "/api/v1/users" (without trailing slash) + w.Write([]byte("List users")) +})) + +// Different HTTP methods can be handled separately +usersGroup.HandleRoot("POST", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // This handles POST requests to "/api/v1/users" + w.Write([]byte("Create user")) +})) +``` + +While it's also possible to handle such paths using a trailing slash pattern (`"/"`) with the regular `Handle` or `HandleFunc` methods, that approach results in a redirect from non-trailing slash URLs (e.g., `/api`) to the trailing slash version (e.g., `/api/`). The `HandleRoot` method avoids this redirect, providing a more direct response and avoiding an extra round-trip, which is especially important for non-GET requests or when clients don't automatically follow redirects. + +### Using derived groups + +In some instances, it's practical to create an initial group that includes a set of middlewares, and then derive all other groups from it. This approach guarantees that every group incorporates a common set of middlewares as a foundation, allowing each to add its specific middlewares. To facilitate this scenario, `routegroup` offers both `Bundle.Group` and `Bundle.Mount` methods, and it also implements the `http.Handler` interface. The following example illustrates how to use derived groups: + +```go +// create a new bundle with a base set of middlewares +// note: the bundle is also http.Handler and can be passed to http.ListenAndServe +router := routegroup.New(http.NewServeMux()) +router.Use(loggingMiddleware, corsMiddleware) + +// add a new, derived group with its own set of middlewares +// this group will inherit the middlewares from the base group +apiGroup := router.Group() +apiGroup.Use(apiMiddleware) +apiGroup.Handle("GET /hello", helloHandler) +apiGroup.Handle("GET /bye", byeHandler) + +// mount another group for the /admin path with its own set of middlewares, +// using `Route` method to show the alternative usage. +// this group will inherit the middlewares from the base group as well +router.Mount("/admin").Route(func(b *routegroup.Bundle) { + b.Use(adminMiddleware) + b.Handle("POST /do", doHandler) +}) + +// start the server, passing the wrapped mux as the handler +http.ListenAndServe(":8080", router) +``` +### Wrap Function + +Sometimes route's group is not necessary, and all you need is to apply middleware(s) directly to a single route. In this case, `routegroup` provides a `Wrap` function that can be used to wrap a single `http.Handler` with one or more middlewares. Here's an example: + +```go +mux := http.NewServeMux() +mux.HandleFunc("/hello", routegroup.Wrap(helloHandler, loggingMiddleware, corsMiddleware)) +http.ListenAndServe(":8080", mux) +``` + +### 404 and 405 behavior + +`routegroup` applies the root bundle's middlewares to all requests at the top level. This keeps the standard library's matching logic intact: +- Wrong method on an existing path returns `405 Method Not Allowed` (with an `Allow` header). +- Unknown path returns `404 Not Found`. + +You can optionally configure a custom 404 handler with `NotFoundHandler(fn)`. It will run only when no route matches and does not affect 405 handling. The custom handler will have global middlewares applied to it. The legacy `DisableNotFoundHandler()` is now a no‑op and kept only for compatibility. + +### HandleFiles helper + +`routegroup` provides a helper function `HandleFiles` that can be used to serve static files from a directory. The function is a thin wrapper around the standard `http.FileServer` and can be used to serve files from a specific directory. Here's an example: + +```go +// serve static files from the "assets/static" directory +router.HandleFiles("/static/", http.Dir("assets/static")) +``` + +## Real-world example + +Here's an example of how `routegroup` can be used in a real-world application. The following code snippet is taken from a web service that provides a set of routes for user authentication, session management, and user management. The service also serves static files from the "assets/static" embedded file system. + +```go + +// Routes returns http.Handler that handles all the routes for the Service. +// It also serves static files from the "assets/static" directory. +// The rootURL option sets prefix for the routes. +func (s *Service) Routes() http.Handler { + router := routegroup.Mount(http.NewServeMux(), s.rootURL) // make a bundle with the rootURL base path + // add common middlewares + router.Use(rest.Maybe(handlers.CompressHandler, func(*http.Request) bool { return !s.skipGZ })) + router.Use(rest.Throttle(s.limitActiveReqs)) + router.Use(s.middleware.securityHeaders(s.skipSecurityHeaders)) + + // prepare csrf middleware + csrfMiddleware := s.middleware.csrf(s.skipCSRFCheck) + + // add open routes + router.HandleFunc("GET /login", s.loginPageHandler) + router.HandleFunc("POST /login", s.loginCheckHandler) + router.HandleFunc("GET /logout", s.logoutHandler) + + // add routes with auth middleware + router.Group().Route(func(auth *routegroup.Bundle) { + auth.Use(s.middleware.Auth()) + auth.HandleFunc("GET /update", s.pwdUpdateHandler) + auth.With(csrfMiddleware).HandleFunc("PUT /update", s.pwdUpdateHandler) + }) + + // add admin routes + router.Mount("/admin").Route(func(admin *routegroup.Bundle) { + admin.Use(s.middleware.Auth("admin")) + admin.Use(s.middleware.AdminOnly) + admin.HandleFunc("GET /", s.admin.renderHandler) + admin.With(csrfMiddleware).Route(func(csrf *routegroup.Bundle) { + csrf.HandleFunc("DELETE /sessions", s.admin.deleteSessionsHandler) + csrf.HandleFunc("POST /user", s.admin.addUserHandler) + csrf.HandleFunc("DELETE /user", s.admin.deleteUserHandler) + }) + }) + + router.HandleFunc("GET /static/*", s.fileServerHandlerFunc()) // serve static files + return router +} + +// fileServerHandlerFunc returns http.HandlerFunc that serves static files from the "assets/static" directory. +// prefix is set by the rootURL option. +func (s *Service) fileServerHandlerFunc() http.HandlerFunc { + staticFS, err := fs.Sub(assets, "assets/static") // error is always nil + if err != nil { + panic(err) // should never happen we load from embedded FS + } + return func(w http.ResponseWriter, r *http.Request) { + webFS := http.StripPrefix(s.rootURL+"/static/", http.FileServer(http.FS(staticFS))) + webFS.ServeHTTP(w, r) + } +} +``` + +## Contributing + +Contributions to `routegroup` are welcome! Please submit a pull request or open an issue for any bugs or feature requests. + +## License + +`routegroup` is available under the MIT license. See the [LICENSE](https://github.com/go-pkgz/routegroup/blob/master/LICENSE) file for more info. diff --git a/backend/vendor/github.com/go-pkgz/routegroup/group.go b/backend/vendor/github.com/go-pkgz/routegroup/group.go new file mode 100644 index 00000000..bc49932a --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/routegroup/group.go @@ -0,0 +1,355 @@ +// Package routegroup provides a way to group routes and applies middleware to them. +// Works with the standard library's http.ServeMux. +package routegroup + +import ( + "net/http" + "regexp" + "strings" +) + +// Bundle represents a group of routes with associated middleware. +type Bundle struct { + mux *http.ServeMux // the underlying mux to register the routes to + basePath string // base path for the group + middlewares []func(http.Handler) http.Handler // middlewares stack + + // optional custom 404 handler + notFound http.HandlerFunc + + // root points to the root bundle for global middleware application. + // for the root bundle, root == nil. + root *Bundle + + // routesLocked indicates that routes have been registered on the root bundle + // and no further root-level middlewares may be added. + routesLocked bool + + // rootCount captures how many root middlewares were present when this bundle + // was created. Used to avoid double-applying root middlewares for per-route wrapping. + rootCount int +} + +// New creates a new Group. +func New(mux *http.ServeMux) *Bundle { + return &Bundle{mux: mux} +} + +// Mount creates a new group with a specified base path. +func Mount(mux *http.ServeMux, basePath string) *Bundle { + return &Bundle{mux: mux, basePath: basePath} +} + +// ServeHTTP implements the http.Handler interface +func (b *Bundle) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // resolve the root bundle (where global middlewares live). + root := b + if b.root != nil { + root = b.root + } + + // get the handler and pattern for this request + _, pattern := b.mux.Handler(r) + + // if a pattern was found, create a shallow copy of the request with the pattern set + // this allows global middlewares to see the pattern before mux.ServeHTTP is called + if pattern != "" { + r2 := *r + r2.Pattern = pattern + r = &r2 + } + + // create a handler that will let the mux do its routing (including setting path parameters) + // but intercept 404s to use custom handler if provided + muxHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if pattern == "" && root.notFound != nil { + // no route matched, need to check if it's a true 404 or a 405 + // probe the mux to see what status it would return + probe := &statusRecorder{status: http.StatusOK} + b.mux.ServeHTTP(probe, r) + + // if mux wants to return 405 (Method Not Allowed), let it handle the request + // to preserve the proper 405 response and Allow header + if probe.status == http.StatusMethodNotAllowed { + b.mux.ServeHTTP(w, r) + return + } + + // it's a true 404, use custom handler + root.notFound.ServeHTTP(w, r) + return + } + // let the mux handle the request normally (this sets path parameters) + b.mux.ServeHTTP(w, r) + }) + + // apply root (global) middlewares around the mux handler and serve the request. + root.wrapGlobal(muxHandler).ServeHTTP(w, r) +} + +// Group creates a new group with the same middleware stack as the original on top of the existing bundle. +func (b *Bundle) Group() *Bundle { + return b.clone() // copy the middlewares to avoid modifying the original +} + +// Mount creates a new group with a specified base path on top of the existing bundle. +func (b *Bundle) Mount(basePath string) *Bundle { + g := b.clone() // copy the middlewares to avoid modifying the original + g.basePath += basePath + return g +} + +// Use adds middleware(s) to the Group. +// Middlewares are executed in the order they are added. +// Note: Root-level middlewares (added to the root bundle) have access to the matched +// route pattern via r.Pattern, but execute before path parameters are parsed. +// Therefore, r.PathValue() will return empty strings in root middlewares. +// Middlewares on mounted groups execute after routing and have full access to path values. +func (b *Bundle) Use(middleware func(http.Handler) http.Handler, more ...func(http.Handler) http.Handler) { + // disallow adding middlewares after any routes have been registered on this bundle. + if b.routesLocked { + panic("routegroup: Use called after routes were registered on this bundle; add middlewares before registering routes or use Group/With for scoped middleware") + } + b.middlewares = append(b.middlewares, middleware) + b.middlewares = append(b.middlewares, more...) +} + +// With adds new middleware(s) to the Group and returns a new Group with the updated middleware stack. +// The With method is similar to Use, but instead of modifying the current Group, +// it returns a new Group instance with the added middleware(s). +// This allows for creating chain of middleware without affecting the original Group. +func (b *Bundle) With(middleware func(http.Handler) http.Handler, more ...func(http.Handler) http.Handler) *Bundle { + newMiddlewares := make([]func(http.Handler) http.Handler, len(b.middlewares), len(b.middlewares)+len(more)+1) + copy(newMiddlewares, b.middlewares) + newMiddlewares = append(newMiddlewares, middleware) + newMiddlewares = append(newMiddlewares, more...) + // preserve root pointer and rootCount + nb := &Bundle{mux: b.mux, basePath: b.basePath, middlewares: newMiddlewares, root: b.root, rootCount: b.rootCount} + if nb.root == nil { + // b is the root, so all b's middlewares are root middlewares + nb.root = b + nb.rootCount = len(b.middlewares) + } + return nb +} + +// Handle adds a new route to the Group's mux, applying all middlewares to the handler. +func (b *Bundle) Handle(pattern string, handler http.Handler) { + b.lockRoot() // lock root on first route registration + + // for file server paths (ending with /), preserve the pattern as-is + if strings.HasSuffix(pattern, "/") { + fullPath := b.basePath + pattern + b.mux.Handle(fullPath, b.wrapMiddleware(handler)) + return + } + b.register(pattern, handler.ServeHTTP) +} + +// HandleFiles is a helper to serve static files from a directory +func (b *Bundle) HandleFiles(pattern string, root http.FileSystem) { + b.lockRoot() // lock root on first route registration + + // normalize pattern to always have trailing slash + if !strings.HasSuffix(pattern, "/") { + pattern += "/" + } + + // build the full path for registration + fullPath := b.basePath + pattern + + if pattern == "/" && b.basePath == "" { + // root case - serve directly without stripping + b.mux.Handle("/", b.wrapMiddleware(http.FileServer(root))) + return + } + + // for both mounted groups and prefixed paths, strip the fullPath + handler := http.StripPrefix(strings.TrimSuffix(fullPath, "/"), http.FileServer(root)) + b.mux.Handle(fullPath, b.wrapMiddleware(handler)) +} + +// HandleFunc registers the handler function for the given pattern to the Group's mux. +// The handler is wrapped with the Group's middlewares. +func (b *Bundle) HandleFunc(pattern string, handler http.HandlerFunc) { + b.register(pattern, handler) +} + +// Handler returns the handler and the pattern that matches the request. +// It always returns a non-nil handler, see http.ServeMux.Handler documentation for details. +func (b *Bundle) Handler(r *http.Request) (h http.Handler, pattern string) { + return b.mux.Handler(r) +} + +// DisableNotFoundHandler used to disable auto-registration of a catch-all 404. +// Deprecated: now a no-op retained for API compatibility. +func (b *Bundle) DisableNotFoundHandler() {} + +// NotFoundHandler sets a custom handler for any unmatched routes (404 responses). +// Note: This handler is only used for true 404s. Requests to valid paths with +// incorrect HTTP methods will still return 405 Method Not Allowed with Allow header. +func (b *Bundle) NotFoundHandler(handler http.HandlerFunc) { + // always set on the root bundle so custom 404 works regardless of which bundle serves. + if b.root != nil { + b.root.notFound = handler + return + } + b.notFound = handler +} + +// matches non-space characters, spaces, then anything, i.e. "GET /path/to/resource" +var reGo122 = regexp.MustCompile(`^(\S+)\s+(.+)$`) + +func (b *Bundle) register(pattern string, handler http.HandlerFunc) { + b.lockRoot() // lock root on first route registration + matches := reGo122.FindStringSubmatch(pattern) + var path, method string + if len(matches) > 2 { // path in the form "GET /path/to/resource" + method = matches[1] + path = matches[2] + pattern = method + " " + b.basePath + path + } else { // path is just "/path/to/resource" + path = pattern + pattern = b.basePath + pattern + // method is not set intentionally here, the request pattern had no method part + } + // if the pattern is the root path on / change it to /{$} + // this keeps handling the root request without becoming a catch-all + if pattern == "/" || path == "/" { + if method != "" { // preserve the method part if it was set + pattern = method + " " + b.basePath + "/{$}" + } else { + pattern = b.basePath + "/{$}" // no method part, just the path + } + } + b.mux.HandleFunc(pattern, b.wrapMiddleware(handler).ServeHTTP) +} + +// Route allows for configuring the Group inside the configureFn function. +// When called on the root bundle, it automatically creates a new group to avoid +// accidentally modifying the root bundle's middleware stack. +func (b *Bundle) Route(configureFn func(*Bundle)) { + // if called on root bundle, auto-create a group for better UX + if b.root == nil { + child := b.Group() + configureFn(child) + // if child registered routes, lock root too to prevent Use() after routes + if child.routesLocked { + b.routesLocked = true + } + return + } + configureFn(b) +} + +// HandleRoot adds a handler for the group's root path without trailing slash. +// This avoids the 301 redirect that would occur with a "/" pattern. +// Method parameter can be empty to register for all HTTP methods. +func (b *Bundle) HandleRoot(method string, handler http.Handler) { + b.lockRoot() // lock root on first route registration + + // for empty base path, use "/" to match the root + pattern := b.basePath + if pattern == "" { + pattern = "/" + } + + // add method if specified + if method != "" { + pattern = method + " " + pattern + } + + b.mux.Handle(pattern, b.wrapMiddleware(handler)) +} + +// HandleRootFunc is like HandleRoot but takes a handler function. +func (b *Bundle) HandleRootFunc(method string, handler http.HandlerFunc) { + b.lockRoot() // lock root on first route registration + + // for empty base path, use "/" to match the root + pattern := b.basePath + if pattern == "" { + pattern = "/" + } + + // add method if specified + if method != "" { + pattern = method + " " + pattern + } + + b.mux.HandleFunc(pattern, b.wrapMiddleware(handler).ServeHTTP) +} + +// wrapMiddleware applies the registered middlewares to a handler. +func (b *Bundle) wrapMiddleware(handler http.Handler) http.Handler { + // root bundle: don't apply middlewares here, they're applied globally in ServeHTTP + if b.root == nil { + return handler + } + + // child bundle: apply only middlewares added after mounting (exclude inherited root middlewares) + start := b.rootCount + if start > len(b.middlewares) { + start = len(b.middlewares) // safety: ensure start doesn't exceed bounds + } + + for i := len(b.middlewares) - 1; i >= start; i-- { + handler = b.middlewares[i](handler) + } + return handler +} + +func (b *Bundle) clone() *Bundle { + middlewares := make([]func(http.Handler) http.Handler, len(b.middlewares)) + copy(middlewares, b.middlewares) + // preserve root pointer and rootCount + nb := &Bundle{mux: b.mux, basePath: b.basePath, middlewares: middlewares, root: b.root, rootCount: b.rootCount} + if nb.root == nil { + // b is the root, so all b's middlewares are root middlewares + nb.root = b + nb.rootCount = len(b.middlewares) + } + return nb +} + +// Wrap directly wraps the handler with the provided middleware(s). +func Wrap(handler http.Handler, mw1 func(http.Handler) http.Handler, mws ...func(http.Handler) http.Handler) http.Handler { + for i := len(mws) - 1; i >= 0; i-- { + handler = mws[i](handler) + } + return mw1(handler) // apply the first middleware +} + +// wrapGlobal applies only the root bundle's middlewares to the provided handler. +func (b *Bundle) wrapGlobal(handler http.Handler) http.Handler { + // resolve root bundle + root := b + if b.root != nil { + root = b.root + } + for i := len(root.middlewares) - 1; i >= 0; i-- { + handler = root.middlewares[i](handler) + } + return handler +} + +// lockRoot marks this bundle as having registered routes. +func (b *Bundle) lockRoot() { b.routesLocked = true } + +// statusRecorder is a minimal ResponseWriter that only records the status code. +// Used to probe what status the mux would return without actually writing a response. +type statusRecorder struct { + status int +} + +func (r *statusRecorder) Header() http.Header { + return make(http.Header) +} + +func (r *statusRecorder) Write([]byte) (int, error) { + return 0, nil +} + +func (r *statusRecorder) WriteHeader(status int) { + r.status = status +} diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md b/backend/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md index ff9c57e1..b3178e75 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md @@ -155,7 +155,7 @@ stored in base64 encoded form, which was redundant with the information in the type Token struct { Raw string // Raw contains the raw token Method SigningMethod // Method is the signing method used or to be used - Header map[string]interface{} // Header is the first segment of the token in decoded form + Header map[string]any // Header is the first segment of the token in decoded form Claims Claims // Claims is the second segment of the token in decoded form Signature []byte // Signature is the third segment of the token in decoded form Valid bool // Valid specifies if the token is valid diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go b/backend/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go index c929e4a0..06cd94d2 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go @@ -55,7 +55,7 @@ func (m *SigningMethodECDSA) Alg() string { // Verify implements token verification for the SigningMethod. // For this verify method, key must be an ecdsa.PublicKey struct -func (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key interface{}) error { +func (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key any) error { // Get the key var ecdsaKey *ecdsa.PublicKey switch k := key.(type) { @@ -89,7 +89,7 @@ func (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key interf // Sign implements token signing for the SigningMethod. // For this signing method, key must be an ecdsa.PrivateKey struct -func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) ([]byte, error) { +func (m *SigningMethodECDSA) Sign(signingString string, key any) ([]byte, error) { // Get the key var ecdsaKey *ecdsa.PrivateKey switch k := key.(type) { diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/ecdsa_utils.go b/backend/vendor/github.com/golang-jwt/jwt/v5/ecdsa_utils.go index 5700636d..44a3b7a1 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/ecdsa_utils.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/ecdsa_utils.go @@ -23,7 +23,7 @@ func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { } // Parse the key - var parsedKey interface{} + var parsedKey any if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { return nil, err @@ -50,7 +50,7 @@ func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { } // Parse the key - var parsedKey interface{} + var parsedKey any if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { if cert, err := x509.ParseCertificate(block.Bytes); err == nil { parsedKey = cert.PublicKey diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/ed25519.go b/backend/vendor/github.com/golang-jwt/jwt/v5/ed25519.go index c2138119..4159e57b 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/ed25519.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/ed25519.go @@ -33,7 +33,7 @@ func (m *SigningMethodEd25519) Alg() string { // Verify implements token verification for the SigningMethod. // For this verify method, key must be an ed25519.PublicKey -func (m *SigningMethodEd25519) Verify(signingString string, sig []byte, key interface{}) error { +func (m *SigningMethodEd25519) Verify(signingString string, sig []byte, key any) error { var ed25519Key ed25519.PublicKey var ok bool @@ -55,7 +55,7 @@ func (m *SigningMethodEd25519) Verify(signingString string, sig []byte, key inte // Sign implements token signing for the SigningMethod. // For this signing method, key must be an ed25519.PrivateKey -func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) ([]byte, error) { +func (m *SigningMethodEd25519) Sign(signingString string, key any) ([]byte, error) { var ed25519Key crypto.Signer var ok bool diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/ed25519_utils.go b/backend/vendor/github.com/golang-jwt/jwt/v5/ed25519_utils.go index cdb5e68e..6f46e886 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/ed25519_utils.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/ed25519_utils.go @@ -24,7 +24,7 @@ func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { } // Parse the key - var parsedKey interface{} + var parsedKey any if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { return nil, err } @@ -49,7 +49,7 @@ func ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { } // Parse the key - var parsedKey interface{} + var parsedKey any if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { return nil, err } diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/errors.go b/backend/vendor/github.com/golang-jwt/jwt/v5/errors.go index 23bb616d..14e00751 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/errors.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/errors.go @@ -2,6 +2,7 @@ package jwt import ( "errors" + "fmt" "strings" ) @@ -47,3 +48,42 @@ func joinErrors(errs ...error) error { errs: errs, } } + +// Unwrap implements the multiple error unwrapping for this error type, which is +// possible in Go 1.20. +func (je joinedError) Unwrap() []error { + return je.errs +} + +// newError creates a new error message with a detailed error message. The +// message will be prefixed with the contents of the supplied error type. +// Additionally, more errors, that provide more context can be supplied which +// will be appended to the message. This makes use of Go 1.20's possibility to +// include more than one %w formatting directive in [fmt.Errorf]. +// +// For example, +// +// newError("no keyfunc was provided", ErrTokenUnverifiable) +// +// will produce the error string +// +// "token is unverifiable: no keyfunc was provided" +func newError(message string, err error, more ...error) error { + var format string + var args []any + if message != "" { + format = "%w: %s" + args = []any{err, message} + } else { + format = "%w" + args = []any{err} + } + + for _, e := range more { + format += ": %w" + args = append(args, e) + } + + err = fmt.Errorf(format, args...) + return err +} diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/errors_go1_20.go b/backend/vendor/github.com/golang-jwt/jwt/v5/errors_go1_20.go deleted file mode 100644 index a893d355..00000000 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/errors_go1_20.go +++ /dev/null @@ -1,47 +0,0 @@ -//go:build go1.20 -// +build go1.20 - -package jwt - -import ( - "fmt" -) - -// Unwrap implements the multiple error unwrapping for this error type, which is -// possible in Go 1.20. -func (je joinedError) Unwrap() []error { - return je.errs -} - -// newError creates a new error message with a detailed error message. The -// message will be prefixed with the contents of the supplied error type. -// Additionally, more errors, that provide more context can be supplied which -// will be appended to the message. This makes use of Go 1.20's possibility to -// include more than one %w formatting directive in [fmt.Errorf]. -// -// For example, -// -// newError("no keyfunc was provided", ErrTokenUnverifiable) -// -// will produce the error string -// -// "token is unverifiable: no keyfunc was provided" -func newError(message string, err error, more ...error) error { - var format string - var args []any - if message != "" { - format = "%w: %s" - args = []any{err, message} - } else { - format = "%w" - args = []any{err} - } - - for _, e := range more { - format += ": %w" - args = append(args, e) - } - - err = fmt.Errorf(format, args...) - return err -} diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go b/backend/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go deleted file mode 100644 index 2ad542f0..00000000 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:build !go1.20 -// +build !go1.20 - -package jwt - -import ( - "errors" - "fmt" -) - -// Is implements checking for multiple errors using [errors.Is], since multiple -// error unwrapping is not possible in versions less than Go 1.20. -func (je joinedError) Is(err error) bool { - for _, e := range je.errs { - if errors.Is(e, err) { - return true - } - } - - return false -} - -// wrappedErrors is a workaround for wrapping multiple errors in environments -// where Go 1.20 is not available. It basically uses the already implemented -// functionality of joinedError to handle multiple errors with supplies a -// custom error message that is identical to the one we produce in Go 1.20 using -// multiple %w directives. -type wrappedErrors struct { - msg string - joinedError -} - -// Error returns the stored error string -func (we wrappedErrors) Error() string { - return we.msg -} - -// newError creates a new error message with a detailed error message. The -// message will be prefixed with the contents of the supplied error type. -// Additionally, more errors, that provide more context can be supplied which -// will be appended to the message. Since we cannot use of Go 1.20's possibility -// to include more than one %w formatting directive in [fmt.Errorf], we have to -// emulate that. -// -// For example, -// -// newError("no keyfunc was provided", ErrTokenUnverifiable) -// -// will produce the error string -// -// "token is unverifiable: no keyfunc was provided" -func newError(message string, err error, more ...error) error { - // We cannot wrap multiple errors here with %w, so we have to be a little - // bit creative. Basically, we are using %s instead of %w to produce the - // same error message and then throw the result into a custom error struct. - var format string - var args []any - if message != "" { - format = "%s: %s" - args = []any{err, message} - } else { - format = "%s" - args = []any{err} - } - errs := []error{err} - - for _, e := range more { - format += ": %s" - args = append(args, e) - errs = append(errs, e) - } - - err = &wrappedErrors{ - msg: fmt.Sprintf(format, args...), - joinedError: joinedError{errs: errs}, - } - return err -} diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/hmac.go b/backend/vendor/github.com/golang-jwt/jwt/v5/hmac.go index aca600ce..1bef138c 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/hmac.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/hmac.go @@ -55,7 +55,7 @@ func (m *SigningMethodHMAC) Alg() string { // about this, and why we intentionally are not supporting string as a key can // be found on our usage guide // https://golang-jwt.github.io/jwt/usage/signing_methods/#signing-methods-and-key-types. -func (m *SigningMethodHMAC) Verify(signingString string, sig []byte, key interface{}) error { +func (m *SigningMethodHMAC) Verify(signingString string, sig []byte, key any) error { // Verify the key is the right type keyBytes, ok := key.([]byte) if !ok { @@ -88,7 +88,7 @@ func (m *SigningMethodHMAC) Verify(signingString string, sig []byte, key interfa // cryptographically random source, e.g. crypto/rand. Additional information // about this, and why we intentionally are not supporting string as a key can // be found on our usage guide https://golang-jwt.github.io/jwt/usage/signing_methods/. -func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) ([]byte, error) { +func (m *SigningMethodHMAC) Sign(signingString string, key any) ([]byte, error) { if keyBytes, ok := key.([]byte); ok { if !m.Hash.Available() { return nil, ErrHashUnavailable diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/map_claims.go b/backend/vendor/github.com/golang-jwt/jwt/v5/map_claims.go index b2b51a1f..3b920527 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/map_claims.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/map_claims.go @@ -5,9 +5,9 @@ import ( "fmt" ) -// MapClaims is a claims type that uses the map[string]interface{} for JSON +// MapClaims is a claims type that uses the map[string]any for JSON // decoding. This is the default claims type if you don't supply one -type MapClaims map[string]interface{} +type MapClaims map[string]any // GetExpirationTime implements the Claims interface. func (m MapClaims) GetExpirationTime() (*NumericDate, error) { @@ -73,7 +73,7 @@ func (m MapClaims) parseClaimsString(key string) (ClaimStrings, error) { cs = append(cs, v) case []string: cs = v - case []interface{}: + case []any: for _, a := range v { vs, ok := a.(string) if !ok { @@ -92,7 +92,7 @@ func (m MapClaims) parseClaimsString(key string) (ClaimStrings, error) { func (m MapClaims) parseString(key string) (string, error) { var ( ok bool - raw interface{} + raw any iss string ) raw, ok = m[key] diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/none.go b/backend/vendor/github.com/golang-jwt/jwt/v5/none.go index 685c2ea3..624ad55e 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/none.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/none.go @@ -25,7 +25,7 @@ func (m *signingMethodNone) Alg() string { } // Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key -func (m *signingMethodNone) Verify(signingString string, sig []byte, key interface{}) (err error) { +func (m *signingMethodNone) Verify(signingString string, sig []byte, key any) (err error) { // Key must be UnsafeAllowNoneSignatureType to prevent accidentally // accepting 'none' signing method if _, ok := key.(unsafeNoneMagicConstant); !ok { @@ -41,7 +41,7 @@ func (m *signingMethodNone) Verify(signingString string, sig []byte, key interfa } // Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key -func (m *signingMethodNone) Sign(signingString string, key interface{}) ([]byte, error) { +func (m *signingMethodNone) Sign(signingString string, key any) ([]byte, error) { if _, ok := key.(unsafeNoneMagicConstant); ok { return []byte{}, nil } diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/parser_option.go b/backend/vendor/github.com/golang-jwt/jwt/v5/parser_option.go index 88a780fb..43157355 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/parser_option.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/parser_option.go @@ -66,20 +66,37 @@ func WithExpirationRequired() ParserOption { } } -// WithAudience configures the validator to require the specified audience in -// the `aud` claim. Validation will fail if the audience is not listed in the -// token or the `aud` claim is missing. +// WithAudience configures the validator to require any of the specified +// audiences in the `aud` claim. Validation will fail if the audience is not +// listed in the token or the `aud` claim is missing. // // NOTE: While the `aud` claim is OPTIONAL in a JWT, the handling of it is // application-specific. Since this validation API is helping developers in // writing secure application, we decided to REQUIRE the existence of the claim, // if an audience is expected. -func WithAudience(aud string) ParserOption { +func WithAudience(aud ...string) ParserOption { return func(p *Parser) { p.validator.expectedAud = aud } } +// WithAllAudiences configures the validator to require all the specified +// audiences in the `aud` claim. Validation will fail if the specified audiences +// are not listed in the token or the `aud` claim is missing. Duplicates within +// the list are de-duplicated since internally, we use a map to look up the +// audiences. +// +// NOTE: While the `aud` claim is OPTIONAL in a JWT, the handling of it is +// application-specific. Since this validation API is helping developers in +// writing secure application, we decided to REQUIRE the existence of the claim, +// if an audience is expected. +func WithAllAudiences(aud ...string) ParserOption { + return func(p *Parser) { + p.validator.expectedAud = aud + p.validator.expectAllAud = true + } +} + // WithIssuer configures the validator to require the specified issuer in the // `iss` claim. Validation will fail if a different issuer is specified in the // token or the `iss` claim is missing. diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/rsa.go b/backend/vendor/github.com/golang-jwt/jwt/v5/rsa.go index 83cbee6a..98b960a7 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/rsa.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/rsa.go @@ -46,7 +46,7 @@ func (m *SigningMethodRSA) Alg() string { // Verify implements token verification for the SigningMethod // For this signing method, must be an *rsa.PublicKey structure. -func (m *SigningMethodRSA) Verify(signingString string, sig []byte, key interface{}) error { +func (m *SigningMethodRSA) Verify(signingString string, sig []byte, key any) error { var rsaKey *rsa.PublicKey var ok bool @@ -67,7 +67,7 @@ func (m *SigningMethodRSA) Verify(signingString string, sig []byte, key interfac // Sign implements token signing for the SigningMethod // For this signing method, must be an *rsa.PrivateKey structure. -func (m *SigningMethodRSA) Sign(signingString string, key interface{}) ([]byte, error) { +func (m *SigningMethodRSA) Sign(signingString string, key any) ([]byte, error) { var rsaKey *rsa.PrivateKey var ok bool diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go b/backend/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go index 28c386ec..f17590cc 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go @@ -1,6 +1,3 @@ -//go:build go1.4 -// +build go1.4 - package jwt import ( @@ -82,7 +79,7 @@ func init() { // Verify implements token verification for the SigningMethod. // For this verify method, key must be an rsa.PublicKey struct -func (m *SigningMethodRSAPSS) Verify(signingString string, sig []byte, key interface{}) error { +func (m *SigningMethodRSAPSS) Verify(signingString string, sig []byte, key any) error { var rsaKey *rsa.PublicKey switch k := key.(type) { case *rsa.PublicKey: @@ -108,7 +105,7 @@ func (m *SigningMethodRSAPSS) Verify(signingString string, sig []byte, key inter // Sign implements token signing for the SigningMethod. // For this signing method, key must be an rsa.PrivateKey struct -func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) ([]byte, error) { +func (m *SigningMethodRSAPSS) Sign(signingString string, key any) ([]byte, error) { var rsaKey *rsa.PrivateKey switch k := key.(type) { diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/rsa_utils.go b/backend/vendor/github.com/golang-jwt/jwt/v5/rsa_utils.go index b3aeebbe..f22c3d06 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/rsa_utils.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/rsa_utils.go @@ -23,7 +23,7 @@ func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { return nil, ErrKeyMustBePEMEncoded } - var parsedKey interface{} + var parsedKey any if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { return nil, err @@ -53,7 +53,7 @@ func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.Pr return nil, ErrKeyMustBePEMEncoded } - var parsedKey interface{} + var parsedKey any var blockDecrypted []byte if blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil { @@ -86,7 +86,7 @@ func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { } // Parse the key - var parsedKey interface{} + var parsedKey any if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { if cert, err := x509.ParseCertificate(block.Bytes); err == nil { parsedKey = cert.PublicKey diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/signing_method.go b/backend/vendor/github.com/golang-jwt/jwt/v5/signing_method.go index 0d73631c..096d0ed4 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/signing_method.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/signing_method.go @@ -12,9 +12,9 @@ var signingMethodLock = new(sync.RWMutex) // signature in Sign. The signature is then usually base64 encoded as part of a // JWT. type SigningMethod interface { - Verify(signingString string, sig []byte, key interface{}) error // Returns nil if signature is valid - Sign(signingString string, key interface{}) ([]byte, error) // Returns signature or error - Alg() string // returns the alg identifier for this method (example: 'HS256') + Verify(signingString string, sig []byte, key any) error // Returns nil if signature is valid + Sign(signingString string, key any) ([]byte, error) // Returns signature or error + Alg() string // returns the alg identifier for this method (example: 'HS256') } // RegisterSigningMethod registers the "alg" name and a factory function for signing method. diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/token.go b/backend/vendor/github.com/golang-jwt/jwt/v5/token.go index 9c7f4ab0..3f715588 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/token.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/token.go @@ -11,9 +11,9 @@ import ( // Token. This allows you to use properties in the Header of the token (such as // `kid`) to identify which key to use. // -// The returned interface{} may be a single key or a VerificationKeySet containing +// The returned any may be a single key or a VerificationKeySet containing // multiple keys. -type Keyfunc func(*Token) (interface{}, error) +type Keyfunc func(*Token) (any, error) // VerificationKey represents a public or secret key for verifying a token's signature. type VerificationKey interface { @@ -28,12 +28,12 @@ type VerificationKeySet struct { // Token represents a JWT Token. Different fields will be used depending on // whether you're creating or parsing/verifying a token. type Token struct { - Raw string // Raw contains the raw token. Populated when you [Parse] a token - Method SigningMethod // Method is the signing method used or to be used - Header map[string]interface{} // Header is the first segment of the token in decoded form - Claims Claims // Claims is the second segment of the token in decoded form - Signature []byte // Signature is the third segment of the token in decoded form. Populated when you Parse a token - Valid bool // Valid specifies if the token is valid. Populated when you Parse/Verify a token + Raw string // Raw contains the raw token. Populated when you [Parse] a token + Method SigningMethod // Method is the signing method used or to be used + Header map[string]any // Header is the first segment of the token in decoded form + Claims Claims // Claims is the second segment of the token in decoded form + Signature []byte // Signature is the third segment of the token in decoded form. Populated when you Parse a token + Valid bool // Valid specifies if the token is valid. Populated when you Parse/Verify a token } // New creates a new [Token] with the specified signing method and an empty map @@ -46,7 +46,7 @@ func New(method SigningMethod, opts ...TokenOption) *Token { // claims. Additional options can be specified, but are currently unused. func NewWithClaims(method SigningMethod, claims Claims, opts ...TokenOption) *Token { return &Token{ - Header: map[string]interface{}{ + Header: map[string]any{ "typ": "JWT", "alg": method.Alg(), }, @@ -60,7 +60,7 @@ func NewWithClaims(method SigningMethod, claims Claims, opts ...TokenOption) *To // https://golang-jwt.github.io/jwt/usage/signing_methods/#signing-methods-and-key-types // for an overview of the different signing methods and their respective key // types. -func (t *Token) SignedString(key interface{}) (string, error) { +func (t *Token) SignedString(key any) (string, error) { sstr, err := t.SigningString() if err != nil { return "", err diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/types.go b/backend/vendor/github.com/golang-jwt/jwt/v5/types.go index b2655a9e..a3e0ef12 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/types.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/types.go @@ -103,7 +103,7 @@ func (date *NumericDate) UnmarshalJSON(b []byte) (err error) { type ClaimStrings []string func (s *ClaimStrings) UnmarshalJSON(data []byte) (err error) { - var value interface{} + var value any if err = json.Unmarshal(data, &value); err != nil { return err @@ -116,7 +116,7 @@ func (s *ClaimStrings) UnmarshalJSON(data []byte) (err error) { aud = append(aud, v) case []string: aud = ClaimStrings(v) - case []interface{}: + case []any: for _, vv := range v { vs, ok := vv.(string) if !ok { diff --git a/backend/vendor/github.com/golang-jwt/jwt/v5/validator.go b/backend/vendor/github.com/golang-jwt/jwt/v5/validator.go index 008ecd87..92b5c057 100644 --- a/backend/vendor/github.com/golang-jwt/jwt/v5/validator.go +++ b/backend/vendor/github.com/golang-jwt/jwt/v5/validator.go @@ -1,8 +1,8 @@ package jwt import ( - "crypto/subtle" "fmt" + "slices" "time" ) @@ -52,8 +52,12 @@ type Validator struct { verifyIat bool // expectedAud contains the audience this token expects. Supplying an empty - // string will disable aud checking. - expectedAud string + // slice will disable aud checking. + expectedAud []string + + // expectAllAud specifies whether all expected audiences must be present in + // the token. If false, only one of the expected audiences must be present. + expectAllAud bool // expectedIss contains the issuer this token expects. Supplying an empty // string will disable iss checking. @@ -88,7 +92,7 @@ func NewValidator(opts ...ParserOption) *Validator { func (v *Validator) Validate(claims Claims) error { var ( now time.Time - errs []error = make([]error, 0, 6) + errs = make([]error, 0, 6) err error ) @@ -120,8 +124,8 @@ func (v *Validator) Validate(claims Claims) error { } // If we have an expected audience, we also require the audience claim - if v.expectedAud != "" { - if err = v.verifyAudience(claims, v.expectedAud, true); err != nil { + if len(v.expectedAud) > 0 { + if err = v.verifyAudience(claims, v.expectedAud, v.expectAllAud); err != nil { errs = append(errs, err) } } @@ -226,33 +230,39 @@ func (v *Validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) // // Additionally, if any error occurs while retrieving the claim, e.g., when its // the wrong type, an ErrTokenUnverifiable error will be returned. -func (v *Validator) verifyAudience(claims Claims, cmp string, required bool) error { +func (v *Validator) verifyAudience(claims Claims, cmp []string, expectAllAud bool) error { aud, err := claims.GetAudience() if err != nil { return err } - if len(aud) == 0 { + // Check that aud exists and is not empty. We only require the aud claim + // if we expect at least one audience to be present. + if len(aud) == 0 || len(aud) == 1 && aud[0] == "" { + required := len(v.expectedAud) > 0 return errorIfRequired(required, "aud") } - // use a var here to keep constant time compare when looping over a number of claims - result := false - - var stringClaims string - for _, a := range aud { - if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 { - result = true + if !expectAllAud { + for _, a := range aud { + // If we only expect one match, we can stop early if we find a match + if slices.Contains(cmp, a) { + return nil + } } - stringClaims = stringClaims + a + + return ErrTokenInvalidAudience } - // case where "" is sent in one or many aud claims - if stringClaims == "" { - return errorIfRequired(required, "aud") + // Note that we are looping cmp here to ensure that all expected audiences + // are present in the aud claim. + for _, a := range cmp { + if !slices.Contains(aud, a) { + return ErrTokenInvalidAudience + } } - return errorIfFalse(result, ErrTokenInvalidAudience) + return nil } // verifyIssuer compares the iss claim in claims against cmp. diff --git a/backend/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/backend/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 7e19eba0..ffb24e8e 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/backend/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -390,7 +390,8 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, failMessage, msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -403,7 +404,8 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, failMessage, msgAndArgs...) } // Less asserts that the first element is less than the second @@ -415,7 +417,8 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareLess}, failMessage, msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -428,7 +431,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2) + return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, failMessage, msgAndArgs...) } // Positive asserts that the specified element is positive @@ -440,7 +444,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, "\"%v\" is not positive", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not positive", e) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...) } // Negative asserts that the specified element is negative @@ -452,7 +457,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, "\"%v\" is not negative", msgAndArgs...) + failMessage := fmt.Sprintf("\"%v\" is not negative", e) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...) } func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { @@ -468,11 +474,11 @@ func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedCompare compareResult, isComparable := compare(e1, e2, e1Kind) if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + return Fail(t, fmt.Sprintf(`Can not compare type "%T"`, e1), msgAndArgs...) } if !containsValue(allowedComparesResults, compareResult) { - return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) + return Fail(t, failMessage, msgAndArgs...) } return true diff --git a/backend/vendor/github.com/stretchr/testify/assert/assertion_format.go b/backend/vendor/github.com/stretchr/testify/assert/assertion_format.go index 19063416..c592f6ad 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/backend/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -50,10 +50,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // assert.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -117,10 +126,8 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// assert.Errorf(t, err, "error message %s", "formatted") func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -438,7 +445,19 @@ func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interf return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) } +// IsNotTypef asserts that the specified objects are not of the same type. +// +// assert.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsNotType(t, theType, object, append([]interface{}{msg}, args...)...) +} + // IsTypef asserts that the specified objects are of the same type. +// +// assert.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -585,8 +604,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if assert.NotEmptyf(t, obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -693,12 +711,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") // assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -782,11 +803,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg return Same(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") // assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/backend/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/backend/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 21629087..58db9284 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/backend/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -92,10 +92,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st return ElementsMatchf(a.t, listA, listB, msg, args...) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -103,10 +112,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { return Empty(a.t, object, msgAndArgs...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -224,10 +242,8 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// a.Error(err) func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -297,10 +313,8 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// a.Errorf(err, "error message %s", "formatted") func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -868,7 +882,29 @@ func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...in return IsNonIncreasingf(a.t, object, msg, args...) } +// IsNotType asserts that the specified objects are not of the same type. +// +// a.IsNotType(&NotMyStruct{}, &MyStruct{}) +func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNotType(a.t, theType, object, msgAndArgs...) +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsNotTypef(a.t, theType, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. +// +// a.IsType(&MyStruct{}, &MyStruct{}) func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -877,6 +913,8 @@ func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAnd } // IsTypef asserts that the specified objects are of the same type. +// +// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1162,8 +1200,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg return NotElementsMatchf(a.t, listA, listB, msg, args...) } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) @@ -1175,8 +1212,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo return NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -1378,12 +1414,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri return NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubset([1, 3, 4], [1, 2]) // a.NotSubset({"x": 1, "y": 2}, {"z": 3}) +// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) +// a.NotSubset({"x": 1, "y": 2}, ["z"]) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1391,12 +1430,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs return NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") // a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1556,11 +1598,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, return Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subset([1, 2, 3], [1, 2]) // a.Subset({"x": 1, "y": 2}, {"x": 1}) +// a.Subset([1, 2, 3], {1: "one", 2: "two"}) +// a.Subset({"x": 1, "y": 2}, ["x"]) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1568,11 +1614,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... return Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") // a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/backend/vendor/github.com/stretchr/testify/assert/assertion_order.go b/backend/vendor/github.com/stretchr/testify/assert/assertion_order.go index 1d2f7182..2fdf80fd 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/backend/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -33,7 +33,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareR compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) + return Fail(t, fmt.Sprintf(`Can not compare type "%T" and "%T"`, value, prevValue), msgAndArgs...) } if !containsValue(allowedComparesResults, compareResult) { diff --git a/backend/vendor/github.com/stretchr/testify/assert/assertions.go b/backend/vendor/github.com/stretchr/testify/assert/assertions.go index 4e91332b..de8de0cb 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/backend/vendor/github.com/stretchr/testify/assert/assertions.go @@ -210,59 +210,77 @@ the problem actually occurred in calling code.*/ // of each stack frame leading from the current test to the assert call that // failed. func CallerInfo() []string { - var pc uintptr - var ok bool var file string var line int var name string + const stackFrameBufferSize = 10 + pcs := make([]uintptr, stackFrameBufferSize) + callers := []string{} - for i := 0; ; i++ { - pc, file, line, ok = runtime.Caller(i) - if !ok { - // The breaks below failed to terminate the loop, and we ran off the - // end of the call stack. + offset := 1 + + for { + n := runtime.Callers(offset, pcs) + + if n == 0 { break } - // This is a huge edge case, but it will panic if this is the case, see #180 - if file == "" { - break - } + frames := runtime.CallersFrames(pcs[:n]) - f := runtime.FuncForPC(pc) - if f == nil { - break - } - name = f.Name() + for { + frame, more := frames.Next() + pc = frame.PC + file = frame.File + line = frame.Line - // testing.tRunner is the standard library function that calls - // tests. Subtests are called directly by tRunner, without going through - // the Test/Benchmark/Example function that contains the t.Run calls, so - // with subtests we should break when we hit tRunner, without adding it - // to the list of callers. - if name == "testing.tRunner" { - break - } + // This is a huge edge case, but it will panic if this is the case, see #180 + if file == "" { + break + } - parts := strings.Split(file, "/") - if len(parts) > 1 { - filename := parts[len(parts)-1] - dir := parts[len(parts)-2] - if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { - callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + f := runtime.FuncForPC(pc) + if f == nil { + break + } + name = f.Name() + + // testing.tRunner is the standard library function that calls + // tests. Subtests are called directly by tRunner, without going through + // the Test/Benchmark/Example function that contains the t.Run calls, so + // with subtests we should break when we hit tRunner, without adding it + // to the list of callers. + if name == "testing.tRunner" { + break + } + + parts := strings.Split(file, "/") + if len(parts) > 1 { + filename := parts[len(parts)-1] + dir := parts[len(parts)-2] + if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + } + } + + // Drop the package + dotPos := strings.LastIndexByte(name, '.') + name = name[dotPos+1:] + if isTest(name, "Test") || + isTest(name, "Benchmark") || + isTest(name, "Example") { + break + } + + if !more { + break } } - // Drop the package - segments := strings.Split(name, ".") - name = segments[len(segments)-1] - if isTest(name, "Test") || - isTest(name, "Benchmark") || - isTest(name, "Example") { - break - } + // Next batch + offset += cap(pcs) } return callers @@ -437,17 +455,34 @@ func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, return true } +func isType(expectedType, object interface{}) bool { + return ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) +} + // IsType asserts that the specified objects are of the same type. -func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { +// +// assert.IsType(t, &MyStruct{}, &MyStruct{}) +func IsType(t TestingT, expectedType, object interface{}, msgAndArgs ...interface{}) bool { + if isType(expectedType, object) { + return true + } if h, ok := t.(tHelper); ok { h.Helper() } + return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...) +} - if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { - return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) +// IsNotType asserts that the specified objects are not of the same type. +// +// assert.IsNotType(t, &NotMyStruct{}, &MyStruct{}) +func IsNotType(t TestingT, theType, object interface{}, msgAndArgs ...interface{}) bool { + if !isType(theType, object) { + return true } - - return true + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, fmt.Sprintf("Object type expected to be different than %T", theType), msgAndArgs...) } // Equal asserts that two objects are equal. @@ -475,7 +510,6 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) } return true - } // validateEqualArgs checks whether provided arguments can be safely used in the @@ -510,8 +544,9 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b if !same { // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ - "expected: %p %#v\n"+ - "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) + "expected: %p %#[1]v\n"+ + "actual : %p %#[2]v", + expected, actual), msgAndArgs...) } return true @@ -530,14 +565,14 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} same, ok := samePointers(expected, actual) if !ok { - //fails when the arguments are not pointers + // fails when the arguments are not pointers return !(Fail(t, "Both arguments must be pointers", msgAndArgs...)) } if same { return Fail(t, fmt.Sprintf( - "Expected and actual point to the same object: %p %#v", - expected, expected), msgAndArgs...) + "Expected and actual point to the same object: %p %#[1]v", + expected), msgAndArgs...) } return true } @@ -549,7 +584,7 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} func samePointers(first, second interface{}) (same bool, ok bool) { firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { - return false, false //not both are pointers + return false, false // not both are pointers } firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) @@ -610,7 +645,6 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } return true - } // EqualExportedValues asserts that the types of two objects are equal and their public @@ -665,7 +699,6 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} } return Equal(t, expected, actual, msgAndArgs...) - } // NotNil asserts that the specified object is not nil. @@ -715,37 +748,45 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // isEmpty gets whether the specified object is considered empty or not. func isEmpty(object interface{}) bool { - // get nil case out of the way if object == nil { return true } - objValue := reflect.ValueOf(object) - - switch objValue.Kind() { - // collection types are empty when they have no element - case reflect.Chan, reflect.Map, reflect.Slice: - return objValue.Len() == 0 - // pointers are empty if nil or if the value they point to is empty - case reflect.Ptr: - if objValue.IsNil() { - return true - } - deref := objValue.Elem().Interface() - return isEmpty(deref) - // for all other types, compare against the zero value - // array types are empty when they match their zero-initialized state - default: - zero := reflect.Zero(objValue.Type()) - return reflect.DeepEqual(object, zero.Interface()) - } + return isEmptyValue(reflect.ValueOf(object)) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// isEmptyValue gets whether the specified reflect.Value is considered empty or not. +func isEmptyValue(objValue reflect.Value) bool { + if objValue.IsZero() { + return true + } + // Special cases of non-zero values that we consider empty + switch objValue.Kind() { + // collection types are empty when they have no element + // Note: array types are empty when they match their zero-initialized state. + case reflect.Chan, reflect.Map, reflect.Slice: + return objValue.Len() == 0 + // non-nil pointers are empty if the value they point to is empty + case reflect.Ptr: + return isEmptyValue(objValue.Elem()) + } + return false +} + +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // assert.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { @@ -756,11 +797,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { } return pass - } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if assert.NotEmpty(t, obj) { // assert.Equal(t, "two", obj[1]) @@ -775,7 +814,6 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { } return pass - } // getLen tries to get the length of an object. @@ -819,7 +857,6 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { } return true - } // False asserts that the specified value is false. @@ -834,7 +871,6 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { } return true - } // NotEqual asserts that the specified values are NOT equal. @@ -857,7 +893,6 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ } return true - } // NotEqualValues asserts that two objects are not equal even when converted to the same type @@ -880,7 +915,6 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte // return (true, false) if element was not found. // return (true, true) if element was found. func containsElement(list interface{}, element interface{}) (ok, found bool) { - listValue := reflect.ValueOf(list) listType := reflect.TypeOf(list) if listType == nil { @@ -915,7 +949,6 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) { } } return true, false - } // Contains asserts that the specified string, list(array, slice...) or map contains the @@ -938,7 +971,6 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo } return true - } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the @@ -961,14 +993,17 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) } return true - } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.Subset(t, [1, 2, 3], [1, 2]) // assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// assert.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) +// assert.Subset(t, {"x": 1, "y": 2}, ["x"]) func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -983,7 +1018,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok } subsetKind := reflect.TypeOf(subset).Kind() - if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } @@ -1007,6 +1042,13 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok } subsetList := reflect.ValueOf(subset) + if subsetKind == reflect.Map { + keys := make([]interface{}, subsetList.Len()) + for idx, key := range subsetList.MapKeys() { + keys[idx] = key.Interface() + } + subsetList = reflect.ValueOf(keys) + } for i := 0; i < subsetList.Len(); i++ { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) @@ -1021,12 +1063,15 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // assert.NotSubset(t, [1, 3, 4], [1, 2]) // assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// assert.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) +// assert.NotSubset(t, {"x": 1, "y": 2}, ["z"]) func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1041,7 +1086,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } subsetKind := reflect.TypeOf(subset).Kind() - if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { + if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } @@ -1065,11 +1110,18 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } subsetList := reflect.ValueOf(subset) + if subsetKind == reflect.Map { + keys := make([]interface{}, subsetList.Len()) + for idx, key := range subsetList.MapKeys() { + keys[idx] = key.Interface() + } + subsetList = reflect.ValueOf(keys) + } for i := 0; i < subsetList.Len(); i++ { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...) } if !found { return true @@ -1591,10 +1643,8 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// assert.Error(t, err) func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { @@ -1667,7 +1717,6 @@ func matchRegexp(rx interface{}, str interface{}) bool { default: return r.MatchString(fmt.Sprint(v)) } - } // Regexp asserts that a specified regexp matches a string. @@ -1703,7 +1752,6 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf } return !match - } // Zero asserts that i is the zero value for its type. @@ -1814,6 +1862,11 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) } + // Shortcut if same bytes + if actual == expected { + return true + } + if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) } @@ -1832,6 +1885,11 @@ func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) } + // Shortcut if same bytes + if actual == expected { + return true + } + if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) } @@ -1933,6 +1991,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } ch := make(chan bool, 1) + checkCond := func() { ch <- condition() } timer := time.NewTimer(waitFor) defer timer.Stop() @@ -1940,18 +1999,23 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tick: - tick = nil - go func() { ch <- condition() }() + case <-tickC: + tickC = nil + go checkCond() case v := <-ch: if v { return true } - tick = ticker.C + tickC = ticker.C } } } @@ -1964,6 +2028,9 @@ type CollectT struct { errors []error } +// Helper is like [testing.T.Helper] but does nothing. +func (CollectT) Helper() {} + // Errorf collects the error. func (c *CollectT) Errorf(format string, args ...interface{}) { c.errors = append(c.errors, fmt.Errorf(format, args...)) @@ -2021,35 +2088,42 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time var lastFinishedTickErrs []error ch := make(chan *CollectT, 1) + checkCond := func() { + collect := new(CollectT) + defer func() { + ch <- collect + }() + condition(collect) + } + timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: for _, err := range lastFinishedTickErrs { t.Errorf("%v", err) } return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tick: - tick = nil - go func() { - collect := new(CollectT) - defer func() { - ch <- collect - }() - condition(collect) - }() + case <-tickC: + tickC = nil + go checkCond() case collect := <-ch: if !collect.failed() { return true } // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. lastFinishedTickErrs = collect.errors - tick = ticker.C + tickC = ticker.C } } } @@ -2064,6 +2138,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D } ch := make(chan bool, 1) + checkCond := func() { ch <- condition() } timer := time.NewTimer(waitFor) defer timer.Stop() @@ -2071,18 +2146,23 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D ticker := time.NewTicker(tick) defer ticker.Stop() - for tick := ticker.C; ; { + var tickC <-chan time.Time + + // Check the condition once first on the initial call. + go checkCond() + + for { select { case <-timer.C: return true - case <-tick: - tick = nil - go func() { ch <- condition() }() + case <-tickC: + tickC = nil + go checkCond() case v := <-ch: if v { return Fail(t, "Condition satisfied", msgAndArgs...) } - tick = ticker.C + tickC = ticker.C } } } @@ -2100,9 +2180,12 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { var expectedText string if target != nil { expectedText = target.Error() + if err == nil { + return Fail(t, fmt.Sprintf("Expected error with %q in chain but got nil.", expectedText), msgAndArgs...) + } } - chain := buildErrorChainString(err) + chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ "expected: %q\n"+ @@ -2125,7 +2208,7 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { expectedText = target.Error() } - chain := buildErrorChainString(err) + chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ "found: %q\n"+ @@ -2143,11 +2226,17 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ return true } - chain := buildErrorChainString(err) + expectedType := reflect.TypeOf(target).Elem().String() + if err == nil { + return Fail(t, fmt.Sprintf("An error is expected but got nil.\n"+ + "expected: %s", expectedType), msgAndArgs...) + } + + chain := buildErrorChainString(err, true) return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ - "expected: %q\n"+ - "in chain: %s", target, chain, + "expected: %s\n"+ + "in chain: %s", expectedType, chain, ), msgAndArgs...) } @@ -2161,24 +2250,46 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa return true } - chain := buildErrorChainString(err) + chain := buildErrorChainString(err, true) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ - "found: %q\n"+ - "in chain: %s", target, chain, + "found: %s\n"+ + "in chain: %s", reflect.TypeOf(target).Elem().String(), chain, ), msgAndArgs...) } -func buildErrorChainString(err error) string { +func unwrapAll(err error) (errs []error) { + errs = append(errs, err) + switch x := err.(type) { + case interface{ Unwrap() error }: + err = x.Unwrap() + if err == nil { + return + } + errs = append(errs, unwrapAll(err)...) + case interface{ Unwrap() []error }: + for _, err := range x.Unwrap() { + errs = append(errs, unwrapAll(err)...) + } + } + return +} + +func buildErrorChainString(err error, withType bool) string { if err == nil { return "" } - e := errors.Unwrap(err) - chain := fmt.Sprintf("%q", err.Error()) - for e != nil { - chain += fmt.Sprintf("\n\t%q", e.Error()) - e = errors.Unwrap(e) + var chain string + errs := unwrapAll(err) + for i := range errs { + if i != 0 { + chain += "\n\t" + } + chain += fmt.Sprintf("%q", errs[i].Error()) + if withType { + chain += fmt.Sprintf(" (%T)", errs[i]) + } } return chain } diff --git a/backend/vendor/github.com/stretchr/testify/assert/doc.go b/backend/vendor/github.com/stretchr/testify/assert/doc.go index 4953981d..a0b953aa 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/doc.go +++ b/backend/vendor/github.com/stretchr/testify/assert/doc.go @@ -1,5 +1,9 @@ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // +// # Note +// +// All functions in this package return a bool value indicating whether the assertion has passed. +// // # Example Usage // // The following is a complete example using assert in a standard test function: diff --git a/backend/vendor/github.com/stretchr/testify/assert/http_assertions.go b/backend/vendor/github.com/stretchr/testify/assert/http_assertions.go index 861ed4b7..5a6bb75f 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/backend/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -138,7 +138,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, contains := strings.Contains(body, fmt.Sprint(str)) if !contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) + Fail(t, fmt.Sprintf("Expected response body for %q to contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) } return contains @@ -158,7 +158,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin contains := strings.Contains(body, fmt.Sprint(str)) if contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) + Fail(t, fmt.Sprintf("Expected response body for %q to NOT contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) } return !contains diff --git a/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go b/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go index baa0cc7d..5a74c4f4 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go +++ b/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go @@ -1,5 +1,4 @@ //go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default -// +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default // Package yaml is an implementation of YAML functions that calls a pluggable implementation. // diff --git a/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go b/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go index b83c6cf6..0bae80e3 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go +++ b/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go @@ -1,5 +1,4 @@ //go:build !testify_yaml_fail && !testify_yaml_custom -// +build !testify_yaml_fail,!testify_yaml_custom // Package yaml is just an indirection to handle YAML deserialization. // diff --git a/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go b/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go index e78f7dfe..8041803f 100644 --- a/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go +++ b/backend/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go @@ -1,5 +1,4 @@ //go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default -// +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default // Package yaml is an implementation of YAML functions that always fail. // diff --git a/backend/vendor/github.com/stretchr/testify/require/doc.go b/backend/vendor/github.com/stretchr/testify/require/doc.go index 96843472..c8e3f94a 100644 --- a/backend/vendor/github.com/stretchr/testify/require/doc.go +++ b/backend/vendor/github.com/stretchr/testify/require/doc.go @@ -23,6 +23,8 @@ // // The `require` package have same global functions as in the `assert` package, // but instead of returning a boolean result they call `t.FailNow()`. +// A consequence of this is that it must be called from the goroutine running +// the test function, not from other goroutines created during the test. // // Every assertion function also takes an optional string message as the final argument, // allowing custom error messages to be appended to the message the assertion method outputs. diff --git a/backend/vendor/github.com/stretchr/testify/require/require.go b/backend/vendor/github.com/stretchr/testify/require/require.go index d8921950..2d02f9bc 100644 --- a/backend/vendor/github.com/stretchr/testify/require/require.go +++ b/backend/vendor/github.com/stretchr/testify/require/require.go @@ -117,10 +117,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string t.FailNow() } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // require.Empty(t, obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -131,10 +140,19 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { t.FailNow() } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // require.Emptyf(t, obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -279,10 +297,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if require.Error(t, err) { -// require.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// require.Error(t, err) func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -373,10 +389,8 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if require.Errorf(t, err, "error message %s", "formatted") { -// require.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// require.Errorf(t, err, "error message %s", "formatted") func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,7 +1111,35 @@ func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interf t.FailNow() } +// IsNotType asserts that the specified objects are not of the same type. +// +// require.IsNotType(t, &NotMyStruct{}, &MyStruct{}) +func IsNotType(t TestingT, theType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNotType(t, theType, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// require.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.IsNotTypef(t, theType, object, msg, args...) { + return + } + t.FailNow() +} + // IsType asserts that the specified objects are of the same type. +// +// require.IsType(t, &MyStruct{}, &MyStruct{}) func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1109,6 +1151,8 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs } // IsTypef asserts that the specified objects are of the same type. +// +// require.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1469,8 +1513,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str t.FailNow() } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if require.NotEmpty(t, obj) { // require.Equal(t, "two", obj[1]) @@ -1485,8 +1528,7 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { t.FailNow() } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if require.NotEmptyf(t, obj, "error message %s", "formatted") { // require.Equal(t, "two", obj[1]) @@ -1745,12 +1787,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // require.NotSubset(t, [1, 3, 4], [1, 2]) // require.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// require.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) +// require.NotSubset(t, {"x": 1, "y": 2}, ["z"]) func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1761,12 +1806,15 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i t.FailNow() } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // require.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") // require.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// require.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// require.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1971,11 +2019,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg t.FailNow() } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // require.Subset(t, [1, 2, 3], [1, 2]) // require.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// require.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) +// require.Subset(t, {"x": 1, "y": 2}, ["x"]) func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1986,11 +2038,15 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte t.FailNow() } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // require.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") // require.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// require.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// require.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/backend/vendor/github.com/stretchr/testify/require/require_forward.go b/backend/vendor/github.com/stretchr/testify/require/require_forward.go index 1bd87304..e6f7e944 100644 --- a/backend/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/backend/vendor/github.com/stretchr/testify/require/require_forward.go @@ -93,10 +93,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st ElementsMatchf(a.t, listA, listB, msg, args...) } -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Empty asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Empty(obj) +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -104,10 +113,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { Empty(a.t, object, msgAndArgs...) } -// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. +// Emptyf asserts that the given value is "empty". +// +// [Zero values] are "empty". +// +// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). +// +// Slices, maps and channels with zero length are "empty". +// +// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // a.Emptyf(obj, "error message %s", "formatted") +// +// [Zero values]: https://go.dev/ref/spec#The_zero_value func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -225,10 +243,8 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// a.Error(err) func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -298,10 +314,8 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// a.Errorf(err, "error message %s", "formatted") func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -869,7 +883,29 @@ func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...in IsNonIncreasingf(a.t, object, msg, args...) } +// IsNotType asserts that the specified objects are not of the same type. +// +// a.IsNotType(&NotMyStruct{}, &MyStruct{}) +func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNotType(a.t, theType, object, msgAndArgs...) +} + +// IsNotTypef asserts that the specified objects are not of the same type. +// +// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") +func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + IsNotTypef(a.t, theType, object, msg, args...) +} + // IsType asserts that the specified objects are of the same type. +// +// a.IsType(&MyStruct{}, &MyStruct{}) func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -878,6 +914,8 @@ func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAnd } // IsTypef asserts that the specified objects are of the same type. +// +// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1163,8 +1201,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg NotElementsMatchf(a.t, listA, listB, msg, args...) } -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmpty asserts that the specified object is NOT [Empty]. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) @@ -1176,8 +1213,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { NotEmpty(a.t, object, msgAndArgs...) } -// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. +// NotEmptyf asserts that the specified object is NOT [Empty]. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) @@ -1379,12 +1415,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubset asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubset([1, 3, 4], [1, 2]) // a.NotSubset({"x": 1, "y": 2}, {"z": 3}) +// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) +// a.NotSubset({"x": 1, "y": 2}, ["z"]) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1392,12 +1431,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) or map does NOT -// contain all elements given in the specified subset list(array, slice...) or -// map. +// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all +// elements given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") // a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1557,11 +1599,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subset asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subset([1, 2, 3], [1, 2]) // a.Subset({"x": 1, "y": 2}, {"x": 1}) +// a.Subset([1, 2, 3], {1: "one", 2: "two"}) +// a.Subset({"x": 1, "y": 2}, ["x"]) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1569,11 +1615,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) or map contains all -// elements given in the specified subset list(array, slice...) or map. +// Subsetf asserts that the list (array, slice, or map) contains all elements +// given in the subset (array, slice, or map). +// Map elements are key-value pairs unless compared with an array or slice where +// only the map key is evaluated. // // a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") // a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/backend/vendor/go.etcd.io/bbolt/.go-version b/backend/vendor/go.etcd.io/bbolt/.go-version index d8c40e53..7bdcec52 100644 --- a/backend/vendor/go.etcd.io/bbolt/.go-version +++ b/backend/vendor/go.etcd.io/bbolt/.go-version @@ -1 +1 @@ -1.23.6 +1.23.12 diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_386.go b/backend/vendor/go.etcd.io/bbolt/bolt_386.go deleted file mode 100644 index aee25960..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_386.go +++ /dev/null @@ -1,7 +0,0 @@ -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0x7FFFFFFF // 2GB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0xFFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_aix.go b/backend/vendor/go.etcd.io/bbolt/bolt_aix.go index 4b424ed4..596e5406 100644 --- a/backend/vendor/go.etcd.io/bbolt/bolt_aix.go +++ b/backend/vendor/go.etcd.io/bbolt/bolt_aix.go @@ -9,6 +9,8 @@ import ( "unsafe" "golang.org/x/sys/unix" + + "go.etcd.io/bbolt/internal/common" ) // flock acquires an advisory lock on a file descriptor. @@ -69,7 +71,7 @@ func mmap(db *DB, sz int) error { // Save the original byte slice and convert to a byte array pointer. db.dataref = b - db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0])) + db.data = (*[common.MaxMapSize]byte)(unsafe.Pointer(&b[0])) db.datasz = sz return nil } diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_amd64.go b/backend/vendor/go.etcd.io/bbolt/bolt_amd64.go deleted file mode 100644 index 5dd8f3f2..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_amd64.go +++ /dev/null @@ -1,7 +0,0 @@ -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0xFFFFFFFFFFFF // 256TB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_android.go b/backend/vendor/go.etcd.io/bbolt/bolt_android.go index 11890f0d..ac64fcf5 100644 --- a/backend/vendor/go.etcd.io/bbolt/bolt_android.go +++ b/backend/vendor/go.etcd.io/bbolt/bolt_android.go @@ -7,6 +7,8 @@ import ( "unsafe" "golang.org/x/sys/unix" + + "go.etcd.io/bbolt/internal/common" ) // flock acquires an advisory lock on a file descriptor. @@ -69,7 +71,7 @@ func mmap(db *DB, sz int) error { // Save the original byte slice and convert to a byte array pointer. db.dataref = b - db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0])) + db.data = (*[common.MaxMapSize]byte)(unsafe.Pointer(&b[0])) db.datasz = sz return nil } diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_arm.go b/backend/vendor/go.etcd.io/bbolt/bolt_arm.go deleted file mode 100644 index aee25960..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_arm.go +++ /dev/null @@ -1,7 +0,0 @@ -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0x7FFFFFFF // 2GB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0xFFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_arm64.go b/backend/vendor/go.etcd.io/bbolt/bolt_arm64.go deleted file mode 100644 index 2c67ab10..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_arm64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build arm64 - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0xFFFFFFFFFFFF // 256TB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_loong64.go b/backend/vendor/go.etcd.io/bbolt/bolt_loong64.go deleted file mode 100644 index 1ef2145c..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_loong64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build loong64 - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0xFFFFFFFFFFFF // 256TB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_mips64x.go b/backend/vendor/go.etcd.io/bbolt/bolt_mips64x.go deleted file mode 100644 index f28a0512..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_mips64x.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build mips64 || mips64le - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0x8000000000 // 512GB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_mipsx.go b/backend/vendor/go.etcd.io/bbolt/bolt_mipsx.go deleted file mode 100644 index 708fccdc..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_mipsx.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build mips || mipsle - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0x40000000 // 1GB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0xFFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_ppc.go b/backend/vendor/go.etcd.io/bbolt/bolt_ppc.go deleted file mode 100644 index 6a21cf33..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_ppc.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build ppc - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0x7FFFFFFF // 2GB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0xFFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_ppc64.go b/backend/vendor/go.etcd.io/bbolt/bolt_ppc64.go deleted file mode 100644 index a32f2462..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_ppc64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build ppc64 - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0xFFFFFFFFFFFF // 256TB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_ppc64le.go b/backend/vendor/go.etcd.io/bbolt/bolt_ppc64le.go deleted file mode 100644 index 8fb60ddd..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_ppc64le.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build ppc64le - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0xFFFFFFFFFFFF // 256TB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_riscv64.go b/backend/vendor/go.etcd.io/bbolt/bolt_riscv64.go deleted file mode 100644 index a63d26ab..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_riscv64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build riscv64 - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0xFFFFFFFFFFFF // 256TB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_s390x.go b/backend/vendor/go.etcd.io/bbolt/bolt_s390x.go deleted file mode 100644 index 749ea97e..00000000 --- a/backend/vendor/go.etcd.io/bbolt/bolt_s390x.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build s390x - -package bbolt - -// maxMapSize represents the largest mmap size supported by Bolt. -const maxMapSize = 0xFFFFFFFFFFFF // 256TB - -// maxAllocSize is the size used when creating array pointers. -const maxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_solaris.go b/backend/vendor/go.etcd.io/bbolt/bolt_solaris.go index babad657..56b2ccab 100644 --- a/backend/vendor/go.etcd.io/bbolt/bolt_solaris.go +++ b/backend/vendor/go.etcd.io/bbolt/bolt_solaris.go @@ -7,6 +7,8 @@ import ( "unsafe" "golang.org/x/sys/unix" + + "go.etcd.io/bbolt/internal/common" ) // flock acquires an advisory lock on a file descriptor. @@ -67,7 +69,7 @@ func mmap(db *DB, sz int) error { // Save the original byte slice and convert to a byte array pointer. db.dataref = b - db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0])) + db.data = (*[common.MaxMapSize]byte)(unsafe.Pointer(&b[0])) db.datasz = sz return nil } diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_unix.go b/backend/vendor/go.etcd.io/bbolt/bolt_unix.go index d1922c2d..f68e721f 100644 --- a/backend/vendor/go.etcd.io/bbolt/bolt_unix.go +++ b/backend/vendor/go.etcd.io/bbolt/bolt_unix.go @@ -11,6 +11,7 @@ import ( "golang.org/x/sys/unix" "go.etcd.io/bbolt/errors" + "go.etcd.io/bbolt/internal/common" ) // flock acquires an advisory lock on a file descriptor. @@ -67,7 +68,7 @@ func mmap(db *DB, sz int) error { // Save the original byte slice and convert to a byte array pointer. db.dataref = b - db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0])) + db.data = (*[common.MaxMapSize]byte)(unsafe.Pointer(&b[0])) db.datasz = sz return nil } diff --git a/backend/vendor/go.etcd.io/bbolt/bolt_windows.go b/backend/vendor/go.etcd.io/bbolt/bolt_windows.go index ec21ecb8..e99a0d62 100644 --- a/backend/vendor/go.etcd.io/bbolt/bolt_windows.go +++ b/backend/vendor/go.etcd.io/bbolt/bolt_windows.go @@ -10,6 +10,7 @@ import ( "golang.org/x/sys/windows" "go.etcd.io/bbolt/errors" + "go.etcd.io/bbolt/internal/common" ) // fdatasync flushes written data to a file descriptor. @@ -95,7 +96,7 @@ func mmap(db *DB, sz int) error { } // Convert to a byte array. - db.data = (*[maxMapSize]byte)(unsafe.Pointer(addr)) + db.data = (*[common.MaxMapSize]byte)(unsafe.Pointer(addr)) db.datasz = sz return nil diff --git a/backend/vendor/go.etcd.io/bbolt/db.go b/backend/vendor/go.etcd.io/bbolt/db.go index 5c1947e9..622947d9 100644 --- a/backend/vendor/go.etcd.io/bbolt/db.go +++ b/backend/vendor/go.etcd.io/bbolt/db.go @@ -125,7 +125,7 @@ type DB struct { // always fails on Windows platform. //nolint dataref []byte // mmap'ed readonly, write throws SEGV - data *[maxMapSize]byte + data *[common.MaxMapSize]byte datasz int meta0 *common.Meta meta1 *common.Meta @@ -563,7 +563,7 @@ func (db *DB) mmapSize(size int) (int, error) { } // Verify the requested size is not above the maximum allowed. - if size > maxMapSize { + if size > common.MaxMapSize { return 0, errors.New("mmap too large") } @@ -581,8 +581,8 @@ func (db *DB) mmapSize(size int) (int, error) { } // If we've exceeded the max size then only grow up to the max size. - if sz > maxMapSize { - sz = maxMapSize + if sz > common.MaxMapSize { + sz = common.MaxMapSize } return int(sz), nil @@ -1080,7 +1080,7 @@ func safelyCall(fn func(*Tx) error, tx *Tx) (err error) { // then it allows you to force the database file to sync against the disk. func (db *DB) Sync() (err error) { if lg := db.Logger(); lg != discardLogger { - lg.Debug("Syncing bbolt db (%s)", db.path) + lg.Debugf("Syncing bbolt db (%s)", db.path) defer func() { if err != nil { lg.Errorf("[GOOS: %s, GOARCH: %s] syncing bbolt db (%s) failed: %v", runtime.GOOS, runtime.GOARCH, db.path, err) @@ -1309,6 +1309,12 @@ type Options struct { // If <=0, the initial map size is 0. // If initialMmapSize is smaller than the previous database size, // it takes no effect. + // + // Note: On Windows, due to platform limitations, the database file size + // will be immediately resized to match `InitialMmapSize` (aligned to page size) + // when the DB is opened. On non-Windows platforms, the file size will grow + // dynamically based on the actual amount of written data, regardless of `InitialMmapSize`. + // Refer to https://github.com/etcd-io/bbolt/issues/378#issuecomment-1378121966. InitialMmapSize int // PageSize overrides the default OS page size. diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_386.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_386.go new file mode 100644 index 00000000..773175de --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_386.go @@ -0,0 +1,7 @@ +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0x7FFFFFFF // 2GB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0xFFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_amd64.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_amd64.go new file mode 100644 index 00000000..9f27d919 --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_amd64.go @@ -0,0 +1,7 @@ +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0xFFFFFFFFFFFF // 256TB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_arm.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_arm.go new file mode 100644 index 00000000..773175de --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_arm.go @@ -0,0 +1,7 @@ +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0x7FFFFFFF // 2GB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0xFFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_arm64.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_arm64.go new file mode 100644 index 00000000..9022f6bc --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_arm64.go @@ -0,0 +1,9 @@ +//go:build arm64 + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0xFFFFFFFFFFFF // 256TB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_loong64.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_loong64.go new file mode 100644 index 00000000..31277523 --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_loong64.go @@ -0,0 +1,9 @@ +//go:build loong64 + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0xFFFFFFFFFFFF // 256TB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_mips64x.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_mips64x.go new file mode 100644 index 00000000..d930f4ed --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_mips64x.go @@ -0,0 +1,9 @@ +//go:build mips64 || mips64le + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0x8000000000 // 512GB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_mipsx.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_mipsx.go new file mode 100644 index 00000000..8b193436 --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_mipsx.go @@ -0,0 +1,9 @@ +//go:build mips || mipsle + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0x40000000 // 1GB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0xFFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc.go new file mode 100644 index 00000000..a374e140 --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc.go @@ -0,0 +1,9 @@ +//go:build ppc + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0x7FFFFFFF // 2GB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0xFFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc64.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc64.go new file mode 100644 index 00000000..80288a83 --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc64.go @@ -0,0 +1,9 @@ +//go:build ppc64 + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0xFFFFFFFFFFFF // 256TB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc64le.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc64le.go new file mode 100644 index 00000000..77561d68 --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_ppc64le.go @@ -0,0 +1,9 @@ +//go:build ppc64le + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0xFFFFFFFFFFFF // 256TB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_riscv64.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_riscv64.go new file mode 100644 index 00000000..2a876e5f --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_riscv64.go @@ -0,0 +1,9 @@ +//go:build riscv64 + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0xFFFFFFFFFFFF // 256TB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_s390x.go b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_s390x.go new file mode 100644 index 00000000..982cb755 --- /dev/null +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/bolt_s390x.go @@ -0,0 +1,9 @@ +//go:build s390x + +package common + +// MaxMapSize represents the largest mmap size supported by Bolt. +const MaxMapSize = 0xFFFFFFFFFFFF // 256TB + +// MaxAllocSize is the size used when creating array pointers. +const MaxAllocSize = 0x7FFFFFFF diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/types.go b/backend/vendor/go.etcd.io/bbolt/internal/common/types.go index 8ad8279a..18d6d69c 100644 --- a/backend/vendor/go.etcd.io/bbolt/internal/common/types.go +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/types.go @@ -17,9 +17,6 @@ const Magic uint32 = 0xED0CDAED const PgidNoFreelist Pgid = 0xffffffffffffffff -// DO NOT EDIT. Copied from the "bolt" package. -const pageMaxAllocSize = 0xFFFFFFF - // IgnoreNoSync specifies whether the NoSync field of a DB is ignored when // syncing changes to a file. This is required as some operating systems, // such as OpenBSD, do not have a unified buffer cache (UBC) and writes diff --git a/backend/vendor/go.etcd.io/bbolt/internal/common/unsafe.go b/backend/vendor/go.etcd.io/bbolt/internal/common/unsafe.go index 9b77dd7b..740ffc70 100644 --- a/backend/vendor/go.etcd.io/bbolt/internal/common/unsafe.go +++ b/backend/vendor/go.etcd.io/bbolt/internal/common/unsafe.go @@ -23,5 +23,5 @@ func UnsafeByteSlice(base unsafe.Pointer, offset uintptr, i, j int) []byte { // index 0. However, the wiki never says that the address must be to // the beginning of a C allocation (or even that malloc was used at // all), so this is believed to be correct. - return (*[pageMaxAllocSize]byte)(UnsafeAdd(base, offset))[i:j:j] + return (*[MaxAllocSize]byte)(UnsafeAdd(base, offset))[i:j:j] } diff --git a/backend/vendor/go.etcd.io/bbolt/tx.go b/backend/vendor/go.etcd.io/bbolt/tx.go index 7b5db772..1669fb16 100644 --- a/backend/vendor/go.etcd.io/bbolt/tx.go +++ b/backend/vendor/go.etcd.io/bbolt/tx.go @@ -387,16 +387,43 @@ func (tx *Tx) Copy(w io.Writer) error { // WriteTo writes the entire database to a writer. // If err == nil then exactly tx.Size() bytes will be written into the writer. func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) { - // Attempt to open reader with WriteFlag - f, err := tx.db.openFile(tx.db.path, os.O_RDONLY|tx.WriteFlag, 0) - if err != nil { - return 0, err - } - defer func() { - if cerr := f.Close(); err == nil { - err = cerr + var f *os.File + // There is a risk that between the time a read-only transaction + // is created and the time the file is actually opened, the + // underlying db file at tx.db.path may have been replaced + // (e.g. via rename). In that case, opening the file again would + // unexpectedly point to a different file, rather than the one + // the transaction was based on. + // + // To overcome this, we reuse the already opened file handle when + // WritFlag not set. When the WriteFlag is set, we reopen the file + // but verify that it still refers to the same underlying file + // (by device and inode). If it does not, we fall back to + // reusing the existing already opened file handle. + if tx.WriteFlag != 0 { + // Attempt to open reader with WriteFlag + f, err = tx.db.openFile(tx.db.path, os.O_RDONLY|tx.WriteFlag, 0) + if err != nil { + return 0, err } - }() + + if ok, err := sameFile(tx.db.file, f); !ok { + lg := tx.db.Logger() + if cerr := f.Close(); cerr != nil { + lg.Errorf("failed to close the file (%s): %v", tx.db.path, cerr) + } + lg.Warningf("The underlying file has changed, so reuse the already opened file (%s): %v", tx.db.path, err) + f = tx.db.file + } else { + defer func() { + if cerr := f.Close(); err == nil { + err = cerr + } + }() + } + } else { + f = tx.db.file + } // Generate a meta page. We use the same page data for both meta pages. buf := make([]byte, tx.db.pageSize) @@ -423,13 +450,13 @@ func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) { return n, fmt.Errorf("meta 1 copy: %s", err) } - // Move past the meta pages in the file. - if _, err := f.Seek(int64(tx.db.pageSize*2), io.SeekStart); err != nil { - return n, fmt.Errorf("seek: %s", err) - } + // Copy data pages using a SectionReader to avoid affecting f's offset. + dataOffset := int64(tx.db.pageSize * 2) + dataSize := tx.Size() - dataOffset + sr := io.NewSectionReader(f, dataOffset, dataSize) // Copy data pages. - wn, err := io.CopyN(w, f, tx.Size()-int64(tx.db.pageSize*2)) + wn, err := io.CopyN(w, sr, dataSize) n += wn if err != nil { return n, err @@ -438,6 +465,19 @@ func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) { return n, nil } +func sameFile(f1, f2 *os.File) (bool, error) { + fi1, err := f1.Stat() + if err != nil { + return false, fmt.Errorf("failed to get fileInfo of the first file (%s): %w", f1.Name(), err) + } + fi2, err := f2.Stat() + if err != nil { + return false, fmt.Errorf("failed to get fileInfo of the second file (%s): %w", f2.Name(), err) + } + + return os.SameFile(fi1, fi2), nil +} + // CopyFile copies the entire database to file at the given path. // A reader transaction is maintained during the copy so it is safe to continue // using the database while a copy is in progress. @@ -495,8 +535,8 @@ func (tx *Tx) write() error { // Write out page in "max allocation" sized chunks. for { sz := rem - if sz > maxAllocSize-1 { - sz = maxAllocSize - 1 + if sz > common.MaxAllocSize-1 { + sz = common.MaxAllocSize - 1 } buf := common.UnsafeByteSlice(unsafe.Pointer(p), written, 0, int(sz)) @@ -561,10 +601,13 @@ func (tx *Tx) writeMeta() error { tx.meta.Write(p) // Write the meta page to file. + tx.db.metalock.Lock() if _, err := tx.db.ops.writeAt(buf, int64(p.Id())*int64(tx.db.pageSize)); err != nil { + tx.db.metalock.Unlock() lg.Errorf("writeAt failed, pgid: %d, pageSize: %d, error: %v", p.Id(), tx.db.pageSize, err) return err } + tx.db.metalock.Unlock() if !tx.db.NoSync || common.IgnoreNoSync { // gofail: var beforeSyncMetaPage struct{} if err := fdatasync(tx.db); err != nil { diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt index 84d4cbb1..2853c6a5 100644 --- a/backend/vendor/modules.txt +++ b/backend/vendor/modules.txt @@ -4,14 +4,11 @@ cloud.google.com/go/compute/metadata # github.com/Depado/bfchroma/v2 v2.0.0 ## explicit; go 1.18 github.com/Depado/bfchroma/v2 -# github.com/PuerkitoBio/goquery v1.10.2 -## explicit; go 1.23 +# github.com/PuerkitoBio/goquery v1.11.0 +## explicit; go 1.24.0 github.com/PuerkitoBio/goquery -# github.com/ajg/form v1.5.1 -## explicit -github.com/ajg/form -# github.com/alecthomas/chroma/v2 v2.15.0 -## explicit; go 1.19 +# github.com/alecthomas/chroma/v2 v2.20.0 +## explicit; go 1.22 github.com/alecthomas/chroma/v2 github.com/alecthomas/chroma/v2/formatters/html github.com/alecthomas/chroma/v2/lexers @@ -43,30 +40,20 @@ github.com/didip/tollbooth/v7/errors github.com/didip/tollbooth/v7/internal/time/rate github.com/didip/tollbooth/v7/libstring github.com/didip/tollbooth/v7/limiter -# github.com/didip/tollbooth/v8 v8.0.1 -## explicit; go 1.19 -github.com/didip/tollbooth/v8 -github.com/didip/tollbooth/v8/errors -github.com/didip/tollbooth/v8/internal/time/rate -github.com/didip/tollbooth/v8/libstring -github.com/didip/tollbooth/v8/limiter # github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f ## explicit; go 1.14 github.com/didip/tollbooth_chi -# github.com/dlclark/regexp2 v1.11.4 +# github.com/dlclark/regexp2 v1.11.5 ## explicit; go 1.13 github.com/dlclark/regexp2 github.com/dlclark/regexp2/syntax -# github.com/go-chi/chi/v5 v5.2.1 +# github.com/go-chi/chi/v5 v5.2.3 ## explicit; go 1.20 github.com/go-chi/chi/v5 github.com/go-chi/chi/v5/middleware -# github.com/go-chi/cors v1.2.1 +# github.com/go-chi/cors v1.2.2 ## explicit; go 1.14 github.com/go-chi/cors -# github.com/go-chi/render v1.0.3 -## explicit; go 1.16 -github.com/go-chi/render # github.com/go-oauth2/oauth2/v4 v4.5.3 ## explicit; go 1.13 github.com/go-oauth2/oauth2/v4 @@ -87,14 +74,14 @@ github.com/go-pkgz/email # github.com/go-pkgz/expirable-cache/v3 v3.0.0 ## explicit; go 1.20 github.com/go-pkgz/expirable-cache/v3 -# github.com/go-pkgz/jrpc v0.3.1 -## explicit; go 1.23 +# github.com/go-pkgz/jrpc v0.4.0 +## explicit; go 1.24.0 github.com/go-pkgz/jrpc # github.com/go-pkgz/lcw/v2 v2.0.0 ## explicit; go 1.21 github.com/go-pkgz/lcw/v2 github.com/go-pkgz/lcw/v2/eventbus -# github.com/go-pkgz/lgr v0.12.0 +# github.com/go-pkgz/lgr v0.12.1 ## explicit; go 1.21 github.com/go-pkgz/lgr # github.com/go-pkgz/notify v1.2.0 @@ -104,16 +91,19 @@ github.com/go-pkgz/notify ## explicit; go 1.20 github.com/go-pkgz/repeater github.com/go-pkgz/repeater/strategy -# github.com/go-pkgz/rest v1.20.3 -## explicit; go 1.21 +# github.com/go-pkgz/rest v1.20.4 +## explicit; go 1.23.0 github.com/go-pkgz/rest github.com/go-pkgz/rest/logger github.com/go-pkgz/rest/realip +# github.com/go-pkgz/routegroup v1.6.0 +## explicit; go 1.23 +github.com/go-pkgz/routegroup # github.com/go-pkgz/syncs v1.3.2 ## explicit; go 1.20 github.com/go-pkgz/syncs -# github.com/golang-jwt/jwt/v5 v5.2.2 -## explicit; go 1.18 +# github.com/golang-jwt/jwt/v5 v5.3.0 +## explicit; go 1.21 github.com/golang-jwt/jwt/v5 # github.com/golang/snappy v1.0.0 ## explicit @@ -199,7 +189,7 @@ github.com/slack-go/slack/internal/backoff github.com/slack-go/slack/internal/errorsx github.com/slack-go/slack/internal/timex github.com/slack-go/slack/slackutilsx -# github.com/stretchr/testify v1.10.0 +# github.com/stretchr/testify v1.11.1 ## explicit; go 1.17 github.com/stretchr/testify/assert github.com/stretchr/testify/assert/yaml @@ -216,7 +206,7 @@ github.com/xdg-go/stringprep # github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 ## explicit; go 1.17 github.com/youmark/pkcs8 -# go.etcd.io/bbolt v1.4.0 +# go.etcd.io/bbolt v1.4.3 ## explicit; go 1.23 go.etcd.io/bbolt go.etcd.io/bbolt/errors @@ -287,8 +277,8 @@ golang.org/x/crypto/blowfish golang.org/x/crypto/ocsp golang.org/x/crypto/pbkdf2 golang.org/x/crypto/scrypt -# golang.org/x/image v0.26.0 -## explicit; go 1.23.0 +# golang.org/x/image v0.33.0 +## explicit; go 1.24.0 golang.org/x/image/draw golang.org/x/image/math/f64 # golang.org/x/net v0.47.0