Update dependencies and build to go1.22 (#2113)

* Update dependencies and build to go1.22

* Fix api changes wrt to dependencies

* Update golangci config
This commit is contained in:
Wim 2024-05-23 23:44:31 +02:00 committed by GitHub
parent 56e7bd01ca
commit 2f33fe86f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1556 changed files with 3279522 additions and 1924375 deletions

View File

@ -16,7 +16,7 @@ jobs:
test-build-upload: test-build-upload:
strategy: strategy:
matrix: matrix:
go-version: [1.20.x] go-version: [1.22.x]
platform: [ubuntu-latest] platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
@ -39,19 +39,19 @@ jobs:
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64 CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
- name: Upload linux 64-bit - name: Upload linux 64-bit
if: startsWith(matrix.go-version,'1.20') if: startsWith(matrix.go-version,'1.22')
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: matterbridge-linux-64bit name: matterbridge-linux-64bit
path: output/lin path: output/lin
- name: Upload windows 64-bit - name: Upload windows 64-bit
if: startsWith(matrix.go-version,'1.20') if: startsWith(matrix.go-version,'1.22')
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: matterbridge-windows-64bit name: matterbridge-windows-64bit
path: output/win path: output/win
- name: Upload darwin 64-bit - name: Upload darwin 64-bit
if: startsWith(matrix.go-version,'1.20') if: startsWith(matrix.go-version,'1.22')
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: matterbridge-darwin-64bit name: matterbridge-darwin-64bit

View File

@ -212,6 +212,8 @@ linters:
- execinquery - execinquery
- nosnakecase - nosnakecase
- exhaustive - exhaustive
- testifylint
- mnd
# rules to deal with reported isues # rules to deal with reported isues
issues: issues:
# List of regexps of issue texts to exclude, empty list by default. # List of regexps of issue texts to exclude, empty list by default.

View File

@ -90,7 +90,7 @@ func (b *Bdiscord) Connect() error {
if err != nil { if err != nil {
return err return err
} }
guilds, err := b.c.UserGuilds(100, "", "") guilds, err := b.c.UserGuilds(100, "", "", false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -101,7 +101,9 @@ func (b *Bslack) populateMessageWithBotInfo(ev *slack.MessageEvent, rmsg *config
var err error var err error
var bot *slack.Bot var bot *slack.Bot
for { for {
bot, err = b.rtm.GetBotInfo(ev.BotID) bot, err = b.rtm.GetBotInfo(slack.GetBotInfoParameters{
Bot: ev.BotID,
})
if err == nil { if err == nil {
break break
} }

View File

@ -136,7 +136,7 @@ func isGroupJid(identifier string) bool {
func (b *Bwhatsapp) getDevice() (*store.Device, error) { func (b *Bwhatsapp) getDevice() (*store.Device, error) {
device := &store.Device{} device := &store.Device{}
storeContainer, err := sqlstore.New("sqlite", "file:"+b.Config.GetString("sessionfile")+".db?_foreign_keys=on&_pragma=busy_timeout=10000", nil) storeContainer, err := sqlstore.New("sqlite", "file:"+b.Config.GetString("sessionfile")+".db?_pragma=foreign_keys(1)&_pragma=busy_timeout=10000", nil)
if err != nil { if err != nil {
return device, fmt.Errorf("failed to connect to database: %v", err) return device, fmt.Errorf("failed to connect to database: %v", err)
} }
@ -151,7 +151,6 @@ func (b *Bwhatsapp) getDevice() (*store.Device, error) {
func (b *Bwhatsapp) getNewReplyContext(parentID string) (*proto.ContextInfo, error) { func (b *Bwhatsapp) getNewReplyContext(parentID string) (*proto.ContextInfo, error) {
replyInfo, err := b.parseMessageID(parentID) replyInfo, err := b.parseMessageID(parentID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

106
go.mod
View File

@ -5,21 +5,21 @@ require (
github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560 github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c
github.com/SevereCloud/vksdk/v2 v2.16.0 github.com/SevereCloud/vksdk/v2 v2.16.1
github.com/bwmarrin/discordgo v0.27.1 github.com/bwmarrin/discordgo v0.28.1
github.com/d5/tengo/v2 v2.16.1 github.com/d5/tengo/v2 v2.17.0
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/fsnotify/fsnotify v1.6.0 github.com/fsnotify/fsnotify v1.7.0
github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2
github.com/google/gops v0.3.27 github.com/google/gops v0.3.27
github.com/gorilla/schema v1.2.0 github.com/gorilla/schema v1.3.0
github.com/harmony-development/shibshib v0.0.0-20220101224523-c98059d09cfa github.com/harmony-development/shibshib v0.0.0-20220101224523-c98059d09cfa
github.com/hashicorp/golang-lru v0.6.0 github.com/hashicorp/golang-lru v0.6.0
github.com/jpillora/backoff v1.0.0 github.com/jpillora/backoff v1.0.0
github.com/keybase/go-keybase-chat-bot v0.0.0-20221220212439-e48d9abd2c20 github.com/keybase/go-keybase-chat-bot v0.0.0-20221220212439-e48d9abd2c20
github.com/kyokomi/emoji/v2 v2.2.12 github.com/kyokomi/emoji/v2 v2.2.13
github.com/labstack/echo/v4 v4.11.1 github.com/labstack/echo/v4 v4.12.0
github.com/lrstanley/girc v0.0.0-20230729130341-dd5853a5f1a6 github.com/lrstanley/girc v0.0.0-20240519163535-a518c5b87a79
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696 github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696
github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be
github.com/matterbridge/gomatrix v0.0.0-20220411225302-271e5088ea27 github.com/matterbridge/gomatrix v0.0.0-20220411225302-271e5088ea27
@ -39,21 +39,21 @@ require (
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
github.com/shazow/ssh-chat v1.10.1 github.com/shazow/ssh-chat v1.10.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/slack-go/slack v0.12.2 github.com/slack-go/slack v0.13.0
github.com/spf13/viper v1.16.0 github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.9.0
github.com/vincent-petithory/dataurl v1.0.0 github.com/vincent-petithory/dataurl v1.0.0
github.com/writeas/go-strip-markdown v2.0.1+incompatible github.com/writeas/go-strip-markdown v2.0.1+incompatible
github.com/yaegashi/msgraph.go v0.1.4 github.com/yaegashi/msgraph.go v0.1.4
github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289 github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289
go.mau.fi/whatsmeow v0.0.0-20230805111647-405414b9b5c0 go.mau.fi/whatsmeow v0.0.0-20240520180327-81f8f07f1dfb
golang.org/x/image v0.11.0 golang.org/x/image v0.16.0
golang.org/x/oauth2 v0.11.0 golang.org/x/oauth2 v0.20.0
golang.org/x/text v0.12.0 golang.org/x/text v0.15.0
gomod.garykim.dev/nc-talk v0.3.0 gomod.garykim.dev/nc-talk v0.3.0
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.33.0
layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14 layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14
modernc.org/sqlite v1.25.0 modernc.org/sqlite v1.29.10
) )
require ( require (
@ -68,24 +68,24 @@ require (
github.com/francoispqt/gojay v1.2.13 // indirect github.com/francoispqt/gojay v1.2.13 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gopackage/ddp v0.0.3 // indirect github.com/gopackage/ddp v0.0.3 // indirect
github.com/gorilla/websocket v1.5.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
github.com/klauspost/compress v1.16.0 // indirect github.com/klauspost/compress v1.17.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.3 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/labstack/gommon v0.4.0 // indirect github.com/labstack/gommon v0.4.2 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
github.com/mattermost/logr/v2 v2.0.15 // indirect github.com/mattermost/logr/v2 v2.0.15 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect
@ -95,57 +95,61 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monaco-io/request v1.0.5 // indirect github.com/monaco-io/request v1.0.5 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pborman/uuid v1.2.1 // indirect github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/philhofer/fwd v1.1.1 // indirect github.com/philhofer/fwd v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rickb777/date v1.12.4 // indirect github.com/rickb777/date v1.12.4 // indirect
github.com/rickb777/plural v1.2.0 // indirect github.com/rickb777/plural v1.2.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/rs/zerolog v1.32.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 // indirect github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 // indirect
github.com/spf13/afero v1.9.5 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.5.1 // indirect github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tinylib/msgp v1.1.6 // indirect github.com/tinylib/msgp v1.1.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/msgpack/v5 v5.4.0 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/wiggin77/merror v1.0.3 // indirect github.com/wiggin77/merror v1.0.3 // indirect
github.com/wiggin77/srslog v1.0.1 // indirect github.com/wiggin77/srslog v1.0.1 // indirect
go.mau.fi/libsignal v0.1.0 // indirect go.mau.fi/libsignal v0.1.0 // indirect
golang.org/x/crypto v0.12.0 // indirect go.mau.fi/util v0.4.1 // indirect
golang.org/x/mod v0.8.0 // indirect go.uber.org/atomic v1.9.0 // indirect
golang.org/x/net v0.14.0 // indirect go.uber.org/multierr v1.9.0 // indirect
golang.org/x/sys v0.11.0 // indirect golang.org/x/crypto v0.23.0 // indirect
golang.org/x/term v0.11.0 // indirect golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/net v0.25.0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/sys v0.20.0 // indirect
google.golang.org/appengine v1.6.7 // indirect golang.org/x/term v0.20.0 // indirect
golang.org/x/time v0.5.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/cc/v3 v3.40.0 // indirect modernc.org/libc v1.49.3 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect modernc.org/mathutil v1.6.0 // indirect
modernc.org/libc v1.24.1 // indirect modernc.org/memory v1.8.0 // indirect
modernc.org/mathutil v1.5.0 // indirect modernc.org/strutil v1.2.0 // indirect
modernc.org/memory v1.6.0 // indirect modernc.org/token v1.1.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
rsc.io/qr v0.2.0 // indirect rsc.io/qr v0.2.0 // indirect
) )
//replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419 //replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
go 1.19 go 1.21
toolchain go1.22.3

250
go.sum
View File

@ -6,7 +6,6 @@ cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgo
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
@ -19,7 +18,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
@ -46,7 +44,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
code.sajari.com/docconv v1.2.0/go.mod h1:r8yfCP6OKbZ9Xkd87aBa4nfpk6ud/PoyLwex3n6cXSc= code.sajari.com/docconv v1.2.0/go.mod h1:r8yfCP6OKbZ9Xkd87aBa4nfpk6ud/PoyLwex3n6cXSc=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@ -127,8 +124,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c h1:4mIZQXKYBymQ9coA82nNyG/CjicMNLBZ8cPVrhNUM3g= github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c h1:4mIZQXKYBymQ9coA82nNyG/CjicMNLBZ8cPVrhNUM3g=
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c/go.mod h1:DNSFRLFDFIqm2+0aJzSOVfn25020vldM4SRqz6YtLgI= github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c/go.mod h1:DNSFRLFDFIqm2+0aJzSOVfn25020vldM4SRqz6YtLgI=
github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA= github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
github.com/SevereCloud/vksdk/v2 v2.16.0 h1:DQ90qqwY/yF1X/SWZQs1kQ/Ik+tphK82d+S6Rch46wQ= github.com/SevereCloud/vksdk/v2 v2.16.1 h1:UiazL3vTy7lMm33oIXRMxXg8S5I8bQuqEdLtbmOSpG4=
github.com/SevereCloud/vksdk/v2 v2.16.0/go.mod h1:VN6BH9nFUXcP7Uf0uX74Aht2DQ7+139aG3/Og+jia4w= github.com/SevereCloud/vksdk/v2 v2.16.1/go.mod h1:UfVcBt8qh5+gIflQO6L+CWwrXcpwhOl5hKvKf8sXUd8=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/advancedlogic/GoOse v0.0.0-20191112112754-e742535969c1/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w= github.com/advancedlogic/GoOse v0.0.0-20191112112754-e742535969c1/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w=
@ -246,8 +243,8 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@ -378,6 +375,7 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k= github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
@ -395,12 +393,13 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
github.com/d5/tengo/v2 v2.16.1 h1:/N6dqiGu9toqANInZEOQMM8I06icdZnmb+81DG/lZdw= github.com/d5/tengo/v2 v2.17.0 h1:BWUN9NoJzw48jZKiYDXDIF3QrIVZRm1uV1gTzeZ2lqM=
github.com/d5/tengo/v2 v2.16.1/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= github.com/d5/tengo/v2 v2.17.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchote/go-openal v0.0.0-20171116030048-f4a9a141d372/go.mod h1:74z+CYu2/mx4N+mcIS/rsvfAxBPBV9uv8zRAnwyFkdI= github.com/dchote/go-openal v0.0.0-20171116030048-f4a9a141d372/go.mod h1:74z+CYu2/mx4N+mcIS/rsvfAxBPBV9uv8zRAnwyFkdI=
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
@ -467,12 +466,13 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw=
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
@ -617,16 +617,16 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 h1:uK3X/2mt4tbSGoHvbLBHUny7CKiuwUip3MArtukol4E= github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 h1:yEt5djSYb4iNtmV9iJGVday+i4e9u6Mrn5iP64HH5QM=
github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -647,6 +647,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v35 v35.2.0/go.mod h1:s0515YVTI+IMrDoy9Y4pHt9ShGpzHvHO8rZ7L7acgvs= github.com/google/go-github/v35 v35.2.0/go.mod h1:s0515YVTI+IMrDoy9Y4pHt9ShGpzHvHO8rZ7L7acgvs=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@ -667,27 +668,27 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopackage/ddp v0.0.3 h1:fd0DxScoiS+ogq22ktey6DjDSDybtJPAn69geMpUtFc= github.com/gopackage/ddp v0.0.3 h1:fd0DxScoiS+ogq22ktey6DjDSDybtJPAn69geMpUtFc=
github.com/gopackage/ddp v0.0.3/go.mod h1:3hUXYG6C/6JsoxKsQaK7st09+GP9RZBFPzyAlU/0SLg= github.com/gopackage/ddp v0.0.3/go.mod h1:3hUXYG6C/6JsoxKsQaK7st09+GP9RZBFPzyAlU/0SLg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@ -700,8 +701,9 @@ github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/schema v1.3.0 h1:rbciOzXAx3IB8stEFnfTwO3sYa6EWlQk79XdyustPDA=
github.com/gorilla/schema v1.3.0/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@ -756,6 +758,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
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/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
@ -873,7 +877,6 @@ github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYb
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f h1:dnCYnTSltLuPMfc7dMrkz2uBUcEf/OFBR8yRh3oRT98= github.com/kettek/apng v0.0.0-20191108220231-414630eed80f h1:dnCYnTSltLuPMfc7dMrkz2uBUcEf/OFBR8yRh3oRT98=
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f/go.mod h1:x78/VRQYKuCftMWS0uK5e+F5RJ7S4gSlESRWI0Prl6Q= github.com/kettek/apng v0.0.0-20191108220231-414630eed80f/go.mod h1:x78/VRQYKuCftMWS0uK5e+F5RJ7S4gSlESRWI0Prl6Q=
@ -895,8 +898,8 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
@ -904,8 +907,8 @@ github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -917,6 +920,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
@ -925,14 +929,14 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4=
github.com/kyokomi/emoji/v2 v2.2.12 h1:sSVA5nH9ebR3Zji1o31wu3yOwD1zKXQA2z0zUyeit60= github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U=
github.com/kyokomi/emoji/v2 v2.2.12/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
@ -945,8 +949,8 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lrstanley/girc v0.0.0-20230729130341-dd5853a5f1a6 h1:InzqHdpTHFBhVtlu9NB7Ot2p6Y4A9IcTjWBihkKE1JM= github.com/lrstanley/girc v0.0.0-20240519163535-a518c5b87a79 h1:6yveo9g55i+VPr+Dinsp/Z7z/msCAq0E+Nag8lTl25k=
github.com/lrstanley/girc v0.0.0-20230729130341-dd5853a5f1a6/go.mod h1:lgrnhcF8bg/Bd5HA5DOb4Z+uGqUqGnp4skr+J2GwVgI= github.com/lrstanley/girc v0.0.0-20240519163535-a518c5b87a79/go.mod h1:lgrnhcF8bg/Bd5HA5DOb4Z+uGqUqGnp4skr+J2GwVgI=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@ -1010,8 +1014,9 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@ -1024,7 +1029,6 @@ github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vq
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/godown v0.0.1 h1:39uk50ufLVQFs0eapIJVX5fCS74a1Fs2g5f1MVqIHdE= github.com/mattn/godown v0.0.1 h1:39uk50ufLVQFs0eapIJVX5fCS74a1Fs2g5f1MVqIHdE=
github.com/mattn/godown v0.0.1/go.mod h1:/ivCKurgV/bx6yqtP/Jtc2Xmrv3beCYBvlfAUl4X5g4= github.com/mattn/godown v0.0.1/go.mod h1:/ivCKurgV/bx6yqtP/Jtc2Xmrv3beCYBvlfAUl4X5g4=
@ -1102,6 +1106,8 @@ github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5Vgl
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
@ -1199,8 +1205,8 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
@ -1220,9 +1226,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@ -1289,6 +1295,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
@ -1296,6 +1303,8 @@ github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rudderlabs/analytics-go v3.3.2+incompatible/go.mod h1:LF8/ty9kUX4PTY3l5c97K3nZZaX5Hwsvt+NBaRL/f30= github.com/rudderlabs/analytics-go v3.3.2+incompatible/go.mod h1:LF8/ty9kUX4PTY3l5c97K3nZZaX5Hwsvt+NBaRL/f30=
github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -1308,6 +1317,10 @@ github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
@ -1366,8 +1379,8 @@ github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 h1:A7o8tOE
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882/go.mod h1:5IwJoz9Pw7JsrCN4/skkxUtSWT7myuUPLhCgv6Q5vvQ= github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882/go.mod h1:5IwJoz9Pw7JsrCN4/skkxUtSWT7myuUPLhCgv6Q5vvQ=
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE= github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.13.0 h1:7my/pR2ubZJ9912p9FtvALYpbt0cQPAqkRy2jaSI1PQ=
github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/slack-go/slack v0.13.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
@ -1377,17 +1390,19 @@ github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4S
github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg= github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
@ -1396,7 +1411,6 @@ github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSW
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@ -1408,8 +1422,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/splitio/go-client/v6 v6.1.0/go.mod h1:CEGAEFT99Fwb32ZIRcnZoXTMXddtB6IIpTmt3RP8mnM= github.com/splitio/go-client/v6 v6.1.0/go.mod h1:CEGAEFT99Fwb32ZIRcnZoXTMXddtB6IIpTmt3RP8mnM=
github.com/splitio/go-split-commons/v3 v3.1.0/go.mod h1:29NCy20oAS4ZMy4qkwTd6277eieVDonx4V/aeDU/wUQ= github.com/splitio/go-split-commons/v3 v3.1.0/go.mod h1:29NCy20oAS4ZMy4qkwTd6277eieVDonx4V/aeDU/wUQ=
github.com/splitio/go-toolkit/v4 v4.2.0/go.mod h1:EdIHN0yzB1GTXDYQc0KdKvnjkO/jfUM2YqHVYfhD3Wo= github.com/splitio/go-toolkit/v4 v4.2.0/go.mod h1:EdIHN0yzB1GTXDYQc0KdKvnjkO/jfUM2YqHVYfhD3Wo=
@ -1431,12 +1445,12 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -1492,8 +1506,9 @@ github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:tw
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608=
github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg= github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
@ -1506,6 +1521,7 @@ github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw= github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE= github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
@ -1533,7 +1549,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.11/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= github.com/yuin/goldmark v1.4.11/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
@ -1551,8 +1566,10 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.mau.fi/libsignal v0.1.0 h1:vAKI/nJ5tMhdzke4cTK1fb0idJzz1JuEIpmjprueC+c= go.mau.fi/libsignal v0.1.0 h1:vAKI/nJ5tMhdzke4cTK1fb0idJzz1JuEIpmjprueC+c=
go.mau.fi/libsignal v0.1.0/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I= go.mau.fi/libsignal v0.1.0/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I=
go.mau.fi/whatsmeow v0.0.0-20230805111647-405414b9b5c0 h1:6kAOyrp8E9p99X1I3uj7BtEFspdcVjnYzUZpqcHo/mE= go.mau.fi/util v0.4.1 h1:3EC9KxIXo5+h869zDGf5OOZklRd/FjeVnimTwtm3owg=
go.mau.fi/whatsmeow v0.0.0-20230805111647-405414b9b5c0/go.mod h1:+ObGpFE6cbbY4hKc1FmQH9MVfqaemmlXGXSnwDvCOyE= go.mau.fi/util v0.4.1/go.mod h1:GjkTEBsehYZbSh2LlE6cWEn+6ZIZTGrTMM/5DMNlmFY=
go.mau.fi/whatsmeow v0.0.0-20240520180327-81f8f07f1dfb h1:FW5W0ewNOmunylB3Pmru8PvxYkhI6ZphCJtimM2aB6o=
go.mau.fi/whatsmeow v0.0.0-20240520180327-81f8f07f1dfb/go.mod h1:0+65CYaE6r4dWzr0dN8i+UZKy0gIfJ79VuSqIl0nKRM=
go.mongodb.org/mongo-driver v1.7.0/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8= go.mongodb.org/mongo-driver v1.7.0/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8=
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
@ -1569,10 +1586,13 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
@ -1613,9 +1633,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1631,6 +1650,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925/go.mod h1:1phAWC201xIgDyaFpmDeZkgf70Q4Pd/CNqfRtVPtxNw= golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925/go.mod h1:1phAWC201xIgDyaFpmDeZkgf70Q4Pd/CNqfRtVPtxNw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -1642,8 +1663,8 @@ golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+o
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -1670,9 +1691,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1749,10 +1769,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1772,8 +1790,8 @@ golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1787,9 +1805,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1883,7 +1898,6 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1892,7 +1906,6 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1908,26 +1921,21 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1937,17 +1945,16 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -2023,7 +2030,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@ -2032,9 +2038,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -2088,7 +2093,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@ -2134,10 +2138,8 @@ google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@ -2203,8 +2205,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
@ -2300,8 +2302,6 @@ layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa/go.mod h1:AOef7vHz0+v4sWwJnr0
layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14 h1:wY8eeq7DpM5iAugNbFrvuhdtmN8XM1iU+Ki5YZWjukg= layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14 h1:wY8eeq7DpM5iAugNbFrvuhdtmN8XM1iU+Ki5YZWjukg=
layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14/go.mod h1:tWPVA9ZAfImNwabjcd9uDE+Mtz0Hfs7a7G3vxrnrwyc= layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14/go.mod h1:tWPVA9ZAfImNwabjcd9uDE+Mtz0Hfs7a7G3vxrnrwyc=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
@ -2318,8 +2318,8 @@ modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo= modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
@ -2360,15 +2360,19 @@ modernc.org/ccgo/v3 v3.12.88/go.mod h1:0MFzUHIuSIthpVZyMWiFYMwjiFnhrN5MkvBrUwON+
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
modernc.org/ccgo/v3 v3.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8= modernc.org/ccgo/v3 v3.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=
modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw=
modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM=
modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
@ -2413,43 +2417,43 @@ modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.99/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= modernc.org/libc v1.11.99/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.104/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= modernc.org/libc v1.11.104/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY=
modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs= modernc.org/sqlite v1.10.6/go.mod h1:Z9FEjUtZP4qFEg6/SiADg9XCER7aYy9a/j7Pg9P7CPs=
modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y= modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y=
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA= modernc.org/sqlite v1.29.10 h1:3u93dz83myFnMilBGCOLbr+HjklS6+5rJLx4q86RDAg=
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= modernc.org/sqlite v1.29.10/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA=
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo= modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo=
modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg= modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo= modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo=
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -7,6 +7,6 @@ package vksdk
// Module constants. // Module constants.
const ( const (
Version = "2.16.0" Version = "2.16.1"
API = "5.131" API = "5.131"
) )

View File

@ -36,5 +36,5 @@ type BoardTopicPoll struct {
OwnerID int `json:"owner_id"` // Poll owner's ID OwnerID int `json:"owner_id"` // Poll owner's ID
PollID int `json:"poll_id"` // Poll ID PollID int `json:"poll_id"` // Poll ID
Question string `json:"question"` // Poll question Question string `json:"question"` // Poll question
Votes string `json:"votes"` // Votes number Votes int `json:"votes"` // Votes number
} }

View File

@ -285,7 +285,7 @@ type BaseLinkProduct struct {
// BaseLinkRating struct. // BaseLinkRating struct.
type BaseLinkRating struct { type BaseLinkRating struct {
ReviewsCount int `json:"reviews_count"` ReviewsCount json.Number `json:"reviews_count"`
Stars float64 `json:"stars"` Stars float64 `json:"stars"`
} }

View File

@ -135,7 +135,7 @@ type Button struct {
Label string `json:"label"` Label string `json:"label"`
Style ButtonStyle `json:"style"` Style ButtonStyle `json:"style"`
Disabled bool `json:"disabled"` Disabled bool `json:"disabled"`
Emoji ComponentEmoji `json:"emoji"` Emoji *ComponentEmoji `json:"emoji,omitempty"`
// NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID. // NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID.
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
@ -169,11 +169,29 @@ type SelectMenuOption struct {
Label string `json:"label,omitempty"` Label string `json:"label,omitempty"`
Value string `json:"value"` Value string `json:"value"`
Description string `json:"description"` Description string `json:"description"`
Emoji ComponentEmoji `json:"emoji"` Emoji *ComponentEmoji `json:"emoji,omitempty"`
// Determines whenever option is selected by default or not. // Determines whenever option is selected by default or not.
Default bool `json:"default"` Default bool `json:"default"`
} }
// SelectMenuDefaultValueType represents the type of an entity selected by default in auto-populated select menus.
type SelectMenuDefaultValueType string
// SelectMenuDefaultValue types.
const (
SelectMenuDefaultValueUser SelectMenuDefaultValueType = "user"
SelectMenuDefaultValueRole SelectMenuDefaultValueType = "role"
SelectMenuDefaultValueChannel SelectMenuDefaultValueType = "channel"
)
// SelectMenuDefaultValue represents an entity selected by default in auto-populated select menus.
type SelectMenuDefaultValue struct {
// ID of the entity.
ID string `json:"id"`
// Type of the entity.
Type SelectMenuDefaultValueType `json:"type"`
}
// SelectMenuType represents select menu type. // SelectMenuType represents select menu type.
type SelectMenuType ComponentType type SelectMenuType ComponentType
@ -199,6 +217,10 @@ type SelectMenu struct {
// This value determines the maximal amount of selected items in the menu. // This value determines the maximal amount of selected items in the menu.
// If MaxValues or MinValues are greater than one then the user can select multiple items in the component. // If MaxValues or MinValues are greater than one then the user can select multiple items in the component.
MaxValues int `json:"max_values,omitempty"` MaxValues int `json:"max_values,omitempty"`
// List of default values for auto-populated select menus.
// NOTE: Number of entries should be in the range defined by MinValues and MaxValues.
DefaultValues []SelectMenuDefaultValue `json:"default_values,omitempty"`
Options []SelectMenuOption `json:"options,omitempty"` Options []SelectMenuOption `json:"options,omitempty"`
Disabled bool `json:"disabled"` Disabled bool `json:"disabled"`

View File

@ -22,7 +22,7 @@ import (
) )
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/) // VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.27.1" const VERSION = "0.28.1"
// New creates a new Discord session with provided token. // New creates a new Discord session with provided token.
// If the token is for a bot, it must be prefixed with "Bot " // If the token is for a bot, it must be prefixed with "Bot "
@ -38,6 +38,7 @@ func New(token string) (s *Session, err error) {
StateEnabled: true, StateEnabled: true,
Compress: true, Compress: true,
ShouldReconnectOnError: true, ShouldReconnectOnError: true,
ShouldReconnectVoiceOnSessionError: true,
ShouldRetryOnRateLimit: true, ShouldRetryOnRateLimit: true,
ShardID: 0, ShardID: 0,
ShardCount: 1, ShardCount: 1,

View File

@ -42,6 +42,7 @@ var (
EndpointCDNChannelIcons = EndpointCDN + "channel-icons/" EndpointCDNChannelIcons = EndpointCDN + "channel-icons/"
EndpointCDNBanners = EndpointCDN + "banners/" EndpointCDNBanners = EndpointCDN + "banners/"
EndpointCDNGuilds = EndpointCDN + "guilds/" EndpointCDNGuilds = EndpointCDN + "guilds/"
EndpointCDNRoleIcons = EndpointCDN + "role-icons/"
EndpointVoice = EndpointAPI + "/voice/" EndpointVoice = EndpointAPI + "/voice/"
EndpointVoiceRegions = EndpointVoice + "regions" EndpointVoiceRegions = EndpointVoice + "regions"
@ -49,9 +50,8 @@ var (
EndpointUser = func(uID string) string { return EndpointUsers + uID } EndpointUser = func(uID string) string { return EndpointUsers + uID }
EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" } EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" } EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" }
EndpointDefaultUserAvatar = func(uDiscriminator string) string { EndpointDefaultUserAvatar = func(idx int) string {
uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator) return EndpointCDN + "embed/avatars/" + strconv.Itoa(idx) + ".png"
return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png"
} }
EndpointUserBanner = func(uID, cID string) string { EndpointUserBanner = func(uID, cID string) string {
return EndpointCDNBanners + uID + "/" + cID + ".png" return EndpointCDNBanners + uID + "/" + cID + ".png"
@ -104,7 +104,8 @@ var (
EndpointGuildScheduledEvents = func(gID string) string { return EndpointGuilds + gID + "/scheduled-events" } EndpointGuildScheduledEvents = func(gID string) string { return EndpointGuilds + gID + "/scheduled-events" }
EndpointGuildScheduledEvent = func(gID, eID string) string { return EndpointGuilds + gID + "/scheduled-events/" + eID } EndpointGuildScheduledEvent = func(gID, eID string) string { return EndpointGuilds + gID + "/scheduled-events/" + eID }
EndpointGuildScheduledEventUsers = func(gID, eID string) string { return EndpointGuildScheduledEvent(gID, eID) + "/users" } EndpointGuildScheduledEventUsers = func(gID, eID string) string { return EndpointGuildScheduledEvent(gID, eID) + "/users" }
EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "/templates/" + tID } EndpointGuildOnboarding = func(gID string) string { return EndpointGuilds + gID + "/onboarding" }
EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "templates/" + tID }
EndpointGuildTemplates = func(gID string) string { return EndpointGuilds + gID + "/templates" } EndpointGuildTemplates = func(gID string) string { return EndpointGuilds + gID + "/templates" }
EndpointGuildTemplateSync = func(gID, tID string) string { return EndpointGuilds + gID + "/templates/" + tID } EndpointGuildTemplateSync = func(gID, tID string) string { return EndpointGuilds + gID + "/templates/" + tID }
EndpointGuildMemberAvatar = func(gId, uID, aID string) string { EndpointGuildMemberAvatar = func(gId, uID, aID string) string {
@ -114,6 +115,10 @@ var (
return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".gif" return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".gif"
} }
EndpointRoleIcon = func(rID, hash string) string {
return EndpointCDNRoleIcons + rID + "/" + hash + ".png"
}
EndpointChannel = func(cID string) string { return EndpointChannels + cID } EndpointChannel = func(cID string) string { return EndpointChannels + cID }
EndpointChannelThreads = func(cID string) string { return EndpointChannel(cID) + "/threads" } EndpointChannelThreads = func(cID string) string { return EndpointChannel(cID) + "/threads" }
EndpointChannelActiveThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/active" } EndpointChannelActiveThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/active" }

View File

@ -19,6 +19,7 @@ const (
connectEventType = "__CONNECT__" connectEventType = "__CONNECT__"
disconnectEventType = "__DISCONNECT__" disconnectEventType = "__DISCONNECT__"
eventEventType = "__EVENT__" eventEventType = "__EVENT__"
guildAuditLogEntryCreateEventType = "GUILD_AUDIT_LOG_ENTRY_CREATE"
guildBanAddEventType = "GUILD_BAN_ADD" guildBanAddEventType = "GUILD_BAN_ADD"
guildBanRemoveEventType = "GUILD_BAN_REMOVE" guildBanRemoveEventType = "GUILD_BAN_REMOVE"
guildCreateEventType = "GUILD_CREATE" guildCreateEventType = "GUILD_CREATE"
@ -294,6 +295,26 @@ func (eh eventEventHandler) Handle(s *Session, i interface{}) {
} }
} }
// guildAuditLogEntryCreateEventHandler is an event handler for GuildAuditLogEntryCreate events.
type guildAuditLogEntryCreateEventHandler func(*Session, *GuildAuditLogEntryCreate)
// Type returns the event type for GuildAuditLogEntryCreate events.
func (eh guildAuditLogEntryCreateEventHandler) Type() string {
return guildAuditLogEntryCreateEventType
}
// New returns a new instance of GuildAuditLogEntryCreate.
func (eh guildAuditLogEntryCreateEventHandler) New() interface{} {
return &GuildAuditLogEntryCreate{}
}
// Handle is the handler for GuildAuditLogEntryCreate events.
func (eh guildAuditLogEntryCreateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildAuditLogEntryCreate); ok {
eh(s, t)
}
}
// guildBanAddEventHandler is an event handler for GuildBanAdd events. // guildBanAddEventHandler is an event handler for GuildBanAdd events.
type guildBanAddEventHandler func(*Session, *GuildBanAdd) type guildBanAddEventHandler func(*Session, *GuildBanAdd)
@ -1277,6 +1298,8 @@ func handlerForInterface(handler interface{}) EventHandler {
return disconnectEventHandler(v) return disconnectEventHandler(v)
case func(*Session, *Event): case func(*Session, *Event):
return eventEventHandler(v) return eventEventHandler(v)
case func(*Session, *GuildAuditLogEntryCreate):
return guildAuditLogEntryCreateEventHandler(v)
case func(*Session, *GuildBanAdd): case func(*Session, *GuildBanAdd):
return guildBanAddEventHandler(v) return guildBanAddEventHandler(v)
case func(*Session, *GuildBanRemove): case func(*Session, *GuildBanRemove):
@ -1388,6 +1411,7 @@ func init() {
registerInterfaceProvider(channelDeleteEventHandler(nil)) registerInterfaceProvider(channelDeleteEventHandler(nil))
registerInterfaceProvider(channelPinsUpdateEventHandler(nil)) registerInterfaceProvider(channelPinsUpdateEventHandler(nil))
registerInterfaceProvider(channelUpdateEventHandler(nil)) registerInterfaceProvider(channelUpdateEventHandler(nil))
registerInterfaceProvider(guildAuditLogEntryCreateEventHandler(nil))
registerInterfaceProvider(guildBanAddEventHandler(nil)) registerInterfaceProvider(guildBanAddEventHandler(nil))
registerInterfaceProvider(guildBanRemoveEventHandler(nil)) registerInterfaceProvider(guildBanRemoveEventHandler(nil))
registerInterfaceProvider(guildCreateEventHandler(nil)) registerInterfaceProvider(guildCreateEventHandler(nil))

View File

@ -401,3 +401,8 @@ type AutoModerationActionExecution struct {
MatchedKeyword string `json:"matched_keyword"` MatchedKeyword string `json:"matched_keyword"`
MatchedContent string `json:"matched_content"` MatchedContent string `json:"matched_content"`
} }
// GuildAuditLogEntryCreate is the data for a GuildAuditLogEntryCreate event.
type GuildAuditLogEntryCreate struct {
*AuditLogEntry
}

View File

@ -316,6 +316,7 @@ type InteractionData interface {
type ApplicationCommandInteractionData struct { type ApplicationCommandInteractionData struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
CommandType ApplicationCommandType `json:"type"`
Resolved *ApplicationCommandInteractionDataResolved `json:"resolved"` Resolved *ApplicationCommandInteractionDataResolved `json:"resolved"`
// Slash command options // Slash command options
@ -553,6 +554,7 @@ type InteractionResponseData struct {
Embeds []*MessageEmbed `json:"embeds"` Embeds []*MessageEmbed `json:"embeds"`
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
Files []*File `json:"-"` Files []*File `json:"-"`
Attachments *[]*MessageAttachment `json:"attachments,omitempty"`
// NOTE: only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set. // NOTE: only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set.
Flags MessageFlags `json:"flags,omitempty"` Flags MessageFlags `json:"flags,omitempty"`

View File

@ -39,6 +39,7 @@ const (
Romanian Locale = "ro" Romanian Locale = "ro"
Russian Locale = "ru" Russian Locale = "ru"
SpanishES Locale = "es-ES" SpanishES Locale = "es-ES"
SpanishLATAM Locale = "es-419"
Swedish Locale = "sv-SE" Swedish Locale = "sv-SE"
Thai Locale = "th" Thai Locale = "th"
Turkish Locale = "tr" Turkish Locale = "tr"
@ -74,6 +75,7 @@ var Locales = map[Locale]string{
Romanian: "Romanian", Romanian: "Romanian",
Russian: "Russian", Russian: "Russian",
SpanishES: "Spanish (Spain)", SpanishES: "Spanish (Spain)",
SpanishLATAM: "Spanish (LATAM)",
Swedish: "Swedish", Swedish: "Swedish",
Thai: "Thai", Thai: "Thai",
Turkish: "Turkish", Turkish: "Turkish",

View File

@ -148,8 +148,8 @@ type Message struct {
// The thread that was started from this message, includes thread member object // The thread that was started from this message, includes thread member object
Thread *Channel `json:"thread,omitempty"` Thread *Channel `json:"thread,omitempty"`
// An array of Sticker objects, if any were sent. // An array of StickerItem objects, representing sent stickers, if there were any.
StickerItems []*Sticker `json:"sticker_items"` StickerItems []*StickerItem `json:"sticker_items"`
} }
// UnmarshalJSON is a helper function to unmarshal the Message. // UnmarshalJSON is a helper function to unmarshal the Message.
@ -215,6 +215,10 @@ const (
MessageFlagsLoading MessageFlags = 1 << 7 MessageFlagsLoading MessageFlags = 1 << 7
// MessageFlagsFailedToMentionSomeRolesInThread this message failed to mention some roles and add their members to the thread. // MessageFlagsFailedToMentionSomeRolesInThread this message failed to mention some roles and add their members to the thread.
MessageFlagsFailedToMentionSomeRolesInThread MessageFlags = 1 << 8 MessageFlagsFailedToMentionSomeRolesInThread MessageFlags = 1 << 8
// MessageFlagsSuppressNotifications this message will not trigger push and desktop notifications.
MessageFlagsSuppressNotifications MessageFlags = 1 << 12
// MessageFlagsIsVoiceMessage this message is a voice message.
MessageFlagsIsVoiceMessage MessageFlags = 1 << 13
) )
// File stores info about files you e.g. send in messages. // File stores info about files you e.g. send in messages.
@ -233,6 +237,8 @@ type MessageSend struct {
Files []*File `json:"-"` Files []*File `json:"-"`
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
Reference *MessageReference `json:"message_reference,omitempty"` Reference *MessageReference `json:"message_reference,omitempty"`
StickerIDs []string `json:"sticker_ids"`
Flags MessageFlags `json:"flags,omitempty"`
// TODO: Remove this when compatibility is not required. // TODO: Remove this when compatibility is not required.
File *File `json:"-"` File *File `json:"-"`
@ -245,8 +251,8 @@ type MessageSend struct {
// is also where you should get the instance from. // is also where you should get the instance from.
type MessageEdit struct { type MessageEdit struct {
Content *string `json:"content,omitempty"` Content *string `json:"content,omitempty"`
Components []MessageComponent `json:"components"` Components *[]MessageComponent `json:"components,omitempty"`
Embeds []*MessageEmbed `json:"embeds"` Embeds *[]*MessageEmbed `json:"embeds,omitempty"`
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
Flags MessageFlags `json:"flags,omitempty"` Flags MessageFlags `json:"flags,omitempty"`
// Files to append to the message // Files to append to the message
@ -280,14 +286,14 @@ func (m *MessageEdit) SetContent(str string) *MessageEdit {
// SetEmbed is a convenience function for setting the embed, // SetEmbed is a convenience function for setting the embed,
// so you can chain commands. // so you can chain commands.
func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit { func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
m.Embeds = []*MessageEmbed{embed} m.Embeds = &[]*MessageEmbed{embed}
return m return m
} }
// SetEmbeds is a convenience function for setting the embeds, // SetEmbeds is a convenience function for setting the embeds,
// so you can chain commands. // so you can chain commands.
func (m *MessageEdit) SetEmbeds(embeds []*MessageEmbed) *MessageEdit { func (m *MessageEdit) SetEmbeds(embeds []*MessageEmbed) *MessageEdit {
m.Embeds = embeds m.Embeds = &embeds
return m return m
} }
@ -463,17 +469,29 @@ type MessageReference struct {
MessageID string `json:"message_id"` MessageID string `json:"message_id"`
ChannelID string `json:"channel_id,omitempty"` ChannelID string `json:"channel_id,omitempty"`
GuildID string `json:"guild_id,omitempty"` GuildID string `json:"guild_id,omitempty"`
FailIfNotExists *bool `json:"fail_if_not_exists,omitempty"`
} }
// Reference returns MessageReference of given message func (m *Message) reference(failIfNotExists bool) *MessageReference {
func (m *Message) Reference() *MessageReference {
return &MessageReference{ return &MessageReference{
GuildID: m.GuildID, GuildID: m.GuildID,
ChannelID: m.ChannelID, ChannelID: m.ChannelID,
MessageID: m.ID, MessageID: m.ID,
FailIfNotExists: &failIfNotExists,
} }
} }
// Reference returns a MessageReference of the given message.
func (m *Message) Reference() *MessageReference {
return m.reference(true)
}
// SoftReference returns a MessageReference of the given message.
// If the message doesn't exist it will instead be sent as a non-reply message.
func (m *Message) SoftReference() *MessageReference {
return m.reference(false)
}
// ContentWithMentionsReplaced will replace all @<id> mentions with the // ContentWithMentionsReplaced will replace all @<id> mentions with the
// username of the mention. // username of the mention.
func (m *Message) ContentWithMentionsReplaced() (content string) { func (m *Message) ContentWithMentionsReplaced() (content string) {

View File

@ -424,10 +424,11 @@ func (s *Session) UserGuildMember(guildID string, options ...RequestOption) (st
} }
// UserGuilds returns an array of UserGuild structures for all guilds. // UserGuilds returns an array of UserGuild structures for all guilds.
// limit : The number guilds that can be returned. (max 100) // limit : The number guilds that can be returned. (max 200)
// beforeID : If provided all guilds returned will be before given ID. // beforeID : If provided all guilds returned will be before given ID.
// afterID : If provided all guilds returned will be after given ID. // afterID : If provided all guilds returned will be after given ID.
func (s *Session) UserGuilds(limit int, beforeID, afterID string, options ...RequestOption) (st []*UserGuild, err error) { // withCounts : Whether to include approximate member and presence counts or not.
func (s *Session) UserGuilds(limit int, beforeID, afterID string, withCounts bool, options ...RequestOption) (st []*UserGuild, err error) {
v := url.Values{} v := url.Values{}
@ -440,6 +441,9 @@ func (s *Session) UserGuilds(limit int, beforeID, afterID string, options ...Req
if beforeID != "" { if beforeID != "" {
v.Set("before", beforeID) v.Set("before", beforeID)
} }
if withCounts {
v.Set("with_counts", "true")
}
uri := EndpointUserGuilds("@me") uri := EndpointUserGuilds("@me")
@ -672,14 +676,9 @@ func (s *Session) GuildEdit(guildID string, g *GuildParams, options ...RequestOp
// GuildDelete deletes a Guild. // GuildDelete deletes a Guild.
// guildID : The ID of a Guild // guildID : The ID of a Guild
func (s *Session) GuildDelete(guildID string, options ...RequestOption) (st *Guild, err error) { func (s *Session) GuildDelete(guildID string, options ...RequestOption) (err error) {
body, err := s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID), options...) _, err = s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID), options...)
if err != nil {
return
}
err = unmarshal(body, &st)
return return
} }
@ -1700,13 +1699,19 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend,
} }
} }
if data.StickerIDs != nil {
if len(data.StickerIDs) > 3 {
err = fmt.Errorf("cannot send more than 3 stickers")
return
}
}
var response []byte var response []byte
if len(files) > 0 { if len(files) > 0 {
contentType, body, encodeErr := MultipartBodyWithJSON(data, files) contentType, body, encodeErr := MultipartBodyWithJSON(data, files)
if encodeErr != nil { if encodeErr != nil {
return st, encodeErr return st, encodeErr
} }
response, err = s.request("POST", endpoint, contentType, body, endpoint, 0, options...) response, err = s.request("POST", endpoint, contentType, body, endpoint, 0, options...)
} else { } else {
response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...) response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...)
@ -1796,18 +1801,20 @@ func (s *Session) ChannelMessageEditComplex(m *MessageEdit, options ...RequestOp
// TODO: Remove this when compatibility is not required. // TODO: Remove this when compatibility is not required.
if m.Embed != nil { if m.Embed != nil {
if m.Embeds == nil { if m.Embeds == nil {
m.Embeds = []*MessageEmbed{m.Embed} m.Embeds = &[]*MessageEmbed{m.Embed}
} else { } else {
err = fmt.Errorf("cannot specify both Embed and Embeds") err = fmt.Errorf("cannot specify both Embed and Embeds")
return return
} }
} }
for _, embed := range m.Embeds { if m.Embeds != nil {
for _, embed := range *m.Embeds {
if embed.Type == "" { if embed.Type == "" {
embed.Type = "rich" embed.Type = "rich"
} }
} }
}
endpoint := EndpointChannelMessage(m.Channel, m.ID) endpoint := EndpointChannelMessage(m.Channel, m.ID)
@ -2267,7 +2274,7 @@ func (s *Session) WebhookWithToken(webhookID, token string, options ...RequestOp
// webhookID: The ID of a webhook. // webhookID: The ID of a webhook.
// name : The name of the webhook. // name : The name of the webhook.
// avatar : The avatar of the webhook. // avatar : The avatar of the webhook.
func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string, options ...RequestOption) (st *Role, err error) { func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string, options ...RequestOption) (st *Webhook, err error) {
data := struct { data := struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
@ -2290,14 +2297,15 @@ func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string, options
// token : The auth token for the webhook. // token : The auth token for the webhook.
// name : The name of the webhook. // name : The name of the webhook.
// avatar : The avatar of the webhook. // avatar : The avatar of the webhook.
func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string, options ...RequestOption) (st *Role, err error) { func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string, options ...RequestOption) (st *Webhook, err error) {
data := struct { data := struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Avatar string `json:"avatar,omitempty"` Avatar string `json:"avatar,omitempty"`
}{name, avatar} }{name, avatar}
body, err := s.RequestWithBucketID("PATCH", EndpointWebhookToken(webhookID, token), data, EndpointWebhookToken("", ""), options...) var body []byte
body, err = s.RequestWithBucketID("PATCH", EndpointWebhookToken(webhookID, token), data, EndpointWebhookToken("", ""), options...)
if err != nil { if err != nil {
return return
} }
@ -2709,11 +2717,22 @@ func (s *Session) ThreadMemberRemove(threadID, memberID string, options ...Reque
return err return err
} }
// ThreadMember returns thread member object for the specified member of a thread // ThreadMember returns thread member object for the specified member of a thread.
func (s *Session) ThreadMember(threadID, memberID string, options ...RequestOption) (member *ThreadMember, err error) { // withMember : Whether to include a guild member object.
endpoint := EndpointThreadMember(threadID, memberID) func (s *Session) ThreadMember(threadID, memberID string, withMember bool, options ...RequestOption) (member *ThreadMember, err error) {
uri := EndpointThreadMember(threadID, memberID)
queryParams := url.Values{}
if withMember {
queryParams.Set("with_member", "true")
}
if len(queryParams) > 0 {
uri += "?" + queryParams.Encode()
}
var body []byte var body []byte
body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) body, err = s.RequestWithBucketID("GET", uri, nil, uri, options...)
if err != nil { if err != nil {
return return
@ -2724,9 +2743,29 @@ func (s *Session) ThreadMember(threadID, memberID string, options ...RequestOpti
} }
// ThreadMembers returns all members of specified thread. // ThreadMembers returns all members of specified thread.
func (s *Session) ThreadMembers(threadID string, options ...RequestOption) (members []*ThreadMember, err error) { // limit : Max number of thread members to return (1-100). Defaults to 100.
// afterID : Get thread members after this user ID.
// withMember : Whether to include a guild member object for each thread member.
func (s *Session) ThreadMembers(threadID string, limit int, withMember bool, afterID string, options ...RequestOption) (members []*ThreadMember, err error) {
uri := EndpointThreadMembers(threadID)
queryParams := url.Values{}
if withMember {
queryParams.Set("with_member", "true")
}
if limit > 0 {
queryParams.Set("limit", strconv.Itoa(limit))
}
if afterID != "" {
queryParams.Set("after", afterID)
}
if len(queryParams) > 0 {
uri += "?" + queryParams.Encode()
}
var body []byte var body []byte
body, err = s.RequestWithBucketID("GET", EndpointThreadMembers(threadID), nil, EndpointThreadMembers(threadID), options...) body, err = s.RequestWithBucketID("GET", uri, nil, uri, options...)
if err != nil { if err != nil {
return return
@ -3248,6 +3287,37 @@ func (s *Session) GuildScheduledEventUsers(guildID, eventID string, limit int, w
return return
} }
// GuildOnboarding returns onboarding configuration of a guild.
// guildID : The ID of the guild
func (s *Session) GuildOnboarding(guildID string, options ...RequestOption) (onboarding *GuildOnboarding, err error) {
endpoint := EndpointGuildOnboarding(guildID)
var body []byte
body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...)
if err != nil {
return
}
err = unmarshal(body, &onboarding)
return
}
// GuildOnboardingEdit edits onboarding configuration of a guild.
// guildID : The ID of the guild
// o : New GuildOnboarding data
func (s *Session) GuildOnboardingEdit(guildID string, o *GuildOnboarding, options ...RequestOption) (onboarding *GuildOnboarding, err error) {
endpoint := EndpointGuildOnboarding(guildID)
var body []byte
body, err = s.RequestWithBucketID("PUT", endpoint, o, endpoint, options...)
if err != nil {
return
}
err = unmarshal(body, &onboarding)
return
}
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Functions specific to auto moderation // Functions specific to auto moderation
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@ -42,6 +42,9 @@ type Session struct {
// Should the session reconnect the websocket on errors. // Should the session reconnect the websocket on errors.
ShouldReconnectOnError bool ShouldReconnectOnError bool
// Should voice connections reconnect on a session reconnect.
ShouldReconnectVoiceOnSessionError bool
// Should the session retry requests when rate limited. // Should the session retry requests when rate limited.
ShouldRetryOnRateLimit bool ShouldRetryOnRateLimit bool
@ -285,7 +288,9 @@ const (
ChannelTypeGuildPublicThread ChannelType = 11 ChannelTypeGuildPublicThread ChannelType = 11
ChannelTypeGuildPrivateThread ChannelType = 12 ChannelTypeGuildPrivateThread ChannelType = 12
ChannelTypeGuildStageVoice ChannelType = 13 ChannelTypeGuildStageVoice ChannelType = 13
ChannelTypeGuildDirectory ChannelType = 14
ChannelTypeGuildForum ChannelType = 15 ChannelTypeGuildForum ChannelType = 15
ChannelTypeGuildMedia ChannelType = 16
) )
// ChannelFlags represent flags of a channel/thread. // ChannelFlags represent flags of a channel/thread.
@ -440,7 +445,7 @@ type ChannelEdit struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Topic string `json:"topic,omitempty"` Topic string `json:"topic,omitempty"`
NSFW *bool `json:"nsfw,omitempty"` NSFW *bool `json:"nsfw,omitempty"`
Position int `json:"position"` Position *int `json:"position,omitempty"`
Bitrate int `json:"bitrate,omitempty"` Bitrate int `json:"bitrate,omitempty"`
UserLimit int `json:"user_limit,omitempty"` UserLimit int `json:"user_limit,omitempty"`
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"` PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
@ -528,6 +533,10 @@ type ThreadMember struct {
JoinTimestamp time.Time `json:"join_timestamp"` JoinTimestamp time.Time `json:"join_timestamp"`
// Any user-thread settings, currently only used for notifications // Any user-thread settings, currently only used for notifications
Flags int `json:"flags"` Flags int `json:"flags"`
// Additional information about the user.
// NOTE: only present if the withMember parameter is set to true
// when calling Session.ThreadMembers or Session.ThreadMember.
Member *Member `json:"member,omitempty"`
} }
// ThreadsList represents a list of threads alongisde with thread member objects for the current user. // ThreadsList represents a list of threads alongisde with thread member objects for the current user.
@ -649,6 +658,13 @@ type Sticker struct {
SortValue int `json:"sort_value"` SortValue int `json:"sort_value"`
} }
// StickerItem represents the smallest amount of data required to render a sticker. A partial sticker object.
type StickerItem struct {
ID string `json:"id"`
Name string `json:"name"`
FormatType StickerFormat `json:"format_type"`
}
// StickerPack represents a pack of standard stickers. // StickerPack represents a pack of standard stickers.
type StickerPack struct { type StickerPack struct {
ID string `json:"id"` ID string `json:"id"`
@ -1067,6 +1083,109 @@ type GuildScheduledEventUser struct {
Member *Member `json:"member"` Member *Member `json:"member"`
} }
// GuildOnboardingMode defines the criteria used to satisfy constraints that are required for enabling onboarding.
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object-onboarding-mode
type GuildOnboardingMode int
// Block containing known GuildOnboardingMode values.
const (
// GuildOnboardingModeDefault counts default channels towards constraints.
GuildOnboardingModeDefault GuildOnboardingMode = 0
// GuildOnboardingModeAdvanced counts default channels and questions towards constraints.
GuildOnboardingModeAdvanced GuildOnboardingMode = 1
)
// GuildOnboarding represents the onboarding flow for a guild.
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object
type GuildOnboarding struct {
// ID of the guild this onboarding flow is part of.
GuildID string `json:"guild_id,omitempty"`
// Prompts shown during onboarding and in the customize community (Channels & Roles) tab.
Prompts *[]GuildOnboardingPrompt `json:"prompts,omitempty"`
// Channel IDs that members get opted into automatically.
DefaultChannelIDs []string `json:"default_channel_ids,omitempty"`
// Whether onboarding is enabled in the guild.
Enabled *bool `json:"enabled,omitempty"`
// Mode of onboarding.
Mode *GuildOnboardingMode `json:"mode,omitempty"`
}
// GuildOnboardingPromptType is the type of an onboarding prompt.
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object-prompt-types
type GuildOnboardingPromptType int
// Block containing known GuildOnboardingPromptType values.
const (
GuildOnboardingPromptTypeMultipleChoice GuildOnboardingPromptType = 0
GuildOnboardingPromptTypeDropdown GuildOnboardingPromptType = 1
)
// GuildOnboardingPrompt is a prompt shown during onboarding and in the customize community (Channels & Roles) tab.
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object-onboarding-prompt-structure
type GuildOnboardingPrompt struct {
// ID of the prompt.
// NOTE: always requires to be a valid snowflake (e.g. "0"), see
// https://github.com/discord/discord-api-docs/issues/6320 for more information.
ID string `json:"id,omitempty"`
// Type of the prompt.
Type GuildOnboardingPromptType `json:"type"`
// Options available within the prompt.
Options []GuildOnboardingPromptOption `json:"options"`
// Title of the prompt.
Title string `json:"title"`
// Indicates whether users are limited to selecting one option for the prompt.
SingleSelect bool `json:"single_select"`
// Indicates whether the prompt is required before a user completes the onboarding flow.
Required bool `json:"required"`
// Indicates whether the prompt is present in the onboarding flow.
// If false, the prompt will only appear in the customize community (Channels & Roles) tab.
InOnboarding bool `json:"in_onboarding"`
}
// GuildOnboardingPromptOption is an option available within an onboarding prompt.
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object-prompt-option-structure
type GuildOnboardingPromptOption struct {
// ID of the prompt option.
ID string `json:"id,omitempty"`
// IDs for channels a member is added to when the option is selected.
ChannelIDs []string `json:"channel_ids"`
// IDs for roles assigned to a member when the option is selected.
RoleIDs []string `json:"role_ids"`
// Emoji of the option.
// NOTE: when creating or updating a prompt option
// EmojiID, EmojiName and EmojiAnimated should be used instead.
Emoji *Emoji `json:"emoji,omitempty"`
// Title of the option.
Title string `json:"title"`
// Description of the option.
Description string `json:"description"`
// ID of the option's emoji.
// NOTE: only used when creating or updating a prompt option.
EmojiID string `json:"emoji_id,omitempty"`
// Name of the option's emoji.
// NOTE: only used when creating or updating a prompt option.
EmojiName string `json:"emoji_name,omitempty"`
// Whether the option's emoji is animated.
// NOTE: only used when creating or updating a prompt option.
EmojiAnimated *bool `json:"emoji_animated,omitempty"`
}
// A GuildTemplate represents a replicable template for guild creation // A GuildTemplate represents a replicable template for guild creation
type GuildTemplate struct { type GuildTemplate struct {
// The unique code for the guild template // The unique code for the guild template
@ -1157,6 +1276,14 @@ type UserGuild struct {
Owner bool `json:"owner"` Owner bool `json:"owner"`
Permissions int64 `json:"permissions,string"` Permissions int64 `json:"permissions,string"`
Features []GuildFeature `json:"features"` Features []GuildFeature `json:"features"`
// Approximate number of members in this guild.
// NOTE: this field is only filled when withCounts is true.
ApproximateMemberCount int `json:"approximate_member_count"`
// Approximate number of non-offline members in this guild.
// NOTE: this field is only filled when withCounts is true.
ApproximatePresenceCount int `json:"approximate_presence_count"`
} }
// GuildFeature indicates the presence of a feature in a guild // GuildFeature indicates the presence of a feature in a guild
@ -1239,13 +1366,51 @@ type Role struct {
// This is a combination of bit masks; the presence of a certain permission can // This is a combination of bit masks; the presence of a certain permission can
// be checked by performing a bitwise AND between this int and the permission. // be checked by performing a bitwise AND between this int and the permission.
Permissions int64 `json:"permissions,string"` Permissions int64 `json:"permissions,string"`
// The hash of the role icon. Use Role.IconURL to retrieve the icon's URL.
Icon string `json:"icon"`
// The emoji assigned to this role.
UnicodeEmoji string `json:"unicode_emoji"`
// The flags of the role, which describe its extra features.
// This is a combination of bit masks; the presence of a certain flag can
// be checked by performing a bitwise AND between this int and the flag.
Flags RoleFlags `json:"flags"`
} }
// RoleFlags represent the flags of a Role.
// https://discord.com/developers/docs/topics/permissions#role-object-role-flags
type RoleFlags int
// Block containing known RoleFlags values.
const (
// RoleFlagInPrompt indicates whether the Role is selectable by members in an onboarding prompt.
RoleFlagInPrompt RoleFlags = 1 << 0
)
// Mention returns a string which mentions the role // Mention returns a string which mentions the role
func (r *Role) Mention() string { func (r *Role) Mention() string {
return fmt.Sprintf("<@&%s>", r.ID) return fmt.Sprintf("<@&%s>", r.ID)
} }
// IconURL returns the URL of the role's icon.
//
// size: The size of the desired role icon as a power of two
// Image size can be any power of two between 16 and 4096.
func (r *Role) IconURL(size string) string {
if r.Icon == "" {
return ""
}
URL := EndpointRoleIcon(r.ID, r.Icon)
if size != "" {
return URL + "?size=" + size
}
return URL
}
// RoleParams represents the parameters needed to create or update a Role // RoleParams represents the parameters needed to create or update a Role
type RoleParams struct { type RoleParams struct {
// The role's name // The role's name
@ -1258,6 +1423,12 @@ type RoleParams struct {
Permissions *int64 `json:"permissions,omitempty,string"` Permissions *int64 `json:"permissions,omitempty,string"`
// Whether this role is mentionable // Whether this role is mentionable
Mentionable *bool `json:"mentionable,omitempty"` Mentionable *bool `json:"mentionable,omitempty"`
// The role's unicode emoji.
// NOTE: can only be set if the guild has the ROLE_ICONS feature.
UnicodeEmoji *string `json:"unicode_emoji,omitempty"`
// The role's icon image encoded in base64.
// NOTE: can only be set if the guild has the ROLE_ICONS feature.
Icon *string `json:"icon,omitempty"`
} }
// Roles are a collection of Role // Roles are a collection of Role
@ -1330,6 +1501,22 @@ type Assets struct {
SmallText string `json:"small_text,omitempty"` SmallText string `json:"small_text,omitempty"`
} }
// MemberFlags represent flags of a guild member.
// https://discord.com/developers/docs/resources/guild#guild-member-object-guild-member-flags
type MemberFlags int
// Block containing known MemberFlags values.
const (
// MemberFlagDidRejoin indicates whether the Member has left and rejoined the guild.
MemberFlagDidRejoin MemberFlags = 1 << 0
// MemberFlagCompletedOnboarding indicates whether the Member has completed onboarding.
MemberFlagCompletedOnboarding MemberFlags = 1 << 1
// MemberFlagBypassesVerification indicates whether the Member is exempt from guild verification requirements.
MemberFlagBypassesVerification MemberFlags = 1 << 2
// MemberFlagStartedOnboarding indicates whether the Member has started onboarding.
MemberFlagStartedOnboarding MemberFlags = 1 << 3
)
// A Member stores user information for Guild members. A guild // A Member stores user information for Guild members. A guild
// member represents a certain user's presence in a guild. // member represents a certain user's presence in a guild.
type Member struct { type Member struct {
@ -1360,6 +1547,10 @@ type Member struct {
// When the user used their Nitro boost on the server // When the user used their Nitro boost on the server
PremiumSince *time.Time `json:"premium_since"` PremiumSince *time.Time `json:"premium_since"`
// The flags of this member. This is a combination of bit masks; the presence of a certain
// flag can be checked by performing a bitwise AND between this int and the flag.
Flags MemberFlags `json:"flags"`
// Is true while the member hasn't accepted the membership screen. // Is true while the member hasn't accepted the membership screen.
Pending bool `json:"pending"` Pending bool `json:"pending"`
@ -1391,6 +1582,15 @@ func (m *Member) AvatarURL(size string) string {
} }
// DisplayName returns the member's guild nickname if they have one,
// otherwise it returns their discord display name.
func (m *Member) DisplayName() string {
if m.Nick != "" {
return m.Nick
}
return m.User.GlobalName
}
// ClientStatus stores the online, offline, idle, or dnd status of each device of a Guild member. // ClientStatus stores the online, offline, idle, or dnd status of each device of a Guild member.
type ClientStatus struct { type ClientStatus struct {
Desktop Status `json:"desktop"` Desktop Status `json:"desktop"`
@ -1746,6 +1946,10 @@ type AuditLogOptions struct {
ID string `json:"id"` ID string `json:"id"`
Type *AuditLogOptionsType `json:"type"` Type *AuditLogOptionsType `json:"type"`
RoleName string `json:"role_name"` RoleName string `json:"role_name"`
ApplicationID string `json:"application_id"`
AutoModerationRuleName string `json:"auto_moderation_rule_name"`
AutoModerationRuleTriggerType string `json:"auto_moderation_rule_trigger_type"`
IntegrationType string `json:"integration_type"`
} }
// AuditLogOptionsType of the AuditLogOption // AuditLogOptionsType of the AuditLogOption
@ -1754,8 +1958,8 @@ type AuditLogOptionsType string
// Valid Types for AuditLogOptionsType // Valid Types for AuditLogOptionsType
const ( const (
AuditLogOptionsTypeMember AuditLogOptionsType = "member" AuditLogOptionsTypeRole AuditLogOptionsType = "0"
AuditLogOptionsTypeRole AuditLogOptionsType = "role" AuditLogOptionsTypeMember AuditLogOptionsType = "1"
) )
// AuditLogAction is the Action of the AuditLog (see AuditLogAction* consts) // AuditLogAction is the Action of the AuditLog (see AuditLogAction* consts)
@ -1816,7 +2020,7 @@ const (
AuditLogActionStickerDelete AuditLogAction = 92 AuditLogActionStickerDelete AuditLogAction = 92
AuditLogGuildScheduledEventCreate AuditLogAction = 100 AuditLogGuildScheduledEventCreate AuditLogAction = 100
AuditLogGuildScheduledEventUpdare AuditLogAction = 101 AuditLogGuildScheduledEventUpdate AuditLogAction = 101
AuditLogGuildScheduledEventDelete AuditLogAction = 102 AuditLogGuildScheduledEventDelete AuditLogAction = 102
AuditLogActionThreadCreate AuditLogAction = 110 AuditLogActionThreadCreate AuditLogAction = 110
@ -1824,6 +2028,16 @@ const (
AuditLogActionThreadDelete AuditLogAction = 112 AuditLogActionThreadDelete AuditLogAction = 112
AuditLogActionApplicationCommandPermissionUpdate AuditLogAction = 121 AuditLogActionApplicationCommandPermissionUpdate AuditLogAction = 121
AuditLogActionAutoModerationRuleCreate AuditLogAction = 140
AuditLogActionAutoModerationRuleUpdate AuditLogAction = 141
AuditLogActionAutoModerationRuleDelete AuditLogAction = 142
AuditLogActionAutoModerationBlockMessage AuditLogAction = 143
AuditLogActionAutoModerationFlagToChannel AuditLogAction = 144
AuditLogActionAutoModerationUserCommunicationDisabled AuditLogAction = 145
AuditLogActionCreatorMonetizationRequestCreated AuditLogAction = 150
AuditLogActionCreatorMonetizationTermsAccepted AuditLogAction = 151
) )
// GuildMemberParams stores data needed to update a member // GuildMemberParams stores data needed to update a member
@ -2341,6 +2555,9 @@ const (
ErrCodeCannotUpdateAFinishedEvent = 180000 ErrCodeCannotUpdateAFinishedEvent = 180000
ErrCodeFailedToCreateStageNeededForStageEvent = 180002 ErrCodeFailedToCreateStageNeededForStageEvent = 180002
ErrCodeCannotEnableOnboardingRequirementsAreNotMet = 350000
ErrCodeCannotUpdateOnboardingWhileBelowRequirements = 350001
) )
// Intent is the type of a Gateway Intent // Intent is the type of a Gateway Intent
@ -2351,7 +2568,7 @@ type Intent int
const ( const (
IntentGuilds Intent = 1 << 0 IntentGuilds Intent = 1 << 0
IntentGuildMembers Intent = 1 << 1 IntentGuildMembers Intent = 1 << 1
IntentGuildBans Intent = 1 << 2 IntentGuildModeration Intent = 1 << 2
IntentGuildEmojis Intent = 1 << 3 IntentGuildEmojis Intent = 1 << 3
IntentGuildIntegrations Intent = 1 << 4 IntentGuildIntegrations Intent = 1 << 4
IntentGuildWebhooks Intent = 1 << 5 IntentGuildWebhooks Intent = 1 << 5
@ -2371,6 +2588,8 @@ const (
// TODO: remove when compatibility is not needed // TODO: remove when compatibility is not needed
IntentGuildBans Intent = IntentGuildModeration
IntentsGuilds Intent = 1 << 0 IntentsGuilds Intent = 1 << 0
IntentsGuildMembers Intent = 1 << 1 IntentsGuildMembers Intent = 1 << 1
IntentsGuildBans Intent = 1 << 2 IntentsGuildBans Intent = 1 << 2

View File

@ -1,5 +1,9 @@
package discordgo package discordgo
import (
"strconv"
)
// UserFlags is the flags of "user" (see UserFlags* consts) // UserFlags is the flags of "user" (see UserFlags* consts)
// https://discord.com/developers/docs/resources/user#user-object-user-flags // https://discord.com/developers/docs/resources/user#user-object-user-flags
type UserFlags int type UserFlags int
@ -20,6 +24,20 @@ const (
UserFlagVerifiedBot UserFlags = 1 << 16 UserFlagVerifiedBot UserFlags = 1 << 16
UserFlagVerifiedBotDeveloper UserFlags = 1 << 17 UserFlagVerifiedBotDeveloper UserFlags = 1 << 17
UserFlagDiscordCertifiedModerator UserFlags = 1 << 18 UserFlagDiscordCertifiedModerator UserFlags = 1 << 18
UserFlagBotHTTPInteractions UserFlags = 1 << 19
UserFlagActiveBotDeveloper UserFlags = 1 << 22
)
// UserPremiumType is the type of premium (nitro) subscription a user has (see UserPremiumType* consts).
// https://discord.com/developers/docs/resources/user#user-object-premium-types
type UserPremiumType int
// Valid UserPremiumType values.
const (
UserPremiumTypeNone UserPremiumType = 0
UserPremiumTypeNitroClassic UserPremiumType = 1
UserPremiumTypeNitro UserPremiumType = 2
UserPremiumTypeNitroBasic UserPremiumType = 3
) )
// A User stores all data for an individual Discord user. // A User stores all data for an individual Discord user.
@ -44,6 +62,10 @@ type User struct {
// The discriminator of the user (4 numbers after name). // The discriminator of the user (4 numbers after name).
Discriminator string `json:"discriminator"` Discriminator string `json:"discriminator"`
// The user's display name, if it is set.
// For bots, this is the application name.
GlobalName string `json:"global_name"`
// The token of the user. This is only present for // The token of the user. This is only present for
// the user represented by the current session. // the user represented by the current session.
Token string `json:"token"` Token string `json:"token"`
@ -70,7 +92,7 @@ type User struct {
// The type of Nitro subscription on a user's account. // The type of Nitro subscription on a user's account.
// Only available when the request is authorized via a Bearer token. // Only available when the request is authorized via a Bearer token.
PremiumType int `json:"premium_type"` PremiumType UserPremiumType `json:"premium_type"`
// Whether the user is an Official Discord System user (part of the urgent message system). // Whether the user is an Official Discord System user (part of the urgent message system).
System bool `json:"system"` System bool `json:"system"`
@ -81,7 +103,14 @@ type User struct {
} }
// String returns a unique identifier of the form username#discriminator // String returns a unique identifier of the form username#discriminator
// or just username, if the discriminator is set to "0".
func (u *User) String() string { func (u *User) String() string {
// If the user has been migrated from the legacy username system, their discriminator is "0".
// See https://support-dev.discord.com/hc/en-us/articles/13667755828631
if u.Discriminator == "0" {
return u.Username
}
return u.Username + "#" + u.Discriminator return u.Username + "#" + u.Discriminator
} }
@ -91,17 +120,35 @@ func (u *User) Mention() string {
} }
// AvatarURL returns a URL to the user's avatar. // AvatarURL returns a URL to the user's avatar.
//
// size: The size of the user's avatar as a power of two // size: The size of the user's avatar as a power of two
// if size is an empty string, no size parameter will // if size is an empty string, no size parameter will
// be added to the URL. // be added to the URL.
func (u *User) AvatarURL(size string) string { func (u *User) AvatarURL(size string) string {
return avatarURL(u.Avatar, EndpointDefaultUserAvatar(u.Discriminator), return avatarURL(
EndpointUserAvatar(u.ID, u.Avatar), EndpointUserAvatarAnimated(u.ID, u.Avatar), size) u.Avatar,
EndpointDefaultUserAvatar(u.DefaultAvatarIndex()),
EndpointUserAvatar(u.ID, u.Avatar),
EndpointUserAvatarAnimated(u.ID, u.Avatar),
size,
)
} }
// BannerURL returns the URL of the users's banner image. // BannerURL returns the URL of the users's banner image.
//
// size: The size of the desired banner image as a power of two // size: The size of the desired banner image as a power of two
// Image size can be any power of two between 16 and 4096. // Image size can be any power of two between 16 and 4096.
func (u *User) BannerURL(size string) string { func (u *User) BannerURL(size string) string {
return bannerURL(u.Banner, EndpointUserBanner(u.ID, u.Banner), EndpointUserBannerAnimated(u.ID, u.Banner), size) return bannerURL(u.Banner, EndpointUserBanner(u.ID, u.Banner), EndpointUserBannerAnimated(u.ID, u.Banner), size)
} }
// DefaultAvatarIndex returns the index of the user's default avatar.
func (u *User) DefaultAvatarIndex() int {
if u.Discriminator == "0" {
id, _ := strconv.ParseUint(u.ID, 10, 64)
return int((id >> 22) % 6)
}
id, _ := strconv.Atoi(u.Discriminator)
return id % 5
}

View File

@ -294,11 +294,15 @@ func (v *VoiceConnection) open() (err error) {
if v.sessionID != "" { if v.sessionID != "" {
break break
} }
if i > 20 { // only loop for up to 1 second total if i > 20 { // only loop for up to 1 second total
return fmt.Errorf("did not receive voice Session ID in time") return fmt.Errorf("did not receive voice Session ID in time")
} }
// Release the lock, so sessionID can be populated upon receiving a VoiceStateUpdate event.
v.Unlock()
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
i++ i++
v.Lock()
} }
// Connect to VoiceConnection Websocket // Connect to VoiceConnection Websocket

View File

@ -34,10 +34,14 @@ type WebhookParams struct {
Files []*File `json:"-"` Files []*File `json:"-"`
Components []MessageComponent `json:"components"` Components []MessageComponent `json:"components"`
Embeds []*MessageEmbed `json:"embeds,omitempty"` Embeds []*MessageEmbed `json:"embeds,omitempty"`
Attachments []*MessageAttachment `json:"attachments,omitempty"`
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
// Only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set. // Only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set.
// MessageFlagsEphemeral can only be set when using Followup Message Create endpoint. // MessageFlagsEphemeral can only be set when using Followup Message Create endpoint.
Flags MessageFlags `json:"flags,omitempty"` Flags MessageFlags `json:"flags,omitempty"`
// Name of the thread to create.
// NOTE: can only be set if the webhook channel is a forum.
ThreadName string `json:"thread_name,omitempty"`
} }
// WebhookEdit stores data for editing of a webhook message. // WebhookEdit stores data for editing of a webhook message.
@ -46,5 +50,6 @@ type WebhookEdit struct {
Components *[]MessageComponent `json:"components,omitempty"` Components *[]MessageComponent `json:"components,omitempty"`
Embeds *[]*MessageEmbed `json:"embeds,omitempty"` Embeds *[]*MessageEmbed `json:"embeds,omitempty"`
Files []*File `json:"-"` Files []*File `json:"-"`
Attachments *[]*MessageAttachment `json:"attachments,omitempty"`
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
} }

View File

@ -389,6 +389,26 @@ func (s *Session) UpdateListeningStatus(name string) (err error) {
return s.UpdateStatusComplex(*newUpdateStatusData(0, ActivityTypeListening, name, "")) return s.UpdateStatusComplex(*newUpdateStatusData(0, ActivityTypeListening, name, ""))
} }
// UpdateCustomStatus is used to update the user's custom status.
// If state!="" then set the custom status.
// Else, set user to active and remove the custom status.
func (s *Session) UpdateCustomStatus(state string) (err error) {
data := UpdateStatusData{
Status: "online",
}
if state != "" {
// Discord requires a non-empty activity name, therefore we provide "Custom Status" as a placeholder.
data.Activities = []*Activity{{
Name: "Custom Status",
Type: ActivityTypeCustom,
State: state,
}}
}
return s.UpdateStatusComplex(data)
}
// UpdateStatusComplex allows for sending the raw status update data untouched by discordgo. // UpdateStatusComplex allows for sending the raw status update data untouched by discordgo.
func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) { func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) {
// The comment does say "untouched by discordgo", but we might need to lie a bit here. // The comment does say "untouched by discordgo", but we might need to lie a bit here.
@ -862,6 +882,7 @@ func (s *Session) reconnect() {
// However, there seems to be cases where something "weird" // However, there seems to be cases where something "weird"
// happens. So we're doing this for now just to improve // happens. So we're doing this for now just to improve
// stability in those edge cases. // stability in those edge cases.
if s.ShouldReconnectVoiceOnSessionError {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
for _, v := range s.VoiceConnections { for _, v := range s.VoiceConnections {
@ -872,7 +893,7 @@ func (s *Session) reconnect() {
// This is here just to prevent violently spamming the // This is here just to prevent violently spamming the
// voice reconnects // voice reconnects
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
}
} }
return return
} }

View File

@ -1 +1,3 @@
dist/ dist/
.idea

View File

@ -28,6 +28,12 @@ func MakeInstruction(opcode parser.Opcode, operands ...int) []byte {
n := uint16(o) n := uint16(o)
instruction[offset] = byte(n >> 8) instruction[offset] = byte(n >> 8)
instruction[offset+1] = byte(n) instruction[offset+1] = byte(n)
case 4:
n := uint32(o)
instruction[offset] = byte(n >> 24)
instruction[offset+1] = byte(n >> 16)
instruction[offset+2] = byte(n >> 8)
instruction[offset+3] = byte(n)
} }
offset += width offset += width
} }

View File

@ -351,7 +351,7 @@ func (e *ImportExpr) End() Pos {
} }
func (e *ImportExpr) String() string { func (e *ImportExpr) String() string {
return `import("` + e.ModuleName + `")"` return `import("` + e.ModuleName + `")`
} }
// IndexExpr represents an index expression. // IndexExpr represents an index expression.

View File

@ -106,10 +106,10 @@ var OpcodeOperands = [...][]int{
OpNotEqual: {}, OpNotEqual: {},
OpMinus: {}, OpMinus: {},
OpLNot: {}, OpLNot: {},
OpJumpFalsy: {2}, OpJumpFalsy: {4},
OpAndJump: {2}, OpAndJump: {4},
OpOrJump: {2}, OpOrJump: {4},
OpJump: {2}, OpJump: {4},
OpNull: {}, OpNull: {},
OpGetGlobal: {2}, OpGetGlobal: {2},
OpSetGlobal: {2}, OpSetGlobal: {2},
@ -149,6 +149,8 @@ func ReadOperands(numOperands []int, ins []byte) (operands []int, offset int) {
operands = append(operands, int(ins[offset])) operands = append(operands, int(ins[offset]))
case 2: case 2:
operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8) operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8)
case 4:
operands = append(operands, int(ins[offset+3])|int(ins[offset+2])<<8|int(ins[offset+1])<<16|int(ins[offset])<<24)
} }
offset += width offset += width
} }

View File

@ -6,11 +6,14 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"runtime"
"github.com/d5/tengo/v2" "github.com/d5/tengo/v2"
) )
var osModule = map[string]tengo.Object{ var osModule = map[string]tengo.Object{
"platform": &tengo.String{Value: runtime.GOOS},
"arch": &tengo.String{Value: runtime.GOARCH},
"o_rdonly": &tengo.Int{Value: int64(os.O_RDONLY)}, "o_rdonly": &tengo.Int{Value: int64(os.O_RDONLY)},
"o_wronly": &tengo.Int{Value: int64(os.O_WRONLY)}, "o_wronly": &tengo.Int{Value: int64(os.O_WRONLY)},
"o_rdwr": &tengo.Int{Value: int64(os.O_RDWR)}, "o_rdwr": &tengo.Int{Value: int64(os.O_RDWR)},

17
vendor/github.com/d5/tengo/v2/vm.go generated vendored
View File

@ -218,30 +218,30 @@ func (v *VM) run() {
return return
} }
case parser.OpJumpFalsy: case parser.OpJumpFalsy:
v.ip += 2 v.ip += 4
v.sp-- v.sp--
if v.stack[v.sp].IsFalsy() { if v.stack[v.sp].IsFalsy() {
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24
v.ip = pos - 1 v.ip = pos - 1
} }
case parser.OpAndJump: case parser.OpAndJump:
v.ip += 2 v.ip += 4
if v.stack[v.sp-1].IsFalsy() { if v.stack[v.sp-1].IsFalsy() {
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24
v.ip = pos - 1 v.ip = pos - 1
} else { } else {
v.sp-- v.sp--
} }
case parser.OpOrJump: case parser.OpOrJump:
v.ip += 2 v.ip += 4
if v.stack[v.sp-1].IsFalsy() { if v.stack[v.sp-1].IsFalsy() {
v.sp-- v.sp--
} else { } else {
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24
v.ip = pos - 1 v.ip = pos - 1
} }
case parser.OpJump: case parser.OpJump:
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 pos := int(v.curInsts[v.ip+4]) | int(v.curInsts[v.ip+3])<<8 | int(v.curInsts[v.ip+2])<<16 | int(v.curInsts[v.ip+1])<<24
v.ip = pos - 1 v.ip = pos - 1
case parser.OpSetGlobal: case parser.OpSetGlobal:
v.ip += 2 v.ip += 2
@ -534,6 +534,9 @@ func (v *VM) run() {
} }
v.stack[v.sp] = val v.stack[v.sp] = val
v.sp++ v.sp++
default:
v.err = fmt.Errorf("not indexable: %s", left.TypeName())
return
} }
case parser.OpCall: case parser.OpCall:
numArgs := int(v.curInsts[v.ip+1]) numArgs := int(v.curInsts[v.ip+1])

13
vendor/github.com/fsnotify/fsnotify/.cirrus.yml generated vendored Normal file
View File

@ -0,0 +1,13 @@
freebsd_task:
name: 'FreeBSD'
freebsd_instance:
image_family: freebsd-13-2
install_script:
- pkg update -f
- pkg install -y go
test_script:
# run tests as user "cirrus" instead of root
- pw useradd cirrus -m
- chown -R cirrus:cirrus .
- FSNOTIFY_BUFFER=4096 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
- sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...

View File

@ -4,3 +4,4 @@
# Output of go build ./cmd/fsnotify # Output of go build ./cmd/fsnotify
/fsnotify /fsnotify
/fsnotify.exe

View File

@ -1,16 +1,87 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. Unreleased
----------
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
Nothing yet. Nothing yet.
## [1.6.0] - 2022-10-13 1.7.0 - 2023-10-22
------------------
This version of fsnotify needs Go 1.17.
### Additions
- illumos: add FEN backend to support illumos and Solaris. ([#371])
- all: add `NewBufferedWatcher()` to use a buffered channel, which can be useful
in cases where you can't control the kernel buffer and receive a large number
of events in bursts. ([#550], [#572])
- all: add `AddWith()`, which is identical to `Add()` but allows passing
options. ([#521])
- windows: allow setting the ReadDirectoryChangesW() buffer size with
`fsnotify.WithBufferSize()`; the default of 64K is the highest value that
works on all platforms and is enough for most purposes, but in some cases a
highest buffer is needed. ([#521])
### Changes and fixes
- inotify: remove watcher if a watched path is renamed ([#518])
After a rename the reported name wasn't updated, or even an empty string.
Inotify doesn't provide any good facilities to update it, so just remove the
watcher. This is already how it worked on kqueue and FEN.
On Windows this does work, and remains working.
- windows: don't listen for file attribute changes ([#520])
File attribute changes are sent as `FILE_ACTION_MODIFIED` by the Windows API,
with no way to see if they're a file write or attribute change, so would show
up as a fsnotify.Write event. This is never useful, and could result in many
spurious Write events.
- windows: return `ErrEventOverflow` if the buffer is full ([#525])
Before it would merely return "short read", making it hard to detect this
error.
- kqueue: make sure events for all files are delivered properly when removing a
watched directory ([#526])
Previously they would get sent with `""` (empty string) or `"."` as the path
name.
- kqueue: don't emit spurious Create events for symbolic links ([#524])
The link would get resolved but kqueue would "forget" it already saw the link
itself, resulting on a Create for every Write event for the directory.
- all: return `ErrClosed` on `Add()` when the watcher is closed ([#516])
- other: add `Watcher.Errors` and `Watcher.Events` to the no-op `Watcher` in
`backend_other.go`, making it easier to use on unsupported platforms such as
WASM, AIX, etc. ([#528])
- other: use the `backend_other.go` no-op if the `appengine` build tag is set;
Google AppEngine forbids usage of the unsafe package so the inotify backend
won't compile there.
[#371]: https://github.com/fsnotify/fsnotify/pull/371
[#516]: https://github.com/fsnotify/fsnotify/pull/516
[#518]: https://github.com/fsnotify/fsnotify/pull/518
[#520]: https://github.com/fsnotify/fsnotify/pull/520
[#521]: https://github.com/fsnotify/fsnotify/pull/521
[#524]: https://github.com/fsnotify/fsnotify/pull/524
[#525]: https://github.com/fsnotify/fsnotify/pull/525
[#526]: https://github.com/fsnotify/fsnotify/pull/526
[#528]: https://github.com/fsnotify/fsnotify/pull/528
[#537]: https://github.com/fsnotify/fsnotify/pull/537
[#550]: https://github.com/fsnotify/fsnotify/pull/550
[#572]: https://github.com/fsnotify/fsnotify/pull/572
1.6.0 - 2022-10-13
------------------
This version of fsnotify needs Go 1.16 (this was already the case since 1.5.1, This version of fsnotify needs Go 1.16 (this was already the case since 1.5.1,
but not documented). It also increases the minimum Linux version to 2.6.32. but not documented). It also increases the minimum Linux version to 2.6.32.

View File

@ -1,29 +1,31 @@
fsnotify is a Go library to provide cross-platform filesystem notifications on fsnotify is a Go library to provide cross-platform filesystem notifications on
Windows, Linux, macOS, and BSD systems. Windows, Linux, macOS, BSD, and illumos.
Go 1.16 or newer is required; the full documentation is at Go 1.17 or newer is required; the full documentation is at
https://pkg.go.dev/github.com/fsnotify/fsnotify https://pkg.go.dev/github.com/fsnotify/fsnotify
**It's best to read the documentation at pkg.go.dev, as it's pinned to the last
released version, whereas this README is for the last development version which
may include additions/changes.**
--- ---
Platform support: Platform support:
| Adapter | OS | Status | | Backend | OS | Status |
| --------------------- | ---------------| -------------------------------------------------------------| | :-------------------- | :--------- | :------------------------------------------------------------------------ |
| inotify | Linux 2.6.32+ | Supported | | inotify | Linux | Supported |
| kqueue | BSD, macOS | Supported | | kqueue | BSD, macOS | Supported |
| ReadDirectoryChangesW | Windows | Supported | | ReadDirectoryChangesW | Windows | Supported |
| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) | | FEN | illumos | Supported |
| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) | | fanotify | Linux 5.9+ | [Not yet](https://github.com/fsnotify/fsnotify/issues/114) |
| fanotify | Linux 5.9+ | [Maybe](https://github.com/fsnotify/fsnotify/issues/114) | | AHAFS | AIX | [aix branch]; experimental due to lack of maintainer and test environment |
| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) | | FSEvents | macOS | [Needs support in x/sys/unix][fsevents] |
| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) | | USN Journals | Windows | [Needs support in x/sys/windows][usn] |
| Polling | *All* | [Not yet](https://github.com/fsnotify/fsnotify/issues/9) |
Linux and macOS should include Android and iOS, but these are currently untested. Linux and illumos should include Android and Solaris, but these are currently
untested.
[fsevents]: https://github.com/fsnotify/fsnotify/issues/11#issuecomment-1279133120
[usn]: https://github.com/fsnotify/fsnotify/issues/53#issuecomment-1279829847
[aix branch]: https://github.com/fsnotify/fsnotify/issues/353#issuecomment-1284590129
Usage Usage
----- -----
@ -83,20 +85,23 @@ run with:
% go run ./cmd/fsnotify % go run ./cmd/fsnotify
Further detailed documentation can be found in godoc:
https://pkg.go.dev/github.com/fsnotify/fsnotify
FAQ FAQ
--- ---
### Will a file still be watched when it's moved to another directory? ### Will a file still be watched when it's moved to another directory?
No, not unless you are watching the location it was moved to. No, not unless you are watching the location it was moved to.
### Are subdirectories watched too? ### Are subdirectories watched?
No, you must add watches for any directory you want to watch (a recursive No, you must add watches for any directory you want to watch (a recursive
watcher is on the roadmap: [#18]). watcher is on the roadmap: [#18]).
[#18]: https://github.com/fsnotify/fsnotify/issues/18 [#18]: https://github.com/fsnotify/fsnotify/issues/18
### Do I have to watch the Error and Event channels in a goroutine? ### Do I have to watch the Error and Event channels in a goroutine?
As of now, yes (you can read both channels in the same goroutine using `select`, Yes. You can read both channels in the same goroutine using `select` (you don't
you don't need a separate goroutine for both channels; see the example). need a separate goroutine for both channels; see the example).
### Why don't notifications work with NFS, SMB, FUSE, /proc, or /sys? ### Why don't notifications work with NFS, SMB, FUSE, /proc, or /sys?
fsnotify requires support from underlying OS to work. The current NFS and SMB fsnotify requires support from underlying OS to work. The current NFS and SMB
@ -107,6 +112,32 @@ This could be fixed with a polling watcher ([#9]), but it's not yet implemented.
[#9]: https://github.com/fsnotify/fsnotify/issues/9 [#9]: https://github.com/fsnotify/fsnotify/issues/9
### Why do I get many Chmod events?
Some programs may generate a lot of attribute changes; for example Spotlight on
macOS, anti-virus programs, backup applications, and some others are known to do
this. As a rule, it's typically best to ignore Chmod events. They're often not
useful, and tend to cause problems.
Spotlight indexing on macOS can result in multiple events (see [#15]). A
temporary workaround is to add your folder(s) to the *Spotlight Privacy
settings* until we have a native FSEvents implementation (see [#11]).
[#11]: https://github.com/fsnotify/fsnotify/issues/11
[#15]: https://github.com/fsnotify/fsnotify/issues/15
### Watching a file doesn't work well
Watching individual files (rather than directories) is generally not recommended
as many programs (especially editors) update files atomically: it will write to
a temporary file which is then moved to to destination, overwriting the original
(or some variant thereof). The watcher on the original file is now lost, as that
no longer exists.
The upshot of this is that a power failure or crash won't leave a half-written
file.
Watch the parent directory and use `Event.Name` to filter out files you're not
interested in. There is an example of this in `cmd/fsnotify/file.go`.
Platform-specific notes Platform-specific notes
----------------------- -----------------------
### Linux ### Linux
@ -151,11 +182,3 @@ these platforms.
The sysctl variables `kern.maxfiles` and `kern.maxfilesperproc` can be used to The sysctl variables `kern.maxfiles` and `kern.maxfilesperproc` can be used to
control the maximum number of open files. control the maximum number of open files.
### macOS
Spotlight indexing on macOS can result in multiple events (see [#15]). A temporary
workaround is to add your folder(s) to the *Spotlight Privacy settings* until we
have a native FSEvents implementation (see [#11]).
[#11]: https://github.com/fsnotify/fsnotify/issues/11
[#15]: https://github.com/fsnotify/fsnotify/issues/15

View File

@ -1,10 +1,19 @@
//go:build solaris //go:build solaris
// +build solaris // +build solaris
// Note: the documentation on the Watcher type and methods is generated from
// mkdoc.zsh
package fsnotify package fsnotify
import ( import (
"errors" "errors"
"fmt"
"os"
"path/filepath"
"sync"
"golang.org/x/sys/unix"
) )
// Watcher watches a set of paths, delivering events on a channel. // Watcher watches a set of paths, delivering events on a channel.
@ -58,14 +67,20 @@ import (
// control the maximum number of open files, as well as /etc/login.conf on BSD // control the maximum number of open files, as well as /etc/login.conf on BSD
// systems. // systems.
// //
// # macOS notes // # Windows notes
// //
// Spotlight indexing on macOS can result in multiple events (see [#15]). A // Paths can be added as "C:\path\to\dir", but forward slashes
// temporary workaround is to add your folder(s) to the "Spotlight Privacy // ("C:/path/to/dir") will also work.
// Settings" until we have a native FSEvents implementation (see [#11]).
// //
// [#11]: https://github.com/fsnotify/fsnotify/issues/11 // When a watched directory is removed it will always send an event for the
// [#15]: https://github.com/fsnotify/fsnotify/issues/15 // directory itself, but may not send events for all files in that directory.
// Sometimes it will send events for all times, sometimes it will send no
// events, and often only for some files.
//
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
// value that is guaranteed to work with SMB filesystems. If you have many
// events in quick succession this may not be enough, and you will have to use
// [WithBufferSize] to increase the value.
type Watcher struct { type Watcher struct {
// Events sends the filesystem change events. // Events sends the filesystem change events.
// //
@ -92,44 +107,129 @@ type Watcher struct {
// initiated by the user may show up as one or multiple // initiated by the user may show up as one or multiple
// writes, depending on when the system syncs things to // writes, depending on when the system syncs things to
// disk. For example when compiling a large Go program // disk. For example when compiling a large Go program
// you may get hundreds of Write events, so you // you may get hundreds of Write events, and you may
// probably want to wait until you've stopped receiving // want to wait until you've stopped receiving them
// them (see the dedup example in cmd/fsnotify). // (see the dedup example in cmd/fsnotify).
//
// Some systems may send Write event for directories
// when the directory content changes.
// //
// fsnotify.Chmod Attributes were changed. On Linux this is also sent // fsnotify.Chmod Attributes were changed. On Linux this is also sent
// when a file is removed (or more accurately, when a // when a file is removed (or more accurately, when a
// link to an inode is removed). On kqueue it's sent // link to an inode is removed). On kqueue it's sent
// and on kqueue when a file is truncated. On Windows // when a file is truncated. On Windows it's never
// it's never sent. // sent.
Events chan Event Events chan Event
// Errors sends any errors. // Errors sends any errors.
//
// ErrEventOverflow is used to indicate there are too many events:
//
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
// - kqueue, fen: Not used.
Errors chan error Errors chan error
mu sync.Mutex
port *unix.EventPort
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
dirs map[string]struct{} // Explicitly watched directories
watches map[string]struct{} // Explicitly watched non-directories
} }
// NewWatcher creates a new Watcher. // NewWatcher creates a new Watcher.
func NewWatcher() (*Watcher, error) { func NewWatcher() (*Watcher, error) {
return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") return NewBufferedWatcher(0)
} }
// Close removes all watches and closes the events channel. // NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
// channel.
//
// The main use case for this is situations with a very large number of events
// where the kernel buffer size can't be increased (e.g. due to lack of
// permissions). An unbuffered Watcher will perform better for almost all use
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
func NewBufferedWatcher(sz uint) (*Watcher, error) {
w := &Watcher{
Events: make(chan Event, sz),
Errors: make(chan error),
dirs: make(map[string]struct{}),
watches: make(map[string]struct{}),
done: make(chan struct{}),
}
var err error
w.port, err = unix.NewEventPort()
if err != nil {
return nil, fmt.Errorf("fsnotify.NewWatcher: %w", err)
}
go w.readEvents()
return w, nil
}
// sendEvent attempts to send an event to the user, returning true if the event
// was put in the channel successfully and false if the watcher has been closed.
func (w *Watcher) sendEvent(name string, op Op) (sent bool) {
select {
case w.Events <- Event{Name: name, Op: op}:
return true
case <-w.done:
return false
}
}
// sendError attempts to send an error to the user, returning true if the error
// was put in the channel successfully and false if the watcher has been closed.
func (w *Watcher) sendError(err error) (sent bool) {
select {
case w.Errors <- err:
return true
case <-w.done:
return false
}
}
func (w *Watcher) isClosed() bool {
select {
case <-w.done:
return true
default:
return false
}
}
// Close removes all watches and closes the Events channel.
func (w *Watcher) Close() error { func (w *Watcher) Close() error {
// Take the lock used by associateFile to prevent lingering events from
// being processed after the close
w.mu.Lock()
defer w.mu.Unlock()
if w.isClosed() {
return nil return nil
}
close(w.done)
return w.port.Close()
} }
// Add starts monitoring the path for changes. // Add starts monitoring the path for changes.
// //
// A path can only be watched once; attempting to watch it more than once will // A path can only be watched once; watching it more than once is a no-op and will
// return an error. Paths that do not yet exist on the filesystem cannot be // not return an error. Paths that do not yet exist on the filesystem cannot be
// added. A watch will be automatically removed if the path is deleted. // watched.
// //
// A path will remain watched if it gets renamed to somewhere else on the same // A watch will be automatically removed if the watched path is deleted or
// filesystem, but the monitor will get removed if the path gets deleted and // renamed. The exception is the Windows backend, which doesn't remove the
// re-created, or if it's moved to a different filesystem. // watcher on renames.
// //
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
// filesystems (/proc, /sys, etc.) generally don't work. // filesystems (/proc, /sys, etc.) generally don't work.
// //
// Returns [ErrClosed] if [Watcher.Close] was called.
//
// See [Watcher.AddWith] for a version that allows adding options.
//
// # Watching directories // # Watching directories
// //
// All files in a directory are monitored, including new files that are created // All files in a directory are monitored, including new files that are created
@ -139,15 +239,63 @@ func (w *Watcher) Close() error {
// # Watching files // # Watching files
// //
// Watching individual files (rather than directories) is generally not // Watching individual files (rather than directories) is generally not
// recommended as many tools update files atomically. Instead of "just" writing // recommended as many programs (especially editors) update files atomically: it
// to the file a temporary file will be written to first, and if successful the // will write to a temporary file which is then moved to to destination,
// temporary file is moved to to destination removing the original, or some // overwriting the original (or some variant thereof). The watcher on the
// variant thereof. The watcher on the original file is now lost, as it no // original file is now lost, as that no longer exists.
// longer exists.
// //
// Instead, watch the parent directory and use Event.Name to filter out files // The upshot of this is that a power failure or crash won't leave a
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. // half-written file.
func (w *Watcher) Add(name string) error { //
// Watch the parent directory and use Event.Name to filter out files you're not
// interested in. There is an example of this in cmd/fsnotify/file.go.
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
// the defaults described below are used.
//
// Possible options are:
//
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
// other platforms. The default is 64K (65536 bytes).
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
if w.isClosed() {
return ErrClosed
}
if w.port.PathIsWatched(name) {
return nil
}
_ = getOptions(opts...)
// Currently we resolve symlinks that were explicitly requested to be
// watched. Otherwise we would use LStat here.
stat, err := os.Stat(name)
if err != nil {
return err
}
// Associate all files in the directory.
if stat.IsDir() {
err := w.handleDirectory(name, stat, true, w.associateFile)
if err != nil {
return err
}
w.mu.Lock()
w.dirs[name] = struct{}{}
w.mu.Unlock()
return nil
}
err = w.associateFile(name, stat, true)
if err != nil {
return err
}
w.mu.Lock()
w.watches[name] = struct{}{}
w.mu.Unlock()
return nil return nil
} }
@ -157,6 +305,336 @@ func (w *Watcher) Add(name string) error {
// /tmp/dir and /tmp/dir/subdir then you will need to remove both. // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
// //
// Removing a path that has not yet been added returns [ErrNonExistentWatch]. // Removing a path that has not yet been added returns [ErrNonExistentWatch].
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) Remove(name string) error { func (w *Watcher) Remove(name string) error {
if w.isClosed() {
return nil
}
if !w.port.PathIsWatched(name) {
return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
}
// The user has expressed an intent. Immediately remove this name from
// whichever watch list it might be in. If it's not in there the delete
// doesn't cause harm.
w.mu.Lock()
delete(w.watches, name)
delete(w.dirs, name)
w.mu.Unlock()
stat, err := os.Stat(name)
if err != nil {
return err
}
// Remove associations for every file in the directory.
if stat.IsDir() {
err := w.handleDirectory(name, stat, false, w.dissociateFile)
if err != nil {
return err
}
return nil
}
err = w.port.DissociatePath(name)
if err != nil {
return err
}
return nil return nil
} }
// readEvents contains the main loop that runs in a goroutine watching for events.
func (w *Watcher) readEvents() {
// If this function returns, the watcher has been closed and we can close
// these channels
defer func() {
close(w.Errors)
close(w.Events)
}()
pevents := make([]unix.PortEvent, 8)
for {
count, err := w.port.Get(pevents, 1, nil)
if err != nil && err != unix.ETIME {
// Interrupted system call (count should be 0) ignore and continue
if errors.Is(err, unix.EINTR) && count == 0 {
continue
}
// Get failed because we called w.Close()
if errors.Is(err, unix.EBADF) && w.isClosed() {
return
}
// There was an error not caused by calling w.Close()
if !w.sendError(err) {
return
}
}
p := pevents[:count]
for _, pevent := range p {
if pevent.Source != unix.PORT_SOURCE_FILE {
// Event from unexpected source received; should never happen.
if !w.sendError(errors.New("Event from unexpected source received")) {
return
}
continue
}
err = w.handleEvent(&pevent)
if err != nil {
if !w.sendError(err) {
return
}
}
}
}
}
func (w *Watcher) handleDirectory(path string, stat os.FileInfo, follow bool, handler func(string, os.FileInfo, bool) error) error {
files, err := os.ReadDir(path)
if err != nil {
return err
}
// Handle all children of the directory.
for _, entry := range files {
finfo, err := entry.Info()
if err != nil {
return err
}
err = handler(filepath.Join(path, finfo.Name()), finfo, false)
if err != nil {
return err
}
}
// And finally handle the directory itself.
return handler(path, stat, follow)
}
// handleEvent might need to emit more than one fsnotify event if the events
// bitmap matches more than one event type (e.g. the file was both modified and
// had the attributes changed between when the association was created and the
// when event was returned)
func (w *Watcher) handleEvent(event *unix.PortEvent) error {
var (
events = event.Events
path = event.Path
fmode = event.Cookie.(os.FileMode)
reRegister = true
)
w.mu.Lock()
_, watchedDir := w.dirs[path]
_, watchedPath := w.watches[path]
w.mu.Unlock()
isWatched := watchedDir || watchedPath
if events&unix.FILE_DELETE != 0 {
if !w.sendEvent(path, Remove) {
return nil
}
reRegister = false
}
if events&unix.FILE_RENAME_FROM != 0 {
if !w.sendEvent(path, Rename) {
return nil
}
// Don't keep watching the new file name
reRegister = false
}
if events&unix.FILE_RENAME_TO != 0 {
// We don't report a Rename event for this case, because Rename events
// are interpreted as referring to the _old_ name of the file, and in
// this case the event would refer to the new name of the file. This
// type of rename event is not supported by fsnotify.
// inotify reports a Remove event in this case, so we simulate this
// here.
if !w.sendEvent(path, Remove) {
return nil
}
// Don't keep watching the file that was removed
reRegister = false
}
// The file is gone, nothing left to do.
if !reRegister {
if watchedDir {
w.mu.Lock()
delete(w.dirs, path)
w.mu.Unlock()
}
if watchedPath {
w.mu.Lock()
delete(w.watches, path)
w.mu.Unlock()
}
return nil
}
// If we didn't get a deletion the file still exists and we're going to have
// to watch it again. Let's Stat it now so that we can compare permissions
// and have what we need to continue watching the file
stat, err := os.Lstat(path)
if err != nil {
// This is unexpected, but we should still emit an event. This happens
// most often on "rm -r" of a subdirectory inside a watched directory We
// get a modify event of something happening inside, but by the time we
// get here, the sudirectory is already gone. Clearly we were watching
// this path but now it is gone. Let's tell the user that it was
// removed.
if !w.sendEvent(path, Remove) {
return nil
}
// Suppress extra write events on removed directories; they are not
// informative and can be confusing.
return nil
}
// resolve symlinks that were explicitly watched as we would have at Add()
// time. this helps suppress spurious Chmod events on watched symlinks
if isWatched {
stat, err = os.Stat(path)
if err != nil {
// The symlink still exists, but the target is gone. Report the
// Remove similar to above.
if !w.sendEvent(path, Remove) {
return nil
}
// Don't return the error
}
}
if events&unix.FILE_MODIFIED != 0 {
if fmode.IsDir() {
if watchedDir {
if err := w.updateDirectory(path); err != nil {
return err
}
} else {
if !w.sendEvent(path, Write) {
return nil
}
}
} else {
if !w.sendEvent(path, Write) {
return nil
}
}
}
if events&unix.FILE_ATTRIB != 0 && stat != nil {
// Only send Chmod if perms changed
if stat.Mode().Perm() != fmode.Perm() {
if !w.sendEvent(path, Chmod) {
return nil
}
}
}
if stat != nil {
// If we get here, it means we've hit an event above that requires us to
// continue watching the file or directory
return w.associateFile(path, stat, isWatched)
}
return nil
}
func (w *Watcher) updateDirectory(path string) error {
// The directory was modified, so we must find unwatched entities and watch
// them. If something was removed from the directory, nothing will happen,
// as everything else should still be watched.
files, err := os.ReadDir(path)
if err != nil {
return err
}
for _, entry := range files {
path := filepath.Join(path, entry.Name())
if w.port.PathIsWatched(path) {
continue
}
finfo, err := entry.Info()
if err != nil {
return err
}
err = w.associateFile(path, finfo, false)
if err != nil {
if !w.sendError(err) {
return nil
}
}
if !w.sendEvent(path, Create) {
return nil
}
}
return nil
}
func (w *Watcher) associateFile(path string, stat os.FileInfo, follow bool) error {
if w.isClosed() {
return ErrClosed
}
// This is primarily protecting the call to AssociatePath but it is
// important and intentional that the call to PathIsWatched is also
// protected by this mutex. Without this mutex, AssociatePath has been seen
// to error out that the path is already associated.
w.mu.Lock()
defer w.mu.Unlock()
if w.port.PathIsWatched(path) {
// Remove the old association in favor of this one If we get ENOENT,
// then while the x/sys/unix wrapper still thought that this path was
// associated, the underlying event port did not. This call will have
// cleared up that discrepancy. The most likely cause is that the event
// has fired but we haven't processed it yet.
err := w.port.DissociatePath(path)
if err != nil && err != unix.ENOENT {
return err
}
}
// FILE_NOFOLLOW means we watch symlinks themselves rather than their
// targets.
events := unix.FILE_MODIFIED | unix.FILE_ATTRIB | unix.FILE_NOFOLLOW
if follow {
// We *DO* follow symlinks for explicitly watched entries.
events = unix.FILE_MODIFIED | unix.FILE_ATTRIB
}
return w.port.AssociatePath(path, stat,
events,
stat.Mode())
}
func (w *Watcher) dissociateFile(path string, stat os.FileInfo, unused bool) error {
if !w.port.PathIsWatched(path) {
return nil
}
return w.port.DissociatePath(path)
}
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) WatchList() []string {
if w.isClosed() {
return nil
}
w.mu.Lock()
defer w.mu.Unlock()
entries := make([]string, 0, len(w.watches)+len(w.dirs))
for pathname := range w.dirs {
entries = append(entries, pathname)
}
for pathname := range w.watches {
entries = append(entries, pathname)
}
return entries
}

View File

@ -1,5 +1,8 @@
//go:build linux //go:build linux && !appengine
// +build linux // +build linux,!appengine
// Note: the documentation on the Watcher type and methods is generated from
// mkdoc.zsh
package fsnotify package fsnotify
@ -67,14 +70,20 @@ import (
// control the maximum number of open files, as well as /etc/login.conf on BSD // control the maximum number of open files, as well as /etc/login.conf on BSD
// systems. // systems.
// //
// # macOS notes // # Windows notes
// //
// Spotlight indexing on macOS can result in multiple events (see [#15]). A // Paths can be added as "C:\path\to\dir", but forward slashes
// temporary workaround is to add your folder(s) to the "Spotlight Privacy // ("C:/path/to/dir") will also work.
// Settings" until we have a native FSEvents implementation (see [#11]).
// //
// [#11]: https://github.com/fsnotify/fsnotify/issues/11 // When a watched directory is removed it will always send an event for the
// [#15]: https://github.com/fsnotify/fsnotify/issues/15 // directory itself, but may not send events for all files in that directory.
// Sometimes it will send events for all times, sometimes it will send no
// events, and often only for some files.
//
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
// value that is guaranteed to work with SMB filesystems. If you have many
// events in quick succession this may not be enough, and you will have to use
// [WithBufferSize] to increase the value.
type Watcher struct { type Watcher struct {
// Events sends the filesystem change events. // Events sends the filesystem change events.
// //
@ -101,36 +110,148 @@ type Watcher struct {
// initiated by the user may show up as one or multiple // initiated by the user may show up as one or multiple
// writes, depending on when the system syncs things to // writes, depending on when the system syncs things to
// disk. For example when compiling a large Go program // disk. For example when compiling a large Go program
// you may get hundreds of Write events, so you // you may get hundreds of Write events, and you may
// probably want to wait until you've stopped receiving // want to wait until you've stopped receiving them
// them (see the dedup example in cmd/fsnotify). // (see the dedup example in cmd/fsnotify).
//
// Some systems may send Write event for directories
// when the directory content changes.
// //
// fsnotify.Chmod Attributes were changed. On Linux this is also sent // fsnotify.Chmod Attributes were changed. On Linux this is also sent
// when a file is removed (or more accurately, when a // when a file is removed (or more accurately, when a
// link to an inode is removed). On kqueue it's sent // link to an inode is removed). On kqueue it's sent
// and on kqueue when a file is truncated. On Windows // when a file is truncated. On Windows it's never
// it's never sent. // sent.
Events chan Event Events chan Event
// Errors sends any errors. // Errors sends any errors.
//
// ErrEventOverflow is used to indicate there are too many events:
//
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
// - kqueue, fen: Not used.
Errors chan error Errors chan error
// Store fd here as os.File.Read() will no longer return on close after // Store fd here as os.File.Read() will no longer return on close after
// calling Fd(). See: https://github.com/golang/go/issues/26439 // calling Fd(). See: https://github.com/golang/go/issues/26439
fd int fd int
mu sync.Mutex // Map access
inotifyFile *os.File inotifyFile *os.File
watches map[string]*watch // Map of inotify watches (key: path) watches *watches
paths map[int]string // Map of watched paths (key: watch descriptor)
done chan struct{} // Channel for sending a "quit message" to the reader goroutine done chan struct{} // Channel for sending a "quit message" to the reader goroutine
closeMu sync.Mutex
doneResp chan struct{} // Channel to respond to Close doneResp chan struct{} // Channel to respond to Close
} }
type (
watches struct {
mu sync.RWMutex
wd map[uint32]*watch // wd → watch
path map[string]uint32 // pathname → wd
}
watch struct {
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
path string // Watch path.
}
)
func newWatches() *watches {
return &watches{
wd: make(map[uint32]*watch),
path: make(map[string]uint32),
}
}
func (w *watches) len() int {
w.mu.RLock()
defer w.mu.RUnlock()
return len(w.wd)
}
func (w *watches) add(ww *watch) {
w.mu.Lock()
defer w.mu.Unlock()
w.wd[ww.wd] = ww
w.path[ww.path] = ww.wd
}
func (w *watches) remove(wd uint32) {
w.mu.Lock()
defer w.mu.Unlock()
delete(w.path, w.wd[wd].path)
delete(w.wd, wd)
}
func (w *watches) removePath(path string) (uint32, bool) {
w.mu.Lock()
defer w.mu.Unlock()
wd, ok := w.path[path]
if !ok {
return 0, false
}
delete(w.path, path)
delete(w.wd, wd)
return wd, true
}
func (w *watches) byPath(path string) *watch {
w.mu.RLock()
defer w.mu.RUnlock()
return w.wd[w.path[path]]
}
func (w *watches) byWd(wd uint32) *watch {
w.mu.RLock()
defer w.mu.RUnlock()
return w.wd[wd]
}
func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error {
w.mu.Lock()
defer w.mu.Unlock()
var existing *watch
wd, ok := w.path[path]
if ok {
existing = w.wd[wd]
}
upd, err := f(existing)
if err != nil {
return err
}
if upd != nil {
w.wd[upd.wd] = upd
w.path[upd.path] = upd.wd
if upd.wd != wd {
delete(w.wd, wd)
}
}
return nil
}
// NewWatcher creates a new Watcher. // NewWatcher creates a new Watcher.
func NewWatcher() (*Watcher, error) { func NewWatcher() (*Watcher, error) {
// Create inotify fd return NewBufferedWatcher(0)
// Need to set the FD to nonblocking mode in order for SetDeadline methods to work }
// Otherwise, blocking i/o operations won't terminate on close
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
// channel.
//
// The main use case for this is situations with a very large number of events
// where the kernel buffer size can't be increased (e.g. due to lack of
// permissions). An unbuffered Watcher will perform better for almost all use
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
func NewBufferedWatcher(sz uint) (*Watcher, error) {
// Need to set nonblocking mode for SetDeadline to work, otherwise blocking
// I/O operations won't terminate on close.
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
if fd == -1 { if fd == -1 {
return nil, errno return nil, errno
@ -139,9 +260,8 @@ func NewWatcher() (*Watcher, error) {
w := &Watcher{ w := &Watcher{
fd: fd, fd: fd,
inotifyFile: os.NewFile(uintptr(fd), ""), inotifyFile: os.NewFile(uintptr(fd), ""),
watches: make(map[string]*watch), watches: newWatches(),
paths: make(map[int]string), Events: make(chan Event, sz),
Events: make(chan Event),
Errors: make(chan error), Errors: make(chan error),
done: make(chan struct{}), done: make(chan struct{}),
doneResp: make(chan struct{}), doneResp: make(chan struct{}),
@ -157,8 +277,8 @@ func (w *Watcher) sendEvent(e Event) bool {
case w.Events <- e: case w.Events <- e:
return true return true
case <-w.done: case <-w.done:
}
return false return false
}
} }
// Returns true if the error was sent, or false if watcher is closed. // Returns true if the error was sent, or false if watcher is closed.
@ -180,17 +300,15 @@ func (w *Watcher) isClosed() bool {
} }
} }
// Close removes all watches and closes the events channel. // Close removes all watches and closes the Events channel.
func (w *Watcher) Close() error { func (w *Watcher) Close() error {
w.mu.Lock() w.closeMu.Lock()
if w.isClosed() { if w.isClosed() {
w.mu.Unlock() w.closeMu.Unlock()
return nil return nil
} }
// Send 'close' signal to goroutine, and set the Watcher to closed.
close(w.done) close(w.done)
w.mu.Unlock() w.closeMu.Unlock()
// Causes any blocking reads to return with an error, provided the file // Causes any blocking reads to return with an error, provided the file
// still supports deadline operations. // still supports deadline operations.
@ -207,17 +325,21 @@ func (w *Watcher) Close() error {
// Add starts monitoring the path for changes. // Add starts monitoring the path for changes.
// //
// A path can only be watched once; attempting to watch it more than once will // A path can only be watched once; watching it more than once is a no-op and will
// return an error. Paths that do not yet exist on the filesystem cannot be // not return an error. Paths that do not yet exist on the filesystem cannot be
// added. A watch will be automatically removed if the path is deleted. // watched.
// //
// A path will remain watched if it gets renamed to somewhere else on the same // A watch will be automatically removed if the watched path is deleted or
// filesystem, but the monitor will get removed if the path gets deleted and // renamed. The exception is the Windows backend, which doesn't remove the
// re-created, or if it's moved to a different filesystem. // watcher on renames.
// //
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
// filesystems (/proc, /sys, etc.) generally don't work. // filesystems (/proc, /sys, etc.) generally don't work.
// //
// Returns [ErrClosed] if [Watcher.Close] was called.
//
// See [Watcher.AddWith] for a version that allows adding options.
//
// # Watching directories // # Watching directories
// //
// All files in a directory are monitored, including new files that are created // All files in a directory are monitored, including new files that are created
@ -227,44 +349,59 @@ func (w *Watcher) Close() error {
// # Watching files // # Watching files
// //
// Watching individual files (rather than directories) is generally not // Watching individual files (rather than directories) is generally not
// recommended as many tools update files atomically. Instead of "just" writing // recommended as many programs (especially editors) update files atomically: it
// to the file a temporary file will be written to first, and if successful the // will write to a temporary file which is then moved to to destination,
// temporary file is moved to to destination removing the original, or some // overwriting the original (or some variant thereof). The watcher on the
// variant thereof. The watcher on the original file is now lost, as it no // original file is now lost, as that no longer exists.
// longer exists.
// //
// Instead, watch the parent directory and use Event.Name to filter out files // The upshot of this is that a power failure or crash won't leave a
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. // half-written file.
func (w *Watcher) Add(name string) error { //
name = filepath.Clean(name) // Watch the parent directory and use Event.Name to filter out files you're not
// interested in. There is an example of this in cmd/fsnotify/file.go.
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
// the defaults described below are used.
//
// Possible options are:
//
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
// other platforms. The default is 64K (65536 bytes).
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
if w.isClosed() { if w.isClosed() {
return errors.New("inotify instance already closed") return ErrClosed
} }
name = filepath.Clean(name)
_ = getOptions(opts...)
var flags uint32 = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | var flags uint32 = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
w.mu.Lock() return w.watches.updatePath(name, func(existing *watch) (*watch, error) {
defer w.mu.Unlock() if existing != nil {
watchEntry := w.watches[name] flags |= existing.flags | unix.IN_MASK_ADD
if watchEntry != nil {
flags |= watchEntry.flags | unix.IN_MASK_ADD
} }
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
wd, err := unix.InotifyAddWatch(w.fd, name, flags)
if wd == -1 { if wd == -1 {
return errno return nil, err
} }
if watchEntry == nil { if existing == nil {
w.watches[name] = &watch{wd: uint32(wd), flags: flags} return &watch{
w.paths[wd] = name wd: uint32(wd),
} else { path: name,
watchEntry.wd = uint32(wd) flags: flags,
watchEntry.flags = flags }, nil
} }
return nil existing.wd = uint32(wd)
existing.flags = flags
return existing, nil
})
} }
// Remove stops monitoring the path for changes. // Remove stops monitoring the path for changes.
@ -273,32 +410,22 @@ func (w *Watcher) Add(name string) error {
// /tmp/dir and /tmp/dir/subdir then you will need to remove both. // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
// //
// Removing a path that has not yet been added returns [ErrNonExistentWatch]. // Removing a path that has not yet been added returns [ErrNonExistentWatch].
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) Remove(name string) error { func (w *Watcher) Remove(name string) error {
name = filepath.Clean(name) if w.isClosed() {
return nil
}
return w.remove(filepath.Clean(name))
}
// Fetch the watch. func (w *Watcher) remove(name string) error {
w.mu.Lock() wd, ok := w.watches.removePath(name)
defer w.mu.Unlock()
watch, ok := w.watches[name]
// Remove it from inotify.
if !ok { if !ok {
return fmt.Errorf("%w: %s", ErrNonExistentWatch, name) return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
} }
// We successfully removed the watch if InotifyRmWatch doesn't return an success, errno := unix.InotifyRmWatch(w.fd, wd)
// error, we need to clean up our internal state to ensure it matches
// inotify's kernel state.
delete(w.paths, int(watch.wd))
delete(w.watches, name)
// inotify_rm_watch will return EINVAL if the file has been deleted;
// the inotify will already have been removed.
// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
// so that EINVAL means that the wd is being rm_watch()ed or its file removed
// by another thread and we have not received IN_IGNORE event.
success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
if success == -1 { if success == -1 {
// TODO: Perhaps it's not helpful to return an error here in every case; // TODO: Perhaps it's not helpful to return an error here in every case;
// The only two possible errors are: // The only two possible errors are:
@ -312,26 +439,26 @@ func (w *Watcher) Remove(name string) error {
// are watching is deleted. // are watching is deleted.
return errno return errno
} }
return nil return nil
} }
// WatchList returns all paths added with [Add] (and are not yet removed). // WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) WatchList() []string { func (w *Watcher) WatchList() []string {
w.mu.Lock() if w.isClosed() {
defer w.mu.Unlock() return nil
entries := make([]string, 0, len(w.watches))
for pathname := range w.watches {
entries = append(entries, pathname)
} }
return entries entries := make([]string, 0, w.watches.len())
} w.watches.mu.RLock()
for pathname := range w.watches.path {
entries = append(entries, pathname)
}
w.watches.mu.RUnlock()
type watch struct { return entries
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
} }
// readEvents reads from the inotify file descriptor, converts the // readEvents reads from the inotify file descriptor, converts the
@ -367,14 +494,11 @@ func (w *Watcher) readEvents() {
if n < unix.SizeofInotifyEvent { if n < unix.SizeofInotifyEvent {
var err error var err error
if n == 0 { if n == 0 {
// If EOF is received. This should really never happen. err = io.EOF // If EOF is received. This should really never happen.
err = io.EOF
} else if n < 0 { } else if n < 0 {
// If an error occurred while reading. err = errno // If an error occurred while reading.
err = errno
} else { } else {
// Read was too short. err = errors.New("notify: short read in readEvents()") // Read was too short.
err = errors.New("notify: short read in readEvents()")
} }
if !w.sendError(err) { if !w.sendError(err) {
return return
@ -403,18 +527,29 @@ func (w *Watcher) readEvents() {
// doesn't append the filename to the event, but we would like to always fill the // doesn't append the filename to the event, but we would like to always fill the
// the "Name" field with a valid filename. We retrieve the path of the watch from // the "Name" field with a valid filename. We retrieve the path of the watch from
// the "paths" map. // the "paths" map.
w.mu.Lock() watch := w.watches.byWd(uint32(raw.Wd))
name, ok := w.paths[int(raw.Wd)]
// IN_DELETE_SELF occurs when the file/directory being watched is removed.
// This is a sign to clean up the maps, otherwise we are no longer in sync
// with the inotify kernel state which has already deleted the watch
// automatically.
if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
delete(w.paths, int(raw.Wd))
delete(w.watches, name)
}
w.mu.Unlock()
// inotify will automatically remove the watch on deletes; just need
// to clean our state here.
if watch != nil && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
w.watches.remove(watch.wd)
}
// We can't really update the state when a watched path is moved;
// only IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove
// the watch.
if watch != nil && mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
err := w.remove(watch.path)
if err != nil && !errors.Is(err, ErrNonExistentWatch) {
if !w.sendError(err) {
return
}
}
}
var name string
if watch != nil {
name = watch.path
}
if nameLen > 0 { if nameLen > 0 {
// Point "bytes" at the first byte of the filename // Point "bytes" at the first byte of the filename
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen] bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]

View File

@ -1,12 +1,14 @@
//go:build freebsd || openbsd || netbsd || dragonfly || darwin //go:build freebsd || openbsd || netbsd || dragonfly || darwin
// +build freebsd openbsd netbsd dragonfly darwin // +build freebsd openbsd netbsd dragonfly darwin
// Note: the documentation on the Watcher type and methods is generated from
// mkdoc.zsh
package fsnotify package fsnotify
import ( import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sync" "sync"
@ -65,14 +67,20 @@ import (
// control the maximum number of open files, as well as /etc/login.conf on BSD // control the maximum number of open files, as well as /etc/login.conf on BSD
// systems. // systems.
// //
// # macOS notes // # Windows notes
// //
// Spotlight indexing on macOS can result in multiple events (see [#15]). A // Paths can be added as "C:\path\to\dir", but forward slashes
// temporary workaround is to add your folder(s) to the "Spotlight Privacy // ("C:/path/to/dir") will also work.
// Settings" until we have a native FSEvents implementation (see [#11]).
// //
// [#11]: https://github.com/fsnotify/fsnotify/issues/11 // When a watched directory is removed it will always send an event for the
// [#15]: https://github.com/fsnotify/fsnotify/issues/15 // directory itself, but may not send events for all files in that directory.
// Sometimes it will send events for all times, sometimes it will send no
// events, and often only for some files.
//
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
// value that is guaranteed to work with SMB filesystems. If you have many
// events in quick succession this may not be enough, and you will have to use
// [WithBufferSize] to increase the value.
type Watcher struct { type Watcher struct {
// Events sends the filesystem change events. // Events sends the filesystem change events.
// //
@ -99,18 +107,27 @@ type Watcher struct {
// initiated by the user may show up as one or multiple // initiated by the user may show up as one or multiple
// writes, depending on when the system syncs things to // writes, depending on when the system syncs things to
// disk. For example when compiling a large Go program // disk. For example when compiling a large Go program
// you may get hundreds of Write events, so you // you may get hundreds of Write events, and you may
// probably want to wait until you've stopped receiving // want to wait until you've stopped receiving them
// them (see the dedup example in cmd/fsnotify). // (see the dedup example in cmd/fsnotify).
//
// Some systems may send Write event for directories
// when the directory content changes.
// //
// fsnotify.Chmod Attributes were changed. On Linux this is also sent // fsnotify.Chmod Attributes were changed. On Linux this is also sent
// when a file is removed (or more accurately, when a // when a file is removed (or more accurately, when a
// link to an inode is removed). On kqueue it's sent // link to an inode is removed). On kqueue it's sent
// and on kqueue when a file is truncated. On Windows // when a file is truncated. On Windows it's never
// it's never sent. // sent.
Events chan Event Events chan Event
// Errors sends any errors. // Errors sends any errors.
//
// ErrEventOverflow is used to indicate there are too many events:
//
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
// - kqueue, fen: Not used.
Errors chan error Errors chan error
done chan struct{} done chan struct{}
@ -133,6 +150,18 @@ type pathInfo struct {
// NewWatcher creates a new Watcher. // NewWatcher creates a new Watcher.
func NewWatcher() (*Watcher, error) { func NewWatcher() (*Watcher, error) {
return NewBufferedWatcher(0)
}
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
// channel.
//
// The main use case for this is situations with a very large number of events
// where the kernel buffer size can't be increased (e.g. due to lack of
// permissions). An unbuffered Watcher will perform better for almost all use
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
func NewBufferedWatcher(sz uint) (*Watcher, error) {
kq, closepipe, err := newKqueue() kq, closepipe, err := newKqueue()
if err != nil { if err != nil {
return nil, err return nil, err
@ -147,7 +176,7 @@ func NewWatcher() (*Watcher, error) {
paths: make(map[int]pathInfo), paths: make(map[int]pathInfo),
fileExists: make(map[string]struct{}), fileExists: make(map[string]struct{}),
userWatches: make(map[string]struct{}), userWatches: make(map[string]struct{}),
Events: make(chan Event), Events: make(chan Event, sz),
Errors: make(chan error), Errors: make(chan error),
done: make(chan struct{}), done: make(chan struct{}),
} }
@ -197,8 +226,8 @@ func (w *Watcher) sendEvent(e Event) bool {
case w.Events <- e: case w.Events <- e:
return true return true
case <-w.done: case <-w.done:
}
return false return false
}
} }
// Returns true if the error was sent, or false if watcher is closed. // Returns true if the error was sent, or false if watcher is closed.
@ -207,11 +236,11 @@ func (w *Watcher) sendError(err error) bool {
case w.Errors <- err: case w.Errors <- err:
return true return true
case <-w.done: case <-w.done:
}
return false return false
}
} }
// Close removes all watches and closes the events channel. // Close removes all watches and closes the Events channel.
func (w *Watcher) Close() error { func (w *Watcher) Close() error {
w.mu.Lock() w.mu.Lock()
if w.isClosed { if w.isClosed {
@ -239,17 +268,21 @@ func (w *Watcher) Close() error {
// Add starts monitoring the path for changes. // Add starts monitoring the path for changes.
// //
// A path can only be watched once; attempting to watch it more than once will // A path can only be watched once; watching it more than once is a no-op and will
// return an error. Paths that do not yet exist on the filesystem cannot be // not return an error. Paths that do not yet exist on the filesystem cannot be
// added. A watch will be automatically removed if the path is deleted. // watched.
// //
// A path will remain watched if it gets renamed to somewhere else on the same // A watch will be automatically removed if the watched path is deleted or
// filesystem, but the monitor will get removed if the path gets deleted and // renamed. The exception is the Windows backend, which doesn't remove the
// re-created, or if it's moved to a different filesystem. // watcher on renames.
// //
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
// filesystems (/proc, /sys, etc.) generally don't work. // filesystems (/proc, /sys, etc.) generally don't work.
// //
// Returns [ErrClosed] if [Watcher.Close] was called.
//
// See [Watcher.AddWith] for a version that allows adding options.
//
// # Watching directories // # Watching directories
// //
// All files in a directory are monitored, including new files that are created // All files in a directory are monitored, including new files that are created
@ -259,15 +292,28 @@ func (w *Watcher) Close() error {
// # Watching files // # Watching files
// //
// Watching individual files (rather than directories) is generally not // Watching individual files (rather than directories) is generally not
// recommended as many tools update files atomically. Instead of "just" writing // recommended as many programs (especially editors) update files atomically: it
// to the file a temporary file will be written to first, and if successful the // will write to a temporary file which is then moved to to destination,
// temporary file is moved to to destination removing the original, or some // overwriting the original (or some variant thereof). The watcher on the
// variant thereof. The watcher on the original file is now lost, as it no // original file is now lost, as that no longer exists.
// longer exists.
// //
// Instead, watch the parent directory and use Event.Name to filter out files // The upshot of this is that a power failure or crash won't leave a
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. // half-written file.
func (w *Watcher) Add(name string) error { //
// Watch the parent directory and use Event.Name to filter out files you're not
// interested in. There is an example of this in cmd/fsnotify/file.go.
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
// the defaults described below are used.
//
// Possible options are:
//
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
// other platforms. The default is 64K (65536 bytes).
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
_ = getOptions(opts...)
w.mu.Lock() w.mu.Lock()
w.userWatches[name] = struct{}{} w.userWatches[name] = struct{}{}
w.mu.Unlock() w.mu.Unlock()
@ -281,9 +327,19 @@ func (w *Watcher) Add(name string) error {
// /tmp/dir and /tmp/dir/subdir then you will need to remove both. // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
// //
// Removing a path that has not yet been added returns [ErrNonExistentWatch]. // Removing a path that has not yet been added returns [ErrNonExistentWatch].
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) Remove(name string) error { func (w *Watcher) Remove(name string) error {
return w.remove(name, true)
}
func (w *Watcher) remove(name string, unwatchFiles bool) error {
name = filepath.Clean(name) name = filepath.Clean(name)
w.mu.Lock() w.mu.Lock()
if w.isClosed {
w.mu.Unlock()
return nil
}
watchfd, ok := w.watches[name] watchfd, ok := w.watches[name]
w.mu.Unlock() w.mu.Unlock()
if !ok { if !ok {
@ -315,7 +371,7 @@ func (w *Watcher) Remove(name string) error {
w.mu.Unlock() w.mu.Unlock()
// Find all watched paths that are in this directory that are not external. // Find all watched paths that are in this directory that are not external.
if isDir { if unwatchFiles && isDir {
var pathsToRemove []string var pathsToRemove []string
w.mu.Lock() w.mu.Lock()
for fd := range w.watchesByDir[name] { for fd := range w.watchesByDir[name] {
@ -326,20 +382,25 @@ func (w *Watcher) Remove(name string) error {
} }
w.mu.Unlock() w.mu.Unlock()
for _, name := range pathsToRemove { for _, name := range pathsToRemove {
// Since these are internal, not much sense in propagating error // Since these are internal, not much sense in propagating error to
// to the user, as that will just confuse them with an error about // the user, as that will just confuse them with an error about a
// a path they did not explicitly watch themselves. // path they did not explicitly watch themselves.
w.Remove(name) w.Remove(name)
} }
} }
return nil return nil
} }
// WatchList returns all paths added with [Add] (and are not yet removed). // WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) WatchList() []string { func (w *Watcher) WatchList() []string {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
if w.isClosed {
return nil
}
entries := make([]string, 0, len(w.userWatches)) entries := make([]string, 0, len(w.userWatches))
for pathname := range w.userWatches { for pathname := range w.userWatches {
@ -352,18 +413,18 @@ func (w *Watcher) WatchList() []string {
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
// addWatch adds name to the watched file set. // addWatch adds name to the watched file set; the flags are interpreted as
// The flags are interpreted as described in kevent(2). // described in kevent(2).
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks. //
// Returns the real path to the file which was added, with symlinks resolved.
func (w *Watcher) addWatch(name string, flags uint32) (string, error) { func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
var isDir bool var isDir bool
// Make ./name and name equivalent
name = filepath.Clean(name) name = filepath.Clean(name)
w.mu.Lock() w.mu.Lock()
if w.isClosed { if w.isClosed {
w.mu.Unlock() w.mu.Unlock()
return "", errors.New("kevent instance already closed") return "", ErrClosed
} }
watchfd, alreadyWatching := w.watches[name] watchfd, alreadyWatching := w.watches[name]
// We already have a watch, but we can still override flags. // We already have a watch, but we can still override flags.
@ -383,27 +444,30 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
return "", nil return "", nil
} }
// Follow Symlinks // Follow Symlinks.
//
// Linux can add unresolvable symlinks to the watch list without issue,
// and Windows can't do symlinks period. To maintain consistency, we
// will act like everything is fine if the link can't be resolved.
// There will simply be no file events for broken symlinks. Hence the
// returns of nil on errors.
if fi.Mode()&os.ModeSymlink == os.ModeSymlink { if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
name, err = filepath.EvalSymlinks(name) link, err := os.Readlink(name)
if err != nil { if err != nil {
// Return nil because Linux can add unresolvable symlinks to the
// watch list without problems, so maintain consistency with
// that. There will be no file events for broken symlinks.
// TODO: more specific check; returns os.PathError; ENOENT?
return "", nil return "", nil
} }
w.mu.Lock() w.mu.Lock()
_, alreadyWatching = w.watches[name] _, alreadyWatching = w.watches[link]
w.mu.Unlock() w.mu.Unlock()
if alreadyWatching { if alreadyWatching {
return name, nil // Add to watches so we don't get spurious Create events later
// on when we diff the directories.
w.watches[name] = 0
w.fileExists[name] = struct{}{}
return link, nil
} }
name = link
fi, err = os.Lstat(name) fi, err = os.Lstat(name)
if err != nil { if err != nil {
return "", nil return "", nil
@ -411,7 +475,7 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
} }
// Retry on EINTR; open() can return EINTR in practice on macOS. // Retry on EINTR; open() can return EINTR in practice on macOS.
// See #354, and go issues 11180 and 39237. // See #354, and Go issues 11180 and 39237.
for { for {
watchfd, err = unix.Open(name, openMode, 0) watchfd, err = unix.Open(name, openMode, 0)
if err == nil { if err == nil {
@ -444,14 +508,13 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
w.watchesByDir[parentName] = watchesByDir w.watchesByDir[parentName] = watchesByDir
} }
watchesByDir[watchfd] = struct{}{} watchesByDir[watchfd] = struct{}{}
w.paths[watchfd] = pathInfo{name: name, isDir: isDir} w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
w.mu.Unlock() w.mu.Unlock()
} }
if isDir { if isDir {
// Watch the directory if it has not been watched before, // Watch the directory if it has not been watched before, or if it was
// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) // watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
w.mu.Lock() w.mu.Lock()
watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
@ -473,13 +536,10 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
// Event values that it sends down the Events channel. // Event values that it sends down the Events channel.
func (w *Watcher) readEvents() { func (w *Watcher) readEvents() {
defer func() { defer func() {
err := unix.Close(w.kq)
if err != nil {
w.Errors <- err
}
unix.Close(w.closepipe[0])
close(w.Events) close(w.Events)
close(w.Errors) close(w.Errors)
_ = unix.Close(w.kq)
unix.Close(w.closepipe[0])
}() }()
eventBuffer := make([]unix.Kevent_t, 10) eventBuffer := make([]unix.Kevent_t, 10)
@ -513,18 +573,8 @@ func (w *Watcher) readEvents() {
event := w.newEvent(path.name, mask) event := w.newEvent(path.name, mask)
if path.isDir && !event.Has(Remove) {
// Double check to make sure the directory exists. This can
// happen when we do a rm -fr on a recursively watched folders
// and we receive a modification event first but the folder has
// been deleted and later receive the delete event.
if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
event.Op |= Remove
}
}
if event.Has(Rename) || event.Has(Remove) { if event.Has(Rename) || event.Has(Remove) {
w.Remove(event.Name) w.remove(event.Name, false)
w.mu.Lock() w.mu.Lock()
delete(w.fileExists, event.Name) delete(w.fileExists, event.Name)
w.mu.Unlock() w.mu.Unlock()
@ -540,26 +590,30 @@ func (w *Watcher) readEvents() {
} }
if event.Has(Remove) { if event.Has(Remove) {
// Look for a file that may have overwritten this. // Look for a file that may have overwritten this; for example,
// For example, mv f1 f2 will delete f2, then create f2. // mv f1 f2 will delete f2, then create f2.
if path.isDir { if path.isDir {
fileDir := filepath.Clean(event.Name) fileDir := filepath.Clean(event.Name)
w.mu.Lock() w.mu.Lock()
_, found := w.watches[fileDir] _, found := w.watches[fileDir]
w.mu.Unlock() w.mu.Unlock()
if found { if found {
// make sure the directory exists before we watch for changes. When we err := w.sendDirectoryChangeEvents(fileDir)
// do a recursive watch and perform rm -fr, the parent directory might if err != nil {
// have gone missing, ignore the missing directory and let the if !w.sendError(err) {
// upcoming delete event remove the watch from the parent directory. closed = true
if _, err := os.Lstat(fileDir); err == nil { }
w.sendDirectoryChangeEvents(fileDir)
} }
} }
} else { } else {
filePath := filepath.Clean(event.Name) filePath := filepath.Clean(event.Name)
if fileInfo, err := os.Lstat(filePath); err == nil { if fi, err := os.Lstat(filePath); err == nil {
w.sendFileCreatedEventIfNew(filePath, fileInfo) err := w.sendFileCreatedEventIfNew(filePath, fi)
if err != nil {
if !w.sendError(err) {
closed = true
}
}
} }
} }
} }
@ -582,21 +636,31 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
e.Op |= Chmod e.Op |= Chmod
} }
// No point sending a write and delete event at the same time: if it's gone,
// then it's gone.
if e.Op.Has(Write) && e.Op.Has(Remove) {
e.Op &^= Write
}
return e return e
} }
// watchDirectoryFiles to mimic inotify when adding a watch on a directory // watchDirectoryFiles to mimic inotify when adding a watch on a directory
func (w *Watcher) watchDirectoryFiles(dirPath string) error { func (w *Watcher) watchDirectoryFiles(dirPath string) error {
// Get all files // Get all files
files, err := ioutil.ReadDir(dirPath) files, err := os.ReadDir(dirPath)
if err != nil { if err != nil {
return err return err
} }
for _, fileInfo := range files { for _, f := range files {
path := filepath.Join(dirPath, fileInfo.Name()) path := filepath.Join(dirPath, f.Name())
cleanPath, err := w.internalWatch(path, fileInfo) fi, err := f.Info()
if err != nil {
return fmt.Errorf("%q: %w", path, err)
}
cleanPath, err := w.internalWatch(path, fi)
if err != nil { if err != nil {
// No permission to read the file; that's not a problem: just skip. // No permission to read the file; that's not a problem: just skip.
// But do add it to w.fileExists to prevent it from being picked up // But do add it to w.fileExists to prevent it from being picked up
@ -606,7 +670,7 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM): case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM):
cleanPath = filepath.Clean(path) cleanPath = filepath.Clean(path)
default: default:
return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err) return fmt.Errorf("%q: %w", path, err)
} }
} }
@ -622,26 +686,37 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
// //
// This functionality is to have the BSD watcher match the inotify, which sends // This functionality is to have the BSD watcher match the inotify, which sends
// a create event for files created in a watched directory. // a create event for files created in a watched directory.
func (w *Watcher) sendDirectoryChangeEvents(dir string) { func (w *Watcher) sendDirectoryChangeEvents(dir string) error {
// Get all files files, err := os.ReadDir(dir)
files, err := ioutil.ReadDir(dir)
if err != nil { if err != nil {
if !w.sendError(fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)) { // Directory no longer exists: we can ignore this safely. kqueue will
return // still give us the correct events.
if errors.Is(err, os.ErrNotExist) {
return nil
} }
return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
} }
// Search for new files for _, f := range files {
for _, fi := range files { fi, err := f.Info()
err := w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
if err != nil { if err != nil {
return return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
}
err = w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
if err != nil {
// Don't need to send an error if this file isn't readable.
if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) {
return nil
}
return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
} }
} }
return nil
} }
// sendFileCreatedEvent sends a create event if the file isn't already being tracked. // sendFileCreatedEvent sends a create event if the file isn't already being tracked.
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fi os.FileInfo) (err error) {
w.mu.Lock() w.mu.Lock()
_, doesExist := w.fileExists[filePath] _, doesExist := w.fileExists[filePath]
w.mu.Unlock() w.mu.Unlock()
@ -652,7 +727,7 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
} }
// like watchDirectoryFiles (but without doing another ReadDir) // like watchDirectoryFiles (but without doing another ReadDir)
filePath, err = w.internalWatch(filePath, fileInfo) filePath, err = w.internalWatch(filePath, fi)
if err != nil { if err != nil {
return err return err
} }
@ -664,10 +739,10 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
return nil return nil
} }
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) { func (w *Watcher) internalWatch(name string, fi os.FileInfo) (string, error) {
if fileInfo.IsDir() { if fi.IsDir() {
// mimic Linux providing delete events for subdirectories // mimic Linux providing delete events for subdirectories, but preserve
// but preserve the flags used if currently watching subdirectory // the flags used if currently watching subdirectory
w.mu.Lock() w.mu.Lock()
flags := w.dirFlags[name] flags := w.dirFlags[name]
w.mu.Unlock() w.mu.Unlock()

View File

@ -1,39 +1,169 @@
//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows //go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows // +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
// Note: the documentation on the Watcher type and methods is generated from
// mkdoc.zsh
package fsnotify package fsnotify
import ( import "errors"
"fmt"
"runtime"
)
// Watcher watches a set of files, delivering events to a channel. // Watcher watches a set of paths, delivering events on a channel.
type Watcher struct{} //
// A watcher should not be copied (e.g. pass it by pointer, rather than by
// value).
//
// # Linux notes
//
// When a file is removed a Remove event won't be emitted until all file
// descriptors are closed, and deletes will always emit a Chmod. For example:
//
// fp := os.Open("file")
// os.Remove("file") // Triggers Chmod
// fp.Close() // Triggers Remove
//
// This is the event that inotify sends, so not much can be changed about this.
//
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
// for the number of watches per user, and fs.inotify.max_user_instances
// specifies the maximum number of inotify instances per user. Every Watcher you
// create is an "instance", and every path you add is a "watch".
//
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
// /proc/sys/fs/inotify/max_user_instances
//
// To increase them you can use sysctl or write the value to the /proc file:
//
// # Default values on Linux 5.18
// sysctl fs.inotify.max_user_watches=124983
// sysctl fs.inotify.max_user_instances=128
//
// To make the changes persist on reboot edit /etc/sysctl.conf or
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
// your distro's documentation):
//
// fs.inotify.max_user_watches=124983
// fs.inotify.max_user_instances=128
//
// Reaching the limit will result in a "no space left on device" or "too many open
// files" error.
//
// # kqueue notes (macOS, BSD)
//
// kqueue requires opening a file descriptor for every file that's being watched;
// so if you're watching a directory with five files then that's six file
// descriptors. You will run in to your system's "max open files" limit faster on
// these platforms.
//
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
// control the maximum number of open files, as well as /etc/login.conf on BSD
// systems.
//
// # Windows notes
//
// Paths can be added as "C:\path\to\dir", but forward slashes
// ("C:/path/to/dir") will also work.
//
// When a watched directory is removed it will always send an event for the
// directory itself, but may not send events for all files in that directory.
// Sometimes it will send events for all times, sometimes it will send no
// events, and often only for some files.
//
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
// value that is guaranteed to work with SMB filesystems. If you have many
// events in quick succession this may not be enough, and you will have to use
// [WithBufferSize] to increase the value.
type Watcher struct {
// Events sends the filesystem change events.
//
// fsnotify can send the following events; a "path" here can refer to a
// file, directory, symbolic link, or special file like a FIFO.
//
// fsnotify.Create A new path was created; this may be followed by one
// or more Write events if data also gets written to a
// file.
//
// fsnotify.Remove A path was removed.
//
// fsnotify.Rename A path was renamed. A rename is always sent with the
// old path as Event.Name, and a Create event will be
// sent with the new name. Renames are only sent for
// paths that are currently watched; e.g. moving an
// unmonitored file into a monitored directory will
// show up as just a Create. Similarly, renaming a file
// to outside a monitored directory will show up as
// only a Rename.
//
// fsnotify.Write A file or named pipe was written to. A Truncate will
// also trigger a Write. A single "write action"
// initiated by the user may show up as one or multiple
// writes, depending on when the system syncs things to
// disk. For example when compiling a large Go program
// you may get hundreds of Write events, and you may
// want to wait until you've stopped receiving them
// (see the dedup example in cmd/fsnotify).
//
// Some systems may send Write event for directories
// when the directory content changes.
//
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
// when a file is removed (or more accurately, when a
// link to an inode is removed). On kqueue it's sent
// when a file is truncated. On Windows it's never
// sent.
Events chan Event
// Errors sends any errors.
//
// ErrEventOverflow is used to indicate there are too many events:
//
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
// - kqueue, fen: Not used.
Errors chan error
}
// NewWatcher creates a new Watcher. // NewWatcher creates a new Watcher.
func NewWatcher() (*Watcher, error) { func NewWatcher() (*Watcher, error) {
return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS) return nil, errors.New("fsnotify not supported on the current platform")
} }
// Close removes all watches and closes the events channel. // NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
func (w *Watcher) Close() error { // channel.
return nil //
} // The main use case for this is situations with a very large number of events
// where the kernel buffer size can't be increased (e.g. due to lack of
// permissions). An unbuffered Watcher will perform better for almost all use
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
func NewBufferedWatcher(sz uint) (*Watcher, error) { return NewWatcher() }
// Close removes all watches and closes the Events channel.
func (w *Watcher) Close() error { return nil }
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) WatchList() []string { return nil }
// Add starts monitoring the path for changes. // Add starts monitoring the path for changes.
// //
// A path can only be watched once; attempting to watch it more than once will // A path can only be watched once; watching it more than once is a no-op and will
// return an error. Paths that do not yet exist on the filesystem cannot be // not return an error. Paths that do not yet exist on the filesystem cannot be
// added. A watch will be automatically removed if the path is deleted. // watched.
// //
// A path will remain watched if it gets renamed to somewhere else on the same // A watch will be automatically removed if the watched path is deleted or
// filesystem, but the monitor will get removed if the path gets deleted and // renamed. The exception is the Windows backend, which doesn't remove the
// re-created, or if it's moved to a different filesystem. // watcher on renames.
// //
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
// filesystems (/proc, /sys, etc.) generally don't work. // filesystems (/proc, /sys, etc.) generally don't work.
// //
// Returns [ErrClosed] if [Watcher.Close] was called.
//
// See [Watcher.AddWith] for a version that allows adding options.
//
// # Watching directories // # Watching directories
// //
// All files in a directory are monitored, including new files that are created // All files in a directory are monitored, including new files that are created
@ -43,17 +173,26 @@ func (w *Watcher) Close() error {
// # Watching files // # Watching files
// //
// Watching individual files (rather than directories) is generally not // Watching individual files (rather than directories) is generally not
// recommended as many tools update files atomically. Instead of "just" writing // recommended as many programs (especially editors) update files atomically: it
// to the file a temporary file will be written to first, and if successful the // will write to a temporary file which is then moved to to destination,
// temporary file is moved to to destination removing the original, or some // overwriting the original (or some variant thereof). The watcher on the
// variant thereof. The watcher on the original file is now lost, as it no // original file is now lost, as that no longer exists.
// longer exists.
// //
// Instead, watch the parent directory and use Event.Name to filter out files // The upshot of this is that a power failure or crash won't leave a
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. // half-written file.
func (w *Watcher) Add(name string) error { //
return nil // Watch the parent directory and use Event.Name to filter out files you're not
} // interested in. There is an example of this in cmd/fsnotify/file.go.
func (w *Watcher) Add(name string) error { return nil }
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
// the defaults described below are used.
//
// Possible options are:
//
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
// other platforms. The default is 64K (65536 bytes).
func (w *Watcher) AddWith(name string, opts ...addOpt) error { return nil }
// Remove stops monitoring the path for changes. // Remove stops monitoring the path for changes.
// //
@ -61,6 +200,6 @@ func (w *Watcher) Add(name string) error {
// /tmp/dir and /tmp/dir/subdir then you will need to remove both. // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
// //
// Removing a path that has not yet been added returns [ErrNonExistentWatch]. // Removing a path that has not yet been added returns [ErrNonExistentWatch].
func (w *Watcher) Remove(name string) error { //
return nil // Returns nil if [Watcher.Close] was called.
} func (w *Watcher) Remove(name string) error { return nil }

View File

@ -1,6 +1,13 @@
//go:build windows //go:build windows
// +build windows // +build windows
// Windows backend based on ReadDirectoryChangesW()
//
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
//
// Note: the documentation on the Watcher type and methods is generated from
// mkdoc.zsh
package fsnotify package fsnotify
import ( import (
@ -68,14 +75,20 @@ import (
// control the maximum number of open files, as well as /etc/login.conf on BSD // control the maximum number of open files, as well as /etc/login.conf on BSD
// systems. // systems.
// //
// # macOS notes // # Windows notes
// //
// Spotlight indexing on macOS can result in multiple events (see [#15]). A // Paths can be added as "C:\path\to\dir", but forward slashes
// temporary workaround is to add your folder(s) to the "Spotlight Privacy // ("C:/path/to/dir") will also work.
// Settings" until we have a native FSEvents implementation (see [#11]).
// //
// [#11]: https://github.com/fsnotify/fsnotify/issues/11 // When a watched directory is removed it will always send an event for the
// [#15]: https://github.com/fsnotify/fsnotify/issues/15 // directory itself, but may not send events for all files in that directory.
// Sometimes it will send events for all times, sometimes it will send no
// events, and often only for some files.
//
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
// value that is guaranteed to work with SMB filesystems. If you have many
// events in quick succession this may not be enough, and you will have to use
// [WithBufferSize] to increase the value.
type Watcher struct { type Watcher struct {
// Events sends the filesystem change events. // Events sends the filesystem change events.
// //
@ -102,31 +115,52 @@ type Watcher struct {
// initiated by the user may show up as one or multiple // initiated by the user may show up as one or multiple
// writes, depending on when the system syncs things to // writes, depending on when the system syncs things to
// disk. For example when compiling a large Go program // disk. For example when compiling a large Go program
// you may get hundreds of Write events, so you // you may get hundreds of Write events, and you may
// probably want to wait until you've stopped receiving // want to wait until you've stopped receiving them
// them (see the dedup example in cmd/fsnotify). // (see the dedup example in cmd/fsnotify).
//
// Some systems may send Write event for directories
// when the directory content changes.
// //
// fsnotify.Chmod Attributes were changed. On Linux this is also sent // fsnotify.Chmod Attributes were changed. On Linux this is also sent
// when a file is removed (or more accurately, when a // when a file is removed (or more accurately, when a
// link to an inode is removed). On kqueue it's sent // link to an inode is removed). On kqueue it's sent
// and on kqueue when a file is truncated. On Windows // when a file is truncated. On Windows it's never
// it's never sent. // sent.
Events chan Event Events chan Event
// Errors sends any errors. // Errors sends any errors.
//
// ErrEventOverflow is used to indicate there are too many events:
//
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
// - kqueue, fen: Not used.
Errors chan error Errors chan error
port windows.Handle // Handle to completion port port windows.Handle // Handle to completion port
input chan *input // Inputs to the reader are sent on this channel input chan *input // Inputs to the reader are sent on this channel
quit chan chan<- error quit chan chan<- error
mu sync.Mutex // Protects access to watches, isClosed mu sync.Mutex // Protects access to watches, closed
watches watchMap // Map of watches (key: i-number) watches watchMap // Map of watches (key: i-number)
isClosed bool // Set to true when Close() is first called closed bool // Set to true when Close() is first called
} }
// NewWatcher creates a new Watcher. // NewWatcher creates a new Watcher.
func NewWatcher() (*Watcher, error) { func NewWatcher() (*Watcher, error) {
return NewBufferedWatcher(50)
}
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
// channel.
//
// The main use case for this is situations with a very large number of events
// where the kernel buffer size can't be increased (e.g. due to lack of
// permissions). An unbuffered Watcher will perform better for almost all use
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
func NewBufferedWatcher(sz uint) (*Watcher, error) {
port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0) port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
if err != nil { if err != nil {
return nil, os.NewSyscallError("CreateIoCompletionPort", err) return nil, os.NewSyscallError("CreateIoCompletionPort", err)
@ -135,7 +169,7 @@ func NewWatcher() (*Watcher, error) {
port: port, port: port,
watches: make(watchMap), watches: make(watchMap),
input: make(chan *input, 1), input: make(chan *input, 1),
Events: make(chan Event, 50), Events: make(chan Event, sz),
Errors: make(chan error), Errors: make(chan error),
quit: make(chan chan<- error, 1), quit: make(chan chan<- error, 1),
} }
@ -143,6 +177,12 @@ func NewWatcher() (*Watcher, error) {
return w, nil return w, nil
} }
func (w *Watcher) isClosed() bool {
w.mu.Lock()
defer w.mu.Unlock()
return w.closed
}
func (w *Watcher) sendEvent(name string, mask uint64) bool { func (w *Watcher) sendEvent(name string, mask uint64) bool {
if mask == 0 { if mask == 0 {
return false return false
@ -167,14 +207,14 @@ func (w *Watcher) sendError(err error) bool {
return false return false
} }
// Close removes all watches and closes the events channel. // Close removes all watches and closes the Events channel.
func (w *Watcher) Close() error { func (w *Watcher) Close() error {
w.mu.Lock() if w.isClosed() {
if w.isClosed {
w.mu.Unlock()
return nil return nil
} }
w.isClosed = true
w.mu.Lock()
w.closed = true
w.mu.Unlock() w.mu.Unlock()
// Send "quit" message to the reader goroutine // Send "quit" message to the reader goroutine
@ -188,17 +228,21 @@ func (w *Watcher) Close() error {
// Add starts monitoring the path for changes. // Add starts monitoring the path for changes.
// //
// A path can only be watched once; attempting to watch it more than once will // A path can only be watched once; watching it more than once is a no-op and will
// return an error. Paths that do not yet exist on the filesystem cannot be // not return an error. Paths that do not yet exist on the filesystem cannot be
// added. A watch will be automatically removed if the path is deleted. // watched.
// //
// A path will remain watched if it gets renamed to somewhere else on the same // A watch will be automatically removed if the watched path is deleted or
// filesystem, but the monitor will get removed if the path gets deleted and // renamed. The exception is the Windows backend, which doesn't remove the
// re-created, or if it's moved to a different filesystem. // watcher on renames.
// //
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
// filesystems (/proc, /sys, etc.) generally don't work. // filesystems (/proc, /sys, etc.) generally don't work.
// //
// Returns [ErrClosed] if [Watcher.Close] was called.
//
// See [Watcher.AddWith] for a version that allows adding options.
//
// # Watching directories // # Watching directories
// //
// All files in a directory are monitored, including new files that are created // All files in a directory are monitored, including new files that are created
@ -208,27 +252,41 @@ func (w *Watcher) Close() error {
// # Watching files // # Watching files
// //
// Watching individual files (rather than directories) is generally not // Watching individual files (rather than directories) is generally not
// recommended as many tools update files atomically. Instead of "just" writing // recommended as many programs (especially editors) update files atomically: it
// to the file a temporary file will be written to first, and if successful the // will write to a temporary file which is then moved to to destination,
// temporary file is moved to to destination removing the original, or some // overwriting the original (or some variant thereof). The watcher on the
// variant thereof. The watcher on the original file is now lost, as it no // original file is now lost, as that no longer exists.
// longer exists.
// //
// Instead, watch the parent directory and use Event.Name to filter out files // The upshot of this is that a power failure or crash won't leave a
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. // half-written file.
func (w *Watcher) Add(name string) error { //
w.mu.Lock() // Watch the parent directory and use Event.Name to filter out files you're not
if w.isClosed { // interested in. There is an example of this in cmd/fsnotify/file.go.
w.mu.Unlock() func (w *Watcher) Add(name string) error { return w.AddWith(name) }
return errors.New("watcher already closed")
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
// the defaults described below are used.
//
// Possible options are:
//
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
// other platforms. The default is 64K (65536 bytes).
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
if w.isClosed() {
return ErrClosed
}
with := getOptions(opts...)
if with.bufsize < 4096 {
return fmt.Errorf("fsnotify.WithBufferSize: buffer size cannot be smaller than 4096 bytes")
} }
w.mu.Unlock()
in := &input{ in := &input{
op: opAddWatch, op: opAddWatch,
path: filepath.Clean(name), path: filepath.Clean(name),
flags: sysFSALLEVENTS, flags: sysFSALLEVENTS,
reply: make(chan error), reply: make(chan error),
bufsize: with.bufsize,
} }
w.input <- in w.input <- in
if err := w.wakeupReader(); err != nil { if err := w.wakeupReader(); err != nil {
@ -243,7 +301,13 @@ func (w *Watcher) Add(name string) error {
// /tmp/dir and /tmp/dir/subdir then you will need to remove both. // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
// //
// Removing a path that has not yet been added returns [ErrNonExistentWatch]. // Removing a path that has not yet been added returns [ErrNonExistentWatch].
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) Remove(name string) error { func (w *Watcher) Remove(name string) error {
if w.isClosed() {
return nil
}
in := &input{ in := &input{
op: opRemoveWatch, op: opRemoveWatch,
path: filepath.Clean(name), path: filepath.Clean(name),
@ -256,8 +320,15 @@ func (w *Watcher) Remove(name string) error {
return <-in.reply return <-in.reply
} }
// WatchList returns all paths added with [Add] (and are not yet removed). // WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
// Returns nil if [Watcher.Close] was called.
func (w *Watcher) WatchList() []string { func (w *Watcher) WatchList() []string {
if w.isClosed() {
return nil
}
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
@ -279,7 +350,6 @@ func (w *Watcher) WatchList() []string {
// This should all be removed at some point, and just use windows.FILE_NOTIFY_* // This should all be removed at some point, and just use windows.FILE_NOTIFY_*
const ( const (
sysFSALLEVENTS = 0xfff sysFSALLEVENTS = 0xfff
sysFSATTRIB = 0x4
sysFSCREATE = 0x100 sysFSCREATE = 0x100
sysFSDELETE = 0x200 sysFSDELETE = 0x200
sysFSDELETESELF = 0x400 sysFSDELETESELF = 0x400
@ -305,9 +375,6 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
e.Op |= Rename e.Op |= Rename
} }
if mask&sysFSATTRIB == sysFSATTRIB {
e.Op |= Chmod
}
return e return e
} }
@ -324,6 +391,7 @@ type input struct {
op int op int
path string path string
flags uint32 flags uint32
bufsize int
reply chan error reply chan error
} }
@ -336,11 +404,12 @@ type inode struct {
type watch struct { type watch struct {
ov windows.Overlapped ov windows.Overlapped
ino *inode // i-number ino *inode // i-number
recurse bool // Recursive watch?
path string // Directory path path string // Directory path
mask uint64 // Directory itself is being watched with these notify flags mask uint64 // Directory itself is being watched with these notify flags
names map[string]uint64 // Map of names being watched and their notify flags names map[string]uint64 // Map of names being watched and their notify flags
rename string // Remembers the old name while renaming a file rename string // Remembers the old name while renaming a file
buf [65536]byte // 64K buffer buf []byte // buffer, allocated later
} }
type ( type (
@ -413,7 +482,10 @@ func (m watchMap) set(ino *inode, watch *watch) {
} }
// Must run within the I/O thread. // Must run within the I/O thread.
func (w *Watcher) addWatch(pathname string, flags uint64) error { func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
//pathname, recurse := recursivePath(pathname)
recurse := false
dir, err := w.getDir(pathname) dir, err := w.getDir(pathname)
if err != nil { if err != nil {
return err return err
@ -436,6 +508,8 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
ino: ino, ino: ino,
path: dir, path: dir,
names: make(map[string]uint64), names: make(map[string]uint64),
recurse: recurse,
buf: make([]byte, bufsize),
} }
w.mu.Lock() w.mu.Lock()
w.watches.set(ino, watchEntry) w.watches.set(ino, watchEntry)
@ -465,6 +539,8 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
// Must run within the I/O thread. // Must run within the I/O thread.
func (w *Watcher) remWatch(pathname string) error { func (w *Watcher) remWatch(pathname string) error {
pathname, recurse := recursivePath(pathname)
dir, err := w.getDir(pathname) dir, err := w.getDir(pathname)
if err != nil { if err != nil {
return err return err
@ -478,6 +554,10 @@ func (w *Watcher) remWatch(pathname string) error {
watch := w.watches.get(ino) watch := w.watches.get(ino)
w.mu.Unlock() w.mu.Unlock()
if recurse && !watch.recurse {
return fmt.Errorf("can't use \\... with non-recursive watch %q", pathname)
}
err = windows.CloseHandle(ino.handle) err = windows.CloseHandle(ino.handle)
if err != nil { if err != nil {
w.sendError(os.NewSyscallError("CloseHandle", err)) w.sendError(os.NewSyscallError("CloseHandle", err))
@ -535,8 +615,11 @@ func (w *Watcher) startRead(watch *watch) error {
return nil return nil
} }
rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], // We need to pass the array, rather than the slice.
uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) hdr := (*reflect.SliceHeader)(unsafe.Pointer(&watch.buf))
rdErr := windows.ReadDirectoryChanges(watch.ino.handle,
(*byte)(unsafe.Pointer(hdr.Data)), uint32(hdr.Len),
watch.recurse, mask, nil, &watch.ov, 0)
if rdErr != nil { if rdErr != nil {
err := os.NewSyscallError("ReadDirectoryChanges", rdErr) err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
@ -563,9 +646,8 @@ func (w *Watcher) readEvents() {
runtime.LockOSThread() runtime.LockOSThread()
for { for {
// This error is handled after the watch == nil check below.
qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE) qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE)
// This error is handled after the watch == nil check below. NOTE: this
// seems odd, note sure if it's correct.
watch := (*watch)(unsafe.Pointer(ov)) watch := (*watch)(unsafe.Pointer(ov))
if watch == nil { if watch == nil {
@ -595,7 +677,7 @@ func (w *Watcher) readEvents() {
case in := <-w.input: case in := <-w.input:
switch in.op { switch in.op {
case opAddWatch: case opAddWatch:
in.reply <- w.addWatch(in.path, uint64(in.flags)) in.reply <- w.addWatch(in.path, uint64(in.flags), in.bufsize)
case opRemoveWatch: case opRemoveWatch:
in.reply <- w.remWatch(in.path) in.reply <- w.remWatch(in.path)
} }
@ -605,6 +687,8 @@ func (w *Watcher) readEvents() {
} }
switch qErr { switch qErr {
case nil:
// No error
case windows.ERROR_MORE_DATA: case windows.ERROR_MORE_DATA:
if watch == nil { if watch == nil {
w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")) w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer"))
@ -626,13 +710,12 @@ func (w *Watcher) readEvents() {
default: default:
w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr)) w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr))
continue continue
case nil:
} }
var offset uint32 var offset uint32
for { for {
if n == 0 { if n == 0 {
w.sendError(errors.New("short read in readEvents()")) w.sendError(ErrEventOverflow)
break break
} }
@ -703,8 +786,9 @@ func (w *Watcher) readEvents() {
// Error! // Error!
if offset >= n { if offset >= n {
//lint:ignore ST1005 Windows should be capitalized
w.sendError(errors.New( w.sendError(errors.New(
"Windows system assumed buffer larger than it is, events have likely been missed.")) "Windows system assumed buffer larger than it is, events have likely been missed"))
break break
} }
} }
@ -720,9 +804,6 @@ func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
if mask&sysFSMODIFY != 0 { if mask&sysFSMODIFY != 0 {
m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE
} }
if mask&sysFSATTRIB != 0 {
m |= windows.FILE_NOTIFY_CHANGE_ATTRIBUTES
}
if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 { if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME
} }

View File

@ -1,13 +1,18 @@
//go:build !plan9
// +build !plan9
// Package fsnotify provides a cross-platform interface for file system // Package fsnotify provides a cross-platform interface for file system
// notifications. // notifications.
//
// Currently supported systems:
//
// Linux 2.6.32+ via inotify
// BSD, macOS via kqueue
// Windows via ReadDirectoryChangesW
// illumos via FEN
package fsnotify package fsnotify
import ( import (
"errors" "errors"
"fmt" "fmt"
"path/filepath"
"strings" "strings"
) )
@ -33,34 +38,52 @@ type Op uint32
// The operations fsnotify can trigger; see the documentation on [Watcher] for a // The operations fsnotify can trigger; see the documentation on [Watcher] for a
// full description, and check them with [Event.Has]. // full description, and check them with [Event.Has].
const ( const (
// A new pathname was created.
Create Op = 1 << iota Create Op = 1 << iota
// The pathname was written to; this does *not* mean the write has finished,
// and a write can be followed by more writes.
Write Write
// The path was removed; any watches on it will be removed. Some "remove"
// operations may trigger a Rename if the file is actually moved (for
// example "remove to trash" is often a rename).
Remove Remove
// The path was renamed to something else; any watched on it will be
// removed.
Rename Rename
// File attributes were changed.
//
// It's generally not recommended to take action on this event, as it may
// get triggered very frequently by some software. For example, Spotlight
// indexing on macOS, anti-virus software, backup software, etc.
Chmod Chmod
) )
// Common errors that can be reported by a watcher // Common errors that can be reported.
var ( var (
ErrNonExistentWatch = errors.New("can't remove non-existent watcher") ErrNonExistentWatch = errors.New("fsnotify: can't remove non-existent watch")
ErrEventOverflow = errors.New("fsnotify queue overflow") ErrEventOverflow = errors.New("fsnotify: queue or buffer overflow")
ErrClosed = errors.New("fsnotify: watcher already closed")
) )
func (op Op) String() string { func (o Op) String() string {
var b strings.Builder var b strings.Builder
if op.Has(Create) { if o.Has(Create) {
b.WriteString("|CREATE") b.WriteString("|CREATE")
} }
if op.Has(Remove) { if o.Has(Remove) {
b.WriteString("|REMOVE") b.WriteString("|REMOVE")
} }
if op.Has(Write) { if o.Has(Write) {
b.WriteString("|WRITE") b.WriteString("|WRITE")
} }
if op.Has(Rename) { if o.Has(Rename) {
b.WriteString("|RENAME") b.WriteString("|RENAME")
} }
if op.Has(Chmod) { if o.Has(Chmod) {
b.WriteString("|CHMOD") b.WriteString("|CHMOD")
} }
if b.Len() == 0 { if b.Len() == 0 {
@ -70,7 +93,7 @@ func (op Op) String() string {
} }
// Has reports if this operation has the given operation. // Has reports if this operation has the given operation.
func (o Op) Has(h Op) bool { return o&h == h } func (o Op) Has(h Op) bool { return o&h != 0 }
// Has reports if this event has the given operation. // Has reports if this event has the given operation.
func (e Event) Has(op Op) bool { return e.Op.Has(op) } func (e Event) Has(op Op) bool { return e.Op.Has(op) }
@ -79,3 +102,45 @@ func (e Event) Has(op Op) bool { return e.Op.Has(op) }
func (e Event) String() string { func (e Event) String() string {
return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name) return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name)
} }
type (
addOpt func(opt *withOpts)
withOpts struct {
bufsize int
}
)
var defaultOpts = withOpts{
bufsize: 65536, // 64K
}
func getOptions(opts ...addOpt) withOpts {
with := defaultOpts
for _, o := range opts {
o(&with)
}
return with
}
// WithBufferSize sets the [ReadDirectoryChangesW] buffer size.
//
// This only has effect on Windows systems, and is a no-op for other backends.
//
// The default value is 64K (65536 bytes) which is the highest value that works
// on all filesystems and should be enough for most applications, but if you
// have a large burst of events it may not be enough. You can increase it if
// you're hitting "queue or buffer overflow" errors ([ErrEventOverflow]).
//
// [ReadDirectoryChangesW]: https://learn.microsoft.com/en-gb/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
func WithBufferSize(bytes int) addOpt {
return func(opt *withOpts) { opt.bufsize = bytes }
}
// Check if this path is recursive (ends with "/..." or "\..."), and return the
// path with the /... stripped.
func recursivePath(path string) (string, bool) {
if filepath.Base(path) == "..." {
return filepath.Dir(path), true
}
return path, false
}

View File

@ -2,8 +2,8 @@
[ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1 [ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1
setopt err_exit no_unset pipefail extended_glob setopt err_exit no_unset pipefail extended_glob
# Simple script to update the godoc comments on all watchers. Probably took me # Simple script to update the godoc comments on all watchers so you don't need
# more time to write this than doing it manually, but ah well 🙃 # to update the same comment 5 times.
watcher=$(<<EOF watcher=$(<<EOF
// Watcher watches a set of paths, delivering events on a channel. // Watcher watches a set of paths, delivering events on a channel.
@ -57,14 +57,20 @@ watcher=$(<<EOF
// control the maximum number of open files, as well as /etc/login.conf on BSD // control the maximum number of open files, as well as /etc/login.conf on BSD
// systems. // systems.
// //
// # macOS notes // # Windows notes
// //
// Spotlight indexing on macOS can result in multiple events (see [#15]). A // Paths can be added as "C:\\path\\to\\dir", but forward slashes
// temporary workaround is to add your folder(s) to the "Spotlight Privacy // ("C:/path/to/dir") will also work.
// Settings" until we have a native FSEvents implementation (see [#11]).
// //
// [#11]: https://github.com/fsnotify/fsnotify/issues/11 // When a watched directory is removed it will always send an event for the
// [#15]: https://github.com/fsnotify/fsnotify/issues/15 // directory itself, but may not send events for all files in that directory.
// Sometimes it will send events for all times, sometimes it will send no
// events, and often only for some files.
//
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
// value that is guaranteed to work with SMB filesystems. If you have many
// events in quick succession this may not be enough, and you will have to use
// [WithBufferSize] to increase the value.
EOF EOF
) )
@ -73,20 +79,36 @@ new=$(<<EOF
EOF EOF
) )
newbuffered=$(<<EOF
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
// channel.
//
// The main use case for this is situations with a very large number of events
// where the kernel buffer size can't be increased (e.g. due to lack of
// permissions). An unbuffered Watcher will perform better for almost all use
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
EOF
)
add=$(<<EOF add=$(<<EOF
// Add starts monitoring the path for changes. // Add starts monitoring the path for changes.
// //
// A path can only be watched once; attempting to watch it more than once will // A path can only be watched once; watching it more than once is a no-op and will
// return an error. Paths that do not yet exist on the filesystem cannot be // not return an error. Paths that do not yet exist on the filesystem cannot be
// added. A watch will be automatically removed if the path is deleted. // watched.
// //
// A path will remain watched if it gets renamed to somewhere else on the same // A watch will be automatically removed if the watched path is deleted or
// filesystem, but the monitor will get removed if the path gets deleted and // renamed. The exception is the Windows backend, which doesn't remove the
// re-created, or if it's moved to a different filesystem. // watcher on renames.
// //
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
// filesystems (/proc, /sys, etc.) generally don't work. // filesystems (/proc, /sys, etc.) generally don't work.
// //
// Returns [ErrClosed] if [Watcher.Close] was called.
//
// See [Watcher.AddWith] for a version that allows adding options.
//
// # Watching directories // # Watching directories
// //
// All files in a directory are monitored, including new files that are created // All files in a directory are monitored, including new files that are created
@ -96,14 +118,27 @@ add=$(<<EOF
// # Watching files // # Watching files
// //
// Watching individual files (rather than directories) is generally not // Watching individual files (rather than directories) is generally not
// recommended as many tools update files atomically. Instead of "just" writing // recommended as many programs (especially editors) update files atomically: it
// to the file a temporary file will be written to first, and if successful the // will write to a temporary file which is then moved to to destination,
// temporary file is moved to to destination removing the original, or some // overwriting the original (or some variant thereof). The watcher on the
// variant thereof. The watcher on the original file is now lost, as it no // original file is now lost, as that no longer exists.
// longer exists.
// //
// Instead, watch the parent directory and use Event.Name to filter out files // The upshot of this is that a power failure or crash won't leave a
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. // half-written file.
//
// Watch the parent directory and use Event.Name to filter out files you're not
// interested in. There is an example of this in cmd/fsnotify/file.go.
EOF
)
addwith=$(<<EOF
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
// the defaults described below are used.
//
// Possible options are:
//
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
// other platforms. The default is 64K (65536 bytes).
EOF EOF
) )
@ -114,16 +149,21 @@ remove=$(<<EOF
// /tmp/dir and /tmp/dir/subdir then you will need to remove both. // /tmp/dir and /tmp/dir/subdir then you will need to remove both.
// //
// Removing a path that has not yet been added returns [ErrNonExistentWatch]. // Removing a path that has not yet been added returns [ErrNonExistentWatch].
//
// Returns nil if [Watcher.Close] was called.
EOF EOF
) )
close=$(<<EOF close=$(<<EOF
// Close removes all watches and closes the events channel. // Close removes all watches and closes the Events channel.
EOF EOF
) )
watchlist=$(<<EOF watchlist=$(<<EOF
// WatchList returns all paths added with [Add] (and are not yet removed). // WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
// Returns nil if [Watcher.Close] was called.
EOF EOF
) )
@ -153,20 +193,29 @@ events=$(<<EOF
// initiated by the user may show up as one or multiple // initiated by the user may show up as one or multiple
// writes, depending on when the system syncs things to // writes, depending on when the system syncs things to
// disk. For example when compiling a large Go program // disk. For example when compiling a large Go program
// you may get hundreds of Write events, so you // you may get hundreds of Write events, and you may
// probably want to wait until you've stopped receiving // want to wait until you've stopped receiving them
// them (see the dedup example in cmd/fsnotify). // (see the dedup example in cmd/fsnotify).
//
// Some systems may send Write event for directories
// when the directory content changes.
// //
// fsnotify.Chmod Attributes were changed. On Linux this is also sent // fsnotify.Chmod Attributes were changed. On Linux this is also sent
// when a file is removed (or more accurately, when a // when a file is removed (or more accurately, when a
// link to an inode is removed). On kqueue it's sent // link to an inode is removed). On kqueue it's sent
// and on kqueue when a file is truncated. On Windows // when a file is truncated. On Windows it's never
// it's never sent. // sent.
EOF EOF
) )
errors=$(<<EOF errors=$(<<EOF
// Errors sends any errors. // Errors sends any errors.
//
// ErrEventOverflow is used to indicate there are too many events:
//
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
// - kqueue, fen: Not used.
EOF EOF
) )
@ -200,7 +249,9 @@ set-cmt() {
set-cmt '^type Watcher struct ' $watcher set-cmt '^type Watcher struct ' $watcher
set-cmt '^func NewWatcher(' $new set-cmt '^func NewWatcher(' $new
set-cmt '^func NewBufferedWatcher(' $newbuffered
set-cmt '^func (w \*Watcher) Add(' $add set-cmt '^func (w \*Watcher) Add(' $add
set-cmt '^func (w \*Watcher) AddWith(' $addwith
set-cmt '^func (w \*Watcher) Remove(' $remove set-cmt '^func (w \*Watcher) Remove(' $remove
set-cmt '^func (w \*Watcher) Close(' $close set-cmt '^func (w \*Watcher) Close(' $close
set-cmt '^func (w \*Watcher) WatchList(' $watchlist set-cmt '^func (w \*Watcher) WatchList(' $watchlist

View File

@ -12,6 +12,31 @@ import (
// Symbols defined in public import of google/protobuf/descriptor.proto. // Symbols defined in public import of google/protobuf/descriptor.proto.
type Edition = descriptorpb.Edition
const Edition_EDITION_UNKNOWN = descriptorpb.Edition_EDITION_UNKNOWN
const Edition_EDITION_PROTO2 = descriptorpb.Edition_EDITION_PROTO2
const Edition_EDITION_PROTO3 = descriptorpb.Edition_EDITION_PROTO3
const Edition_EDITION_2023 = descriptorpb.Edition_EDITION_2023
const Edition_EDITION_2024 = descriptorpb.Edition_EDITION_2024
const Edition_EDITION_1_TEST_ONLY = descriptorpb.Edition_EDITION_1_TEST_ONLY
const Edition_EDITION_2_TEST_ONLY = descriptorpb.Edition_EDITION_2_TEST_ONLY
const Edition_EDITION_99997_TEST_ONLY = descriptorpb.Edition_EDITION_99997_TEST_ONLY
const Edition_EDITION_99998_TEST_ONLY = descriptorpb.Edition_EDITION_99998_TEST_ONLY
const Edition_EDITION_99999_TEST_ONLY = descriptorpb.Edition_EDITION_99999_TEST_ONLY
const Edition_EDITION_MAX = descriptorpb.Edition_EDITION_MAX
var Edition_name = descriptorpb.Edition_name
var Edition_value = descriptorpb.Edition_value
type ExtensionRangeOptions_VerificationState = descriptorpb.ExtensionRangeOptions_VerificationState
const ExtensionRangeOptions_DECLARATION = descriptorpb.ExtensionRangeOptions_DECLARATION
const ExtensionRangeOptions_UNVERIFIED = descriptorpb.ExtensionRangeOptions_UNVERIFIED
var ExtensionRangeOptions_VerificationState_name = descriptorpb.ExtensionRangeOptions_VerificationState_name
var ExtensionRangeOptions_VerificationState_value = descriptorpb.ExtensionRangeOptions_VerificationState_value
type FieldDescriptorProto_Type = descriptorpb.FieldDescriptorProto_Type type FieldDescriptorProto_Type = descriptorpb.FieldDescriptorProto_Type
const FieldDescriptorProto_TYPE_DOUBLE = descriptorpb.FieldDescriptorProto_TYPE_DOUBLE const FieldDescriptorProto_TYPE_DOUBLE = descriptorpb.FieldDescriptorProto_TYPE_DOUBLE
@ -39,8 +64,8 @@ var FieldDescriptorProto_Type_value = descriptorpb.FieldDescriptorProto_Type_val
type FieldDescriptorProto_Label = descriptorpb.FieldDescriptorProto_Label type FieldDescriptorProto_Label = descriptorpb.FieldDescriptorProto_Label
const FieldDescriptorProto_LABEL_OPTIONAL = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL const FieldDescriptorProto_LABEL_OPTIONAL = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL
const FieldDescriptorProto_LABEL_REQUIRED = descriptorpb.FieldDescriptorProto_LABEL_REQUIRED
const FieldDescriptorProto_LABEL_REPEATED = descriptorpb.FieldDescriptorProto_LABEL_REPEATED const FieldDescriptorProto_LABEL_REPEATED = descriptorpb.FieldDescriptorProto_LABEL_REPEATED
const FieldDescriptorProto_LABEL_REQUIRED = descriptorpb.FieldDescriptorProto_LABEL_REQUIRED
var FieldDescriptorProto_Label_name = descriptorpb.FieldDescriptorProto_Label_name var FieldDescriptorProto_Label_name = descriptorpb.FieldDescriptorProto_Label_name
var FieldDescriptorProto_Label_value = descriptorpb.FieldDescriptorProto_Label_value var FieldDescriptorProto_Label_value = descriptorpb.FieldDescriptorProto_Label_value
@ -72,6 +97,31 @@ const FieldOptions_JS_NUMBER = descriptorpb.FieldOptions_JS_NUMBER
var FieldOptions_JSType_name = descriptorpb.FieldOptions_JSType_name var FieldOptions_JSType_name = descriptorpb.FieldOptions_JSType_name
var FieldOptions_JSType_value = descriptorpb.FieldOptions_JSType_value var FieldOptions_JSType_value = descriptorpb.FieldOptions_JSType_value
type FieldOptions_OptionRetention = descriptorpb.FieldOptions_OptionRetention
const FieldOptions_RETENTION_UNKNOWN = descriptorpb.FieldOptions_RETENTION_UNKNOWN
const FieldOptions_RETENTION_RUNTIME = descriptorpb.FieldOptions_RETENTION_RUNTIME
const FieldOptions_RETENTION_SOURCE = descriptorpb.FieldOptions_RETENTION_SOURCE
var FieldOptions_OptionRetention_name = descriptorpb.FieldOptions_OptionRetention_name
var FieldOptions_OptionRetention_value = descriptorpb.FieldOptions_OptionRetention_value
type FieldOptions_OptionTargetType = descriptorpb.FieldOptions_OptionTargetType
const FieldOptions_TARGET_TYPE_UNKNOWN = descriptorpb.FieldOptions_TARGET_TYPE_UNKNOWN
const FieldOptions_TARGET_TYPE_FILE = descriptorpb.FieldOptions_TARGET_TYPE_FILE
const FieldOptions_TARGET_TYPE_EXTENSION_RANGE = descriptorpb.FieldOptions_TARGET_TYPE_EXTENSION_RANGE
const FieldOptions_TARGET_TYPE_MESSAGE = descriptorpb.FieldOptions_TARGET_TYPE_MESSAGE
const FieldOptions_TARGET_TYPE_FIELD = descriptorpb.FieldOptions_TARGET_TYPE_FIELD
const FieldOptions_TARGET_TYPE_ONEOF = descriptorpb.FieldOptions_TARGET_TYPE_ONEOF
const FieldOptions_TARGET_TYPE_ENUM = descriptorpb.FieldOptions_TARGET_TYPE_ENUM
const FieldOptions_TARGET_TYPE_ENUM_ENTRY = descriptorpb.FieldOptions_TARGET_TYPE_ENUM_ENTRY
const FieldOptions_TARGET_TYPE_SERVICE = descriptorpb.FieldOptions_TARGET_TYPE_SERVICE
const FieldOptions_TARGET_TYPE_METHOD = descriptorpb.FieldOptions_TARGET_TYPE_METHOD
var FieldOptions_OptionTargetType_name = descriptorpb.FieldOptions_OptionTargetType_name
var FieldOptions_OptionTargetType_value = descriptorpb.FieldOptions_OptionTargetType_value
type MethodOptions_IdempotencyLevel = descriptorpb.MethodOptions_IdempotencyLevel type MethodOptions_IdempotencyLevel = descriptorpb.MethodOptions_IdempotencyLevel
const MethodOptions_IDEMPOTENCY_UNKNOWN = descriptorpb.MethodOptions_IDEMPOTENCY_UNKNOWN const MethodOptions_IDEMPOTENCY_UNKNOWN = descriptorpb.MethodOptions_IDEMPOTENCY_UNKNOWN
@ -81,10 +131,77 @@ const MethodOptions_IDEMPOTENT = descriptorpb.MethodOptions_IDEMPOTENT
var MethodOptions_IdempotencyLevel_name = descriptorpb.MethodOptions_IdempotencyLevel_name var MethodOptions_IdempotencyLevel_name = descriptorpb.MethodOptions_IdempotencyLevel_name
var MethodOptions_IdempotencyLevel_value = descriptorpb.MethodOptions_IdempotencyLevel_value var MethodOptions_IdempotencyLevel_value = descriptorpb.MethodOptions_IdempotencyLevel_value
type FeatureSet_FieldPresence = descriptorpb.FeatureSet_FieldPresence
const FeatureSet_FIELD_PRESENCE_UNKNOWN = descriptorpb.FeatureSet_FIELD_PRESENCE_UNKNOWN
const FeatureSet_EXPLICIT = descriptorpb.FeatureSet_EXPLICIT
const FeatureSet_IMPLICIT = descriptorpb.FeatureSet_IMPLICIT
const FeatureSet_LEGACY_REQUIRED = descriptorpb.FeatureSet_LEGACY_REQUIRED
var FeatureSet_FieldPresence_name = descriptorpb.FeatureSet_FieldPresence_name
var FeatureSet_FieldPresence_value = descriptorpb.FeatureSet_FieldPresence_value
type FeatureSet_EnumType = descriptorpb.FeatureSet_EnumType
const FeatureSet_ENUM_TYPE_UNKNOWN = descriptorpb.FeatureSet_ENUM_TYPE_UNKNOWN
const FeatureSet_OPEN = descriptorpb.FeatureSet_OPEN
const FeatureSet_CLOSED = descriptorpb.FeatureSet_CLOSED
var FeatureSet_EnumType_name = descriptorpb.FeatureSet_EnumType_name
var FeatureSet_EnumType_value = descriptorpb.FeatureSet_EnumType_value
type FeatureSet_RepeatedFieldEncoding = descriptorpb.FeatureSet_RepeatedFieldEncoding
const FeatureSet_REPEATED_FIELD_ENCODING_UNKNOWN = descriptorpb.FeatureSet_REPEATED_FIELD_ENCODING_UNKNOWN
const FeatureSet_PACKED = descriptorpb.FeatureSet_PACKED
const FeatureSet_EXPANDED = descriptorpb.FeatureSet_EXPANDED
var FeatureSet_RepeatedFieldEncoding_name = descriptorpb.FeatureSet_RepeatedFieldEncoding_name
var FeatureSet_RepeatedFieldEncoding_value = descriptorpb.FeatureSet_RepeatedFieldEncoding_value
type FeatureSet_Utf8Validation = descriptorpb.FeatureSet_Utf8Validation
const FeatureSet_UTF8_VALIDATION_UNKNOWN = descriptorpb.FeatureSet_UTF8_VALIDATION_UNKNOWN
const FeatureSet_VERIFY = descriptorpb.FeatureSet_VERIFY
const FeatureSet_NONE = descriptorpb.FeatureSet_NONE
var FeatureSet_Utf8Validation_name = descriptorpb.FeatureSet_Utf8Validation_name
var FeatureSet_Utf8Validation_value = descriptorpb.FeatureSet_Utf8Validation_value
type FeatureSet_MessageEncoding = descriptorpb.FeatureSet_MessageEncoding
const FeatureSet_MESSAGE_ENCODING_UNKNOWN = descriptorpb.FeatureSet_MESSAGE_ENCODING_UNKNOWN
const FeatureSet_LENGTH_PREFIXED = descriptorpb.FeatureSet_LENGTH_PREFIXED
const FeatureSet_DELIMITED = descriptorpb.FeatureSet_DELIMITED
var FeatureSet_MessageEncoding_name = descriptorpb.FeatureSet_MessageEncoding_name
var FeatureSet_MessageEncoding_value = descriptorpb.FeatureSet_MessageEncoding_value
type FeatureSet_JsonFormat = descriptorpb.FeatureSet_JsonFormat
const FeatureSet_JSON_FORMAT_UNKNOWN = descriptorpb.FeatureSet_JSON_FORMAT_UNKNOWN
const FeatureSet_ALLOW = descriptorpb.FeatureSet_ALLOW
const FeatureSet_LEGACY_BEST_EFFORT = descriptorpb.FeatureSet_LEGACY_BEST_EFFORT
var FeatureSet_JsonFormat_name = descriptorpb.FeatureSet_JsonFormat_name
var FeatureSet_JsonFormat_value = descriptorpb.FeatureSet_JsonFormat_value
type GeneratedCodeInfo_Annotation_Semantic = descriptorpb.GeneratedCodeInfo_Annotation_Semantic
const GeneratedCodeInfo_Annotation_NONE = descriptorpb.GeneratedCodeInfo_Annotation_NONE
const GeneratedCodeInfo_Annotation_SET = descriptorpb.GeneratedCodeInfo_Annotation_SET
const GeneratedCodeInfo_Annotation_ALIAS = descriptorpb.GeneratedCodeInfo_Annotation_ALIAS
var GeneratedCodeInfo_Annotation_Semantic_name = descriptorpb.GeneratedCodeInfo_Annotation_Semantic_name
var GeneratedCodeInfo_Annotation_Semantic_value = descriptorpb.GeneratedCodeInfo_Annotation_Semantic_value
type FileDescriptorSet = descriptorpb.FileDescriptorSet type FileDescriptorSet = descriptorpb.FileDescriptorSet
type FileDescriptorProto = descriptorpb.FileDescriptorProto type FileDescriptorProto = descriptorpb.FileDescriptorProto
type DescriptorProto = descriptorpb.DescriptorProto type DescriptorProto = descriptorpb.DescriptorProto
type ExtensionRangeOptions = descriptorpb.ExtensionRangeOptions type ExtensionRangeOptions = descriptorpb.ExtensionRangeOptions
const Default_ExtensionRangeOptions_Verification = descriptorpb.Default_ExtensionRangeOptions_Verification
type FieldDescriptorProto = descriptorpb.FieldDescriptorProto type FieldDescriptorProto = descriptorpb.FieldDescriptorProto
type OneofDescriptorProto = descriptorpb.OneofDescriptorProto type OneofDescriptorProto = descriptorpb.OneofDescriptorProto
type EnumDescriptorProto = descriptorpb.EnumDescriptorProto type EnumDescriptorProto = descriptorpb.EnumDescriptorProto
@ -103,7 +220,6 @@ const Default_FileOptions_OptimizeFor = descriptorpb.Default_FileOptions_Optimiz
const Default_FileOptions_CcGenericServices = descriptorpb.Default_FileOptions_CcGenericServices const Default_FileOptions_CcGenericServices = descriptorpb.Default_FileOptions_CcGenericServices
const Default_FileOptions_JavaGenericServices = descriptorpb.Default_FileOptions_JavaGenericServices const Default_FileOptions_JavaGenericServices = descriptorpb.Default_FileOptions_JavaGenericServices
const Default_FileOptions_PyGenericServices = descriptorpb.Default_FileOptions_PyGenericServices const Default_FileOptions_PyGenericServices = descriptorpb.Default_FileOptions_PyGenericServices
const Default_FileOptions_PhpGenericServices = descriptorpb.Default_FileOptions_PhpGenericServices
const Default_FileOptions_Deprecated = descriptorpb.Default_FileOptions_Deprecated const Default_FileOptions_Deprecated = descriptorpb.Default_FileOptions_Deprecated
const Default_FileOptions_CcEnableArenas = descriptorpb.Default_FileOptions_CcEnableArenas const Default_FileOptions_CcEnableArenas = descriptorpb.Default_FileOptions_CcEnableArenas
@ -118,8 +234,10 @@ type FieldOptions = descriptorpb.FieldOptions
const Default_FieldOptions_Ctype = descriptorpb.Default_FieldOptions_Ctype const Default_FieldOptions_Ctype = descriptorpb.Default_FieldOptions_Ctype
const Default_FieldOptions_Jstype = descriptorpb.Default_FieldOptions_Jstype const Default_FieldOptions_Jstype = descriptorpb.Default_FieldOptions_Jstype
const Default_FieldOptions_Lazy = descriptorpb.Default_FieldOptions_Lazy const Default_FieldOptions_Lazy = descriptorpb.Default_FieldOptions_Lazy
const Default_FieldOptions_UnverifiedLazy = descriptorpb.Default_FieldOptions_UnverifiedLazy
const Default_FieldOptions_Deprecated = descriptorpb.Default_FieldOptions_Deprecated const Default_FieldOptions_Deprecated = descriptorpb.Default_FieldOptions_Deprecated
const Default_FieldOptions_Weak = descriptorpb.Default_FieldOptions_Weak const Default_FieldOptions_Weak = descriptorpb.Default_FieldOptions_Weak
const Default_FieldOptions_DebugRedact = descriptorpb.Default_FieldOptions_DebugRedact
type OneofOptions = descriptorpb.OneofOptions type OneofOptions = descriptorpb.OneofOptions
type EnumOptions = descriptorpb.EnumOptions type EnumOptions = descriptorpb.EnumOptions
@ -129,6 +247,7 @@ const Default_EnumOptions_Deprecated = descriptorpb.Default_EnumOptions_Deprecat
type EnumValueOptions = descriptorpb.EnumValueOptions type EnumValueOptions = descriptorpb.EnumValueOptions
const Default_EnumValueOptions_Deprecated = descriptorpb.Default_EnumValueOptions_Deprecated const Default_EnumValueOptions_Deprecated = descriptorpb.Default_EnumValueOptions_Deprecated
const Default_EnumValueOptions_DebugRedact = descriptorpb.Default_EnumValueOptions_DebugRedact
type ServiceOptions = descriptorpb.ServiceOptions type ServiceOptions = descriptorpb.ServiceOptions
@ -140,12 +259,17 @@ const Default_MethodOptions_Deprecated = descriptorpb.Default_MethodOptions_Depr
const Default_MethodOptions_IdempotencyLevel = descriptorpb.Default_MethodOptions_IdempotencyLevel const Default_MethodOptions_IdempotencyLevel = descriptorpb.Default_MethodOptions_IdempotencyLevel
type UninterpretedOption = descriptorpb.UninterpretedOption type UninterpretedOption = descriptorpb.UninterpretedOption
type FeatureSet = descriptorpb.FeatureSet
type FeatureSetDefaults = descriptorpb.FeatureSetDefaults
type SourceCodeInfo = descriptorpb.SourceCodeInfo type SourceCodeInfo = descriptorpb.SourceCodeInfo
type GeneratedCodeInfo = descriptorpb.GeneratedCodeInfo type GeneratedCodeInfo = descriptorpb.GeneratedCodeInfo
type DescriptorProto_ExtensionRange = descriptorpb.DescriptorProto_ExtensionRange type DescriptorProto_ExtensionRange = descriptorpb.DescriptorProto_ExtensionRange
type DescriptorProto_ReservedRange = descriptorpb.DescriptorProto_ReservedRange type DescriptorProto_ReservedRange = descriptorpb.DescriptorProto_ReservedRange
type ExtensionRangeOptions_Declaration = descriptorpb.ExtensionRangeOptions_Declaration
type EnumDescriptorProto_EnumReservedRange = descriptorpb.EnumDescriptorProto_EnumReservedRange type EnumDescriptorProto_EnumReservedRange = descriptorpb.EnumDescriptorProto_EnumReservedRange
type FieldOptions_EditionDefault = descriptorpb.FieldOptions_EditionDefault
type UninterpretedOption_NamePart = descriptorpb.UninterpretedOption_NamePart type UninterpretedOption_NamePart = descriptorpb.UninterpretedOption_NamePart
type FeatureSetDefaults_FeatureSetEditionDefault = descriptorpb.FeatureSetDefaults_FeatureSetEditionDefault
type SourceCodeInfo_Location = descriptorpb.SourceCodeInfo_Location type SourceCodeInfo_Location = descriptorpb.SourceCodeInfo_Location
type GeneratedCodeInfo_Annotation = descriptorpb.GeneratedCodeInfo_Annotation type GeneratedCodeInfo_Annotation = descriptorpb.GeneratedCodeInfo_Annotation

View File

@ -9,11 +9,11 @@ It's very fast and supports common extensions.
Tutorial: https://blog.kowalczyk.info/article/cxn3/advanced-markdown-processing-in-go.html Tutorial: https://blog.kowalczyk.info/article/cxn3/advanced-markdown-processing-in-go.html
Code examples: Code examples:
* https://onlinetool.io/goplayground/#txO7hJ-ibeU : basic markdown => HTML * https://tools.arslexis.io/goplayground/#txO7hJ-ibeU : basic markdown => HTML
* https://onlinetool.io/goplayground/#yFRIWRiu-KL : customize HTML renderer * https://tools.arslexis.io/goplayground/#yFRIWRiu-KL : customize HTML renderer
* https://onlinetool.io/goplayground/#2yV5-HDKBUV : modify AST * https://tools.arslexis.io/goplayground/#2yV5-HDKBUV : modify AST
* https://onlinetool.io/goplayground/#9fqKwRbuJ04 : customize parser * https://tools.arslexis.io/goplayground/#9fqKwRbuJ04 : customize parser
* https://onlinetool.io/goplayground/#Bk0zTvrzUDR : syntax highlight * https://tools.arslexis.io/goplayground/#Bk0zTvrzUDR : syntax highlight
Those examples are also in [examples](./examples) directory. Those examples are also in [examples](./examples) directory.
@ -226,7 +226,7 @@ implements the following extensions:
- **Hard line breaks**. With this extension enabled newlines in the input - **Hard line breaks**. With this extension enabled newlines in the input
translates into line breaks in the output. This extension is off by default. translates into line breaks in the output. This extension is off by default.
- **Non blocking space**. With this extension enabled spaces preceeded by a backslash - **Non blocking space**. With this extension enabled spaces preceded by a backslash
in the input translates non-blocking spaces in the output. This extension is off by default. in the input translates non-blocking spaces in the output. This extension is off by default.
- **Smart quotes**. Smartypants-style punctuation substitution is - **Smart quotes**. Smartypants-style punctuation substitution is

View File

@ -191,6 +191,11 @@ func (p *Parser) Block(data []byte) {
// <div> // <div>
// ... // ...
// </div> // </div>
if len(data) == 0 {
continue
}
if data[0] == '<' { if data[0] == '<' {
if i := p.html(data, true); i > 0 { if i := p.html(data, true); i > 0 {
data = data[i:] data = data[i:]
@ -393,7 +398,7 @@ func (p *Parser) AddBlock(n ast.Node) ast.Node {
} }
func (p *Parser) isPrefixHeading(data []byte) bool { func (p *Parser) isPrefixHeading(data []byte) bool {
if data[0] != '#' { if len(data) > 0 && data[0] != '#' {
return false return false
} }

View File

@ -65,6 +65,11 @@ func citation(p *Parser, data []byte, offset int) (int, ast.Node) {
} }
citeType := ast.CitationTypeInformative citeType := ast.CitationTypeInformative
if len(citation) < 2 {
continue
}
j = 1 j = 1
switch citation[j] { switch citation[j] {
case '!': case '!':

View File

@ -736,7 +736,7 @@ func leftAngle(p *Parser, data []byte, offset int) (int, ast.Node) {
} }
// '\\' backslash escape // '\\' backslash escape
var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~^") var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~^$")
func escape(p *Parser, data []byte, offset int) (int, ast.Node) { func escape(p *Parser, data []byte, offset int) (int, ast.Node) {
data = data[offset:] data = data[offset:]

View File

@ -56,7 +56,7 @@ const (
) )
// for each character that triggers a response when parsing inline data. // for each character that triggers a response when parsing inline data.
type inlineParser func(p *Parser, data []byte, offset int) (int, ast.Node) type InlineParser func(p *Parser, data []byte, offset int) (int, ast.Node)
// ReferenceOverrideFunc is expected to be called with a reference string and // ReferenceOverrideFunc is expected to be called with a reference string and
// return either a valid Reference type that the reference string maps to or // return either a valid Reference type that the reference string maps to or
@ -98,7 +98,7 @@ type Parser struct {
refs map[string]*reference refs map[string]*reference
refsRecord map[string]struct{} refsRecord map[string]struct{}
inlineCallback [256]inlineParser inlineCallback [256]InlineParser
nesting int nesting int
maxNesting int maxNesting int
insideLink bool insideLink bool
@ -181,6 +181,12 @@ func NewWithExtensions(extension Extensions) *Parser {
return &p return &p
} }
func (p *Parser) RegisterInline(n byte, fn InlineParser) InlineParser {
prev := p.inlineCallback[n]
p.inlineCallback[n] = fn
return prev
}
func (p *Parser) getRef(refid string) (ref *reference, found bool) { func (p *Parser) getRef(refid string) (ref *reference, found bool) {
if p.ReferenceOverride != nil { if p.ReferenceOverride != nil {
r, overridden := p.ReferenceOverride(refid) r, overridden := p.ReferenceOverride(refid)
@ -901,6 +907,9 @@ func isListItem(d ast.Node) bool {
} }
func NormalizeNewlines(d []byte) []byte { func NormalizeNewlines(d []byte) []byte {
res := make([]byte, len(d))
copy(res, d)
d = res
wi := 0 wi := 0
n := len(d) n := len(d)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {

View File

@ -1,9 +0,0 @@
language: go
go:
- 1.4.3
- 1.5.3
- tip
script:
- go test -v ./...

41
vendor/github.com/google/uuid/CHANGELOG.md generated vendored Normal file
View File

@ -0,0 +1,41 @@
# Changelog
## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16)
### Features
* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3))
### Bug Fixes
* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06))
* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6))
## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12)
### Features
* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29))
## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26)
### Features
* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4))
### Fixes
* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior)
## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18)
### Bug Fixes
* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0))
## Changelog

View File

@ -2,6 +2,22 @@
We definitely welcome patches and contribution to this project! We definitely welcome patches and contribution to this project!
### Tips
Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org).
Always try to include a test case! If it is not possible or not necessary,
please explain why in the pull request description.
### Releasing
Commits that would precipitate a SemVer change, as described in the Conventional
Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action)
to create a release candidate pull request. Once submitted, `release-please`
will create a release.
For tips on how to work with `release-please`, see its documentation.
### Legal requirements ### Legal requirements
In order to protect both you and ourselves, you will need to sign the In order to protect both you and ourselves, you will need to sign the

View File

@ -1,6 +1,6 @@
# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) # uuid
The uuid package generates and inspects UUIDs based on The uuid package generates and inspects UUIDs based on
[RFC 4122](http://tools.ietf.org/html/rfc4122) [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122)
and DCE 1.1: Authentication and Security Services. and DCE 1.1: Authentication and Security Services.
This package is based on the github.com/pborman/uuid package (previously named This package is based on the github.com/pborman/uuid package (previously named
@ -9,10 +9,12 @@ a UUID is a 16 byte array rather than a byte slice. One loss due to this
change is the ability to represent an invalid UUID (vs a NIL UUID). change is the ability to represent an invalid UUID (vs a NIL UUID).
###### Install ###### Install
`go get github.com/google/uuid` ```sh
go get github.com/google/uuid
```
###### Documentation ###### Documentation
[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid) [![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid)
Full `go doc` style documentation for the package can be viewed online without Full `go doc` style documentation for the package can be viewed online without
installing this package by using the GoDoc site here: installing this package by using the GoDoc site here:

View File

@ -17,6 +17,12 @@ var (
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
Nil UUID // empty UUID, all zeros Nil UUID // empty UUID, all zeros
// The Max UUID is special form of UUID that is specified to have all 128 bits set to 1.
Max = UUID{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
}
) )
// NewHash returns a new UUID derived from the hash of space concatenated with // NewHash returns a new UUID derived from the hash of space concatenated with

View File

@ -7,6 +7,6 @@
package uuid package uuid
// getHardwareInterface returns nil values for the JS version of the code. // getHardwareInterface returns nil values for the JS version of the code.
// This remvoves the "net" dependency, because it is not used in the browser. // This removes the "net" dependency, because it is not used in the browser.
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes. // Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
func getHardwareInterface(name string) (string, []byte) { return "", nil } func getHardwareInterface(name string) (string, []byte) { return "", nil }

View File

@ -108,12 +108,23 @@ func setClockSequence(seq int) {
} }
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in // Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
// uuid. The time is only defined for version 1 and 2 UUIDs. // uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs.
func (uuid UUID) Time() Time { func (uuid UUID) Time() Time {
var t Time
switch uuid.Version() {
case 6:
time := binary.BigEndian.Uint64(uuid[:8]) // Ignore uuid[6] version b0110
t = Time(time)
case 7:
time := binary.BigEndian.Uint64(uuid[:8])
t = Time((time>>16)*10000 + g1582ns100)
default: // forward compatible
time := int64(binary.BigEndian.Uint32(uuid[0:4])) time := int64(binary.BigEndian.Uint32(uuid[0:4]))
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
return Time(time) t = Time(time)
}
return t
} }
// ClockSequence returns the clock sequence encoded in uuid. // ClockSequence returns the clock sequence encoded in uuid.

View File

@ -56,11 +56,15 @@ func IsInvalidLengthError(err error) bool {
return ok return ok
} }
// Parse decodes s into a UUID or returns an error. Both the standard UUID // Parse decodes s into a UUID or returns an error if it cannot be parsed. Both
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // the standard UUID forms defined in RFC 4122
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the // (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition,
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. // Parse accepts non-standard strings such as the raw hex encoding
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings,
// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are
// examined in the latter case. Parse should not be used to validate strings as
// it parses non-standard encodings as indicated above.
func Parse(s string) (UUID, error) { func Parse(s string) (UUID, error) {
var uuid UUID var uuid UUID
switch len(s) { switch len(s) {
@ -69,7 +73,7 @@ func Parse(s string) (UUID, error) {
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9: case 36 + 9:
if strings.ToLower(s[:9]) != "urn:uuid:" { if !strings.EqualFold(s[:9], "urn:uuid:") {
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
} }
s = s[9:] s = s[9:]
@ -101,7 +105,8 @@ func Parse(s string) (UUID, error) {
9, 11, 9, 11,
14, 16, 14, 16,
19, 21, 19, 21,
24, 26, 28, 30, 32, 34} { 24, 26, 28, 30, 32, 34,
} {
v, ok := xtob(s[x], s[x+1]) v, ok := xtob(s[x], s[x+1])
if !ok { if !ok {
return uuid, errors.New("invalid UUID format") return uuid, errors.New("invalid UUID format")
@ -117,7 +122,7 @@ func ParseBytes(b []byte) (UUID, error) {
switch len(b) { switch len(b) {
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) {
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
} }
b = b[9:] b = b[9:]
@ -145,7 +150,8 @@ func ParseBytes(b []byte) (UUID, error) {
9, 11, 9, 11,
14, 16, 14, 16,
19, 21, 19, 21,
24, 26, 28, 30, 32, 34} { 24, 26, 28, 30, 32, 34,
} {
v, ok := xtob(b[x], b[x+1]) v, ok := xtob(b[x], b[x+1])
if !ok { if !ok {
return uuid, errors.New("invalid UUID format") return uuid, errors.New("invalid UUID format")
@ -180,6 +186,59 @@ func Must(uuid UUID, err error) UUID {
return uuid return uuid
} }
// Validate returns an error if s is not a properly formatted UUID in one of the following formats:
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
// It returns an error if the format is invalid, otherwise nil.
func Validate(s string) error {
switch len(s) {
// Standard UUID format
case 36:
// UUID with "urn:uuid:" prefix
case 36 + 9:
if !strings.EqualFold(s[:9], "urn:uuid:") {
return fmt.Errorf("invalid urn prefix: %q", s[:9])
}
s = s[9:]
// UUID enclosed in braces
case 36 + 2:
if s[0] != '{' || s[len(s)-1] != '}' {
return fmt.Errorf("invalid bracketed UUID format")
}
s = s[1 : len(s)-1]
// UUID without hyphens
case 32:
for i := 0; i < len(s); i += 2 {
_, ok := xtob(s[i], s[i+1])
if !ok {
return errors.New("invalid UUID format")
}
}
default:
return invalidLengthError{len(s)}
}
// Check for standard UUID format
if len(s) == 36 {
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return errors.New("invalid UUID format")
}
for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} {
if _, ok := xtob(s[x], s[x+1]); !ok {
return errors.New("invalid UUID format")
}
}
}
return nil
}
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// , or "" if uuid is invalid. // , or "" if uuid is invalid.
func (uuid UUID) String() string { func (uuid UUID) String() string {
@ -292,3 +351,15 @@ func DisableRandPool() {
poolMu.Lock() poolMu.Lock()
poolPos = randPoolSize poolPos = randPoolSize
} }
// UUIDs is a slice of UUID types.
type UUIDs []UUID
// Strings returns a string slice containing the string form of each UUID in uuids.
func (uuids UUIDs) Strings() []string {
var uuidStrs = make([]string, len(uuids))
for i, uuid := range uuids {
uuidStrs[i] = uuid.String()
}
return uuidStrs
}

56
vendor/github.com/google/uuid/version6.go generated vendored Normal file
View File

@ -0,0 +1,56 @@
// Copyright 2023 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import "encoding/binary"
// UUID version 6 is a field-compatible version of UUIDv1, reordered for improved DB locality.
// It is expected that UUIDv6 will primarily be used in contexts where there are existing v1 UUIDs.
// Systems that do not involve legacy UUIDv1 SHOULD consider using UUIDv7 instead.
//
// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#uuidv6
//
// NewV6 returns a Version 6 UUID based on the current NodeID and clock
// sequence, and the current time. If the NodeID has not been set by SetNodeID
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
// be set NewV6 set NodeID is random bits automatically . If clock sequence has not been set by
// SetClockSequence then it will be set automatically. If GetTime fails to
// return the current NewV6 returns Nil and an error.
func NewV6() (UUID, error) {
var uuid UUID
now, seq, err := GetTime()
if err != nil {
return uuid, err
}
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time_high |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time_mid | time_low_and_version |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|clk_seq_hi_res | clk_seq_low | node (0-1) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| node (2-5) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
binary.BigEndian.PutUint64(uuid[0:], uint64(now))
binary.BigEndian.PutUint16(uuid[8:], seq)
uuid[6] = 0x60 | (uuid[6] & 0x0F)
uuid[8] = 0x80 | (uuid[8] & 0x3F)
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
copy(uuid[10:], nodeID[:])
nodeMu.Unlock()
return uuid, nil
}

104
vendor/github.com/google/uuid/version7.go generated vendored Normal file
View File

@ -0,0 +1,104 @@
// Copyright 2023 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"io"
)
// UUID version 7 features a time-ordered value field derived from the widely
// implemented and well known Unix Epoch timestamp source,
// the number of milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded.
// As well as improved entropy characteristics over versions 1 or 6.
//
// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#name-uuid-version-7
//
// Implementations SHOULD utilize UUID version 7 over UUID version 1 and 6 if possible.
//
// NewV7 returns a Version 7 UUID based on the current time(Unix Epoch).
// Uses the randomness pool if it was enabled with EnableRandPool.
// On error, NewV7 returns Nil and an error
func NewV7() (UUID, error) {
uuid, err := NewRandom()
if err != nil {
return uuid, err
}
makeV7(uuid[:])
return uuid, nil
}
// NewV7FromReader returns a Version 7 UUID based on the current time(Unix Epoch).
// it use NewRandomFromReader fill random bits.
// On error, NewV7FromReader returns Nil and an error.
func NewV7FromReader(r io.Reader) (UUID, error) {
uuid, err := NewRandomFromReader(r)
if err != nil {
return uuid, err
}
makeV7(uuid[:])
return uuid, nil
}
// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6])
// uuid[8] already has the right version number (Variant is 10)
// see function NewV7 and NewV7FromReader
func makeV7(uuid []byte) {
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms | ver | rand_a (12 bit seq) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
_ = uuid[15] // bounds check
t, s := getV7Time()
uuid[0] = byte(t >> 40)
uuid[1] = byte(t >> 32)
uuid[2] = byte(t >> 24)
uuid[3] = byte(t >> 16)
uuid[4] = byte(t >> 8)
uuid[5] = byte(t)
uuid[6] = 0x70 | (0x0F & byte(s>>8))
uuid[7] = byte(s)
}
// lastV7time is the last time we returned stored as:
//
// 52 bits of time in milliseconds since epoch
// 12 bits of (fractional nanoseconds) >> 8
var lastV7time int64
const nanoPerMilli = 1000000
// getV7Time returns the time in milliseconds and nanoseconds / 256.
// The returned (milli << 12 + seq) is guarenteed to be greater than
// (milli << 12 + seq) returned by any previous call to getV7Time.
func getV7Time() (milli, seq int64) {
timeMu.Lock()
defer timeMu.Unlock()
nano := timeNow().UnixNano()
milli = nano / nanoPerMilli
// Sequence number is between 0 and 3906 (nanoPerMilli>>8)
seq = (nano - milli*nanoPerMilli) >> 8
now := milli<<12 + seq
if now <= lastV7time {
now = lastV7time + 1
milli = now >> 12
seq = now & 0xfff
}
lastV7time = now
return milli, seq
}

20
vendor/github.com/gorilla/schema/.editorconfig generated vendored Normal file
View File

@ -0,0 +1,20 @@
; https://editorconfig.org/
root = true
[*]
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
indent_style = tab
indent_size = 4
[*.md]
indent_size = 4
trim_trailing_whitespace = false
eclint_indent_style = unset

1
vendor/github.com/gorilla/schema/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
coverage.coverprofile

View File

@ -1,4 +1,4 @@
Copyright (c) 2012 Rodrigo Moraes. All rights reserved. Copyright (c) 2023 The Gorilla Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are

34
vendor/github.com/gorilla/schema/Makefile generated vendored Normal file
View File

@ -0,0 +1,34 @@
GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '')
GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest
GO_SEC=$(shell which gosec 2> /dev/null || echo '')
GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest
GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '')
GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest
.PHONY: golangci-lint
golangci-lint:
$(if $(GO_LINT), ,go install $(GO_LINT_URI))
@echo "##### Running golangci-lint"
golangci-lint run -v
.PHONY: gosec
gosec:
$(if $(GO_SEC), ,go install $(GO_SEC_URI))
@echo "##### Running gosec"
gosec ./...
.PHONY: govulncheck
govulncheck:
$(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI))
@echo "##### Running govulncheck"
govulncheck ./...
.PHONY: verify
verify: golangci-lint gosec govulncheck
.PHONY: test
test:
@echo "##### Running tests"
go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./...

View File

@ -1,8 +1,12 @@
schema # gorilla/schema
======
[![GoDoc](https://godoc.org/github.com/gorilla/schema?status.svg)](https://godoc.org/github.com/gorilla/schema) [![Build Status](https://travis-ci.org/gorilla/schema.png?branch=master)](https://travis-ci.org/gorilla/schema)
[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/schema/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/schema?badge)
![testing](https://github.com/gorilla/schema/actions/workflows/test.yml/badge.svg)
[![codecov](https://codecov.io/github/gorilla/schema/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/schema)
[![godoc](https://godoc.org/github.com/gorilla/schema?status.svg)](https://godoc.org/github.com/gorilla/schema)
[![sourcegraph](https://sourcegraph.com/github.com/gorilla/schema/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/schema?badge)
![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5)
Package gorilla/schema converts structs to and from form values. Package gorilla/schema converts structs to and from form values.
@ -83,7 +87,32 @@ The supported field types in the struct are:
Unsupported types are simply ignored, however custom types can be registered to be converted. Unsupported types are simply ignored, however custom types can be registered to be converted.
More examples are available on the Gorilla website: https://www.gorillatoolkit.org/pkg/schema ## Setting Defaults
It is possible to set default values when encoding/decoding by using the `default` tag option. The value of `default` is applied when a field has a zero value, a pointer has a nil value, or a slice is empty.
```go
type Person struct {
Phone string `schema:"phone,default:+123456"` // custom name
Age int `schema:"age,default:21"`
Admin bool `schema:"admin,default:false"`
Balance float64 `schema:"balance,default:10.0"`
Friends []string `schema:friends,default:john|bob`
}
```
The `default` tag option is supported for the following types:
* bool
* float variants (float32, float64)
* int variants (int, int8, int16, int32, int64)
* uint variants (uint, uint8, uint16, uint32, uint64)
* string
* a slice of the above types. As shown in the example above, `|` should be used to separate between slice items.
* a pointer to one of the above types (pointer to slice and slice of pointers are not supported).
> [!NOTE]
> Because primitive types like int, float, bool, unint and their variants have their default (or zero) values set by Golang, it is not possible to distinguish them from a provided value when decoding/encoding form values. In this case, the value provided by the `default` option tag will be always applied. For example, let's assume that the value submitted in the form for `balance` is `0.0` then the default of `10.0` will be applied, even if `0.0` is part of the form data for the `balance` field. In such cases, it is highly recommended to use pointers to allow schema to distinguish between when a form field has no provided value and when a form has a value equal to the corresponding default set by Golang for a particular type. If the type of the `Balance` field above is changed to `*float64`, then the zero value would be `nil`. In this case, if the form data value for `balance` is `0.0`, then the default will not be applied.
## License ## License

View File

@ -12,7 +12,7 @@ import (
"sync" "sync"
) )
var invalidPath = errors.New("schema: invalid path") var errInvalidPath = errors.New("schema: invalid path")
// newCache returns a new cache. // newCache returns a new cache.
func newCache() *cache { func newCache() *cache {
@ -53,13 +53,13 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) {
keys := strings.Split(p, ".") keys := strings.Split(p, ".")
for i := 0; i < len(keys); i++ { for i := 0; i < len(keys); i++ {
if t.Kind() != reflect.Struct { if t.Kind() != reflect.Struct {
return nil, invalidPath return nil, errInvalidPath
} }
if struc = c.get(t); struc == nil { if struc = c.get(t); struc == nil {
return nil, invalidPath return nil, errInvalidPath
} }
if field = struc.get(keys[i]); field == nil { if field = struc.get(keys[i]); field == nil {
return nil, invalidPath return nil, errInvalidPath
} }
// Valid field. Append index. // Valid field. Append index.
path = append(path, field.name) path = append(path, field.name)
@ -72,10 +72,10 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) {
// So checking i+2 is not necessary anymore. // So checking i+2 is not necessary anymore.
i++ i++
if i+1 > len(keys) { if i+1 > len(keys) {
return nil, invalidPath return nil, errInvalidPath
} }
if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil { if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil {
return nil, invalidPath return nil, errInvalidPath
} }
parts = append(parts, pathPart{ parts = append(parts, pathPart{
path: path, path: path,
@ -197,6 +197,7 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel
isSliceOfStructs: isSlice && isStruct, isSliceOfStructs: isSlice && isStruct,
isAnonymous: field.Anonymous, isAnonymous: field.Anonymous,
isRequired: options.Contains("required"), isRequired: options.Contains("required"),
defaultValue: options.getDefaultOptionValue(),
} }
} }
@ -248,6 +249,7 @@ type fieldInfo struct {
// isAnonymous indicates whether the field is embedded in the struct. // isAnonymous indicates whether the field is embedded in the struct.
isAnonymous bool isAnonymous bool
isRequired bool isRequired bool
defaultValue string
} }
func (f *fieldInfo) paths(prefix string) []string { func (f *fieldInfo) paths(prefix string) []string {
@ -303,3 +305,13 @@ func (o tagOptions) Contains(option string) bool {
} }
return false return false
} }
func (o tagOptions) getDefaultOptionValue() string {
for _, s := range o {
if strings.HasPrefix(s, "default:") {
return strings.Split(s, ":")[1]
}
}
return ""
}

View File

@ -143,3 +143,80 @@ func convertUint64(value string) reflect.Value {
} }
return invalidValue return invalidValue
} }
func convertPointer(k reflect.Kind, value string) reflect.Value {
switch k {
case boolType:
if v := convertBool(value); v.IsValid() {
converted := v.Bool()
return reflect.ValueOf(&converted)
}
case float32Type:
if v := convertFloat32(value); v.IsValid() {
converted := float32(v.Float())
return reflect.ValueOf(&converted)
}
case float64Type:
if v := convertFloat64(value); v.IsValid() {
converted := float64(v.Float())
return reflect.ValueOf(&converted)
}
case intType:
if v := convertInt(value); v.IsValid() {
converted := int(v.Int())
return reflect.ValueOf(&converted)
}
case int8Type:
if v := convertInt8(value); v.IsValid() {
converted := int8(v.Int())
return reflect.ValueOf(&converted)
}
case int16Type:
if v := convertInt16(value); v.IsValid() {
converted := int16(v.Int())
return reflect.ValueOf(&converted)
}
case int32Type:
if v := convertInt32(value); v.IsValid() {
converted := int32(v.Int())
return reflect.ValueOf(&converted)
}
case int64Type:
if v := convertInt64(value); v.IsValid() {
converted := int64(v.Int())
return reflect.ValueOf(&converted)
}
case stringType:
if v := convertString(value); v.IsValid() {
converted := v.String()
return reflect.ValueOf(&converted)
}
case uintType:
if v := convertUint(value); v.IsValid() {
converted := uint(v.Uint())
return reflect.ValueOf(&converted)
}
case uint8Type:
if v := convertUint8(value); v.IsValid() {
converted := uint8(v.Uint())
return reflect.ValueOf(&converted)
}
case uint16Type:
if v := convertUint16(value); v.IsValid() {
converted := uint16(v.Uint())
return reflect.ValueOf(&converted)
}
case uint32Type:
if v := convertUint32(value); v.IsValid() {
converted := uint32(v.Uint())
return reflect.ValueOf(&converted)
}
case uint64Type:
if v := convertUint64(value); v.IsValid() {
converted := uint64(v.Uint())
return reflect.ValueOf(&converted)
}
}
return invalidValue
}

View File

@ -84,6 +84,7 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
errors[path] = UnknownKeyError{Key: path} errors[path] = UnknownKeyError{Key: path}
} }
} }
errors.merge(d.setDefaults(t, v))
errors.merge(d.checkRequired(t, src)) errors.merge(d.checkRequired(t, src))
if len(errors) > 0 { if len(errors) > 0 {
return errors return errors
@ -91,6 +92,76 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
return nil return nil
} }
//setDefaults sets the default values when the `default` tag is specified,
//default is supported on basic/primitive types and their pointers,
//nested structs can also have default tags
func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value) MultiError {
struc := d.cache.get(t)
if struc == nil {
// unexpect, cache.get never return nil
return MultiError{"default-" + t.Name(): errors.New("cache fail")}
}
errs := MultiError{}
for _, f := range struc.fields {
vCurrent := v.FieldByName(f.name)
if vCurrent.Type().Kind() == reflect.Struct && f.defaultValue == "" {
errs.merge(d.setDefaults(vCurrent.Type(), vCurrent))
} else if isPointerToStruct(vCurrent) && f.defaultValue == "" {
errs.merge(d.setDefaults(vCurrent.Elem().Type(), vCurrent.Elem()))
}
if f.defaultValue != "" && f.isRequired {
errs.merge(MultiError{"default-" + f.name: errors.New("required fields cannot have a default value")})
} else if f.defaultValue != "" && vCurrent.IsZero() && !f.isRequired {
if f.typ.Kind() == reflect.Struct {
errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")})
} else if f.typ.Kind() == reflect.Slice {
vals := strings.Split(f.defaultValue, "|")
//check if slice has one of the supported types for defaults
if _, ok := builtinConverters[f.typ.Elem().Kind()]; !ok {
errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")})
continue
}
defaultSlice := reflect.MakeSlice(f.typ, 0, cap(vals))
for _, val := range vals {
//this check is to handle if the wrong value is provided
if convertedVal := builtinConverters[f.typ.Elem().Kind()](val); convertedVal.IsValid() {
defaultSlice = reflect.Append(defaultSlice, convertedVal)
}
}
vCurrent.Set(defaultSlice)
} else if f.typ.Kind() == reflect.Ptr {
t1 := f.typ.Elem()
if t1.Kind() == reflect.Struct || t1.Kind() == reflect.Slice {
errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")})
}
//this check is to handle if the wrong value is provided
if convertedVal := convertPointer(t1.Kind(), f.defaultValue); convertedVal.IsValid() {
vCurrent.Set(convertedVal)
}
} else {
//this check is to handle if the wrong value is provided
if convertedVal := builtinConverters[f.typ.Kind()](f.defaultValue); convertedVal.IsValid() {
vCurrent.Set(builtinConverters[f.typ.Kind()](f.defaultValue))
}
}
}
}
return errs
}
func isPointerToStruct(v reflect.Value) bool {
return !v.IsZero() && v.Type().Kind() == reflect.Ptr && v.Elem().Type().Kind() == reflect.Struct
}
// checkRequired checks whether required fields are empty // checkRequired checks whether required fields are empty
// //
// check type t recursively if t has struct fields. // check type t recursively if t has struct fields.
@ -193,7 +264,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
if v.Type().Kind() == reflect.Struct { if v.Type().Kind() == reflect.Struct {
for i := 0; i < v.NumField(); i++ { for i := 0; i < v.NumField(); i++ {
field := v.Field(i) field := v.Field(i)
if field.Type().Kind() == reflect.Ptr && field.IsNil() && v.Type().Field(i).Anonymous == true { if field.Type().Kind() == reflect.Ptr && field.IsNil() && v.Type().Field(i).Anonymous {
field.Set(reflect.New(field.Type().Elem())) field.Set(reflect.New(field.Type().Elem()))
} }
} }

View File

@ -93,8 +93,11 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
} }
// Encode struct pointer types if the field is a valid pointer and a struct. // Encode struct pointer types if the field is a valid pointer and a struct.
if isValidStructPointer(v.Field(i)) { if isValidStructPointer(v.Field(i)) && !e.hasCustomEncoder(v.Field(i).Type()) {
e.encode(v.Field(i).Elem(), dst) err := e.encode(v.Field(i).Elem(), dst)
if err != nil {
errors[v.Field(i).Elem().Type().String()] = err
}
continue continue
} }
@ -112,7 +115,10 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
} }
if v.Field(i).Type().Kind() == reflect.Struct { if v.Field(i).Type().Kind() == reflect.Struct {
e.encode(v.Field(i), dst) err := e.encode(v.Field(i), dst)
if err != nil {
errors[v.Field(i).Type().String()] = err
}
continue continue
} }
@ -142,6 +148,11 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
return nil return nil
} }
func (e *Encoder) hasCustomEncoder(t reflect.Type) bool {
_, exists := e.regenc[t]
return exists
}
func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc { func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc {
if f, ok := reg[t]; ok { if f, ok := reg[t]; ok {
return f return f

View File

@ -20,5 +20,4 @@ _cgo_export.*
_testmain.go _testmain.go
*.exe *.exe
*.bench *.test
go.sum

View File

@ -0,0 +1,46 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0
linters:
fast: false
disable-all: true
enable:
- revive
- megacheck
- govet
- unconvert
- gas
- gocyclo
- dupl
- misspell
- unparam
- unused
- typecheck
- ineffassign
# - stylecheck
- exportloopref
- gocritic
- nakedret
- gosimple
- prealloc
# golangci-lint configuration file
linters-settings:
revive:
ignore-generated-header: true
severity: warning
rules:
- name: package-comments
severity: warning
disabled: true
- name: exported
severity: warning
disabled: false
arguments: ["checkPrivateReceivers", "disableStutteringCheck"]
issues:
exclude-use-default: false
exclude-rules:
- path: _test\.go
linters:
- dupl

267
vendor/github.com/hashicorp/golang-lru/v2/2q.go generated vendored Normal file
View File

@ -0,0 +1,267 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package lru
import (
"errors"
"sync"
"github.com/hashicorp/golang-lru/v2/simplelru"
)
const (
// Default2QRecentRatio is the ratio of the 2Q cache dedicated
// to recently added entries that have only been accessed once.
Default2QRecentRatio = 0.25
// Default2QGhostEntries is the default ratio of ghost
// entries kept to track entries recently evicted
Default2QGhostEntries = 0.50
)
// TwoQueueCache is a thread-safe fixed size 2Q cache.
// 2Q is an enhancement over the standard LRU cache
// in that it tracks both frequently and recently used
// entries separately. This avoids a burst in access to new
// entries from evicting frequently used entries. It adds some
// additional tracking overhead to the standard LRU cache, and is
// computationally about 2x the cost, and adds some metadata over
// head. The ARCCache is similar, but does not require setting any
// parameters.
type TwoQueueCache[K comparable, V any] struct {
size int
recentSize int
recentRatio float64
ghostRatio float64
recent simplelru.LRUCache[K, V]
frequent simplelru.LRUCache[K, V]
recentEvict simplelru.LRUCache[K, struct{}]
lock sync.RWMutex
}
// New2Q creates a new TwoQueueCache using the default
// values for the parameters.
func New2Q[K comparable, V any](size int) (*TwoQueueCache[K, V], error) {
return New2QParams[K, V](size, Default2QRecentRatio, Default2QGhostEntries)
}
// New2QParams creates a new TwoQueueCache using the provided
// parameter values.
func New2QParams[K comparable, V any](size int, recentRatio, ghostRatio float64) (*TwoQueueCache[K, V], error) {
if size <= 0 {
return nil, errors.New("invalid size")
}
if recentRatio < 0.0 || recentRatio > 1.0 {
return nil, errors.New("invalid recent ratio")
}
if ghostRatio < 0.0 || ghostRatio > 1.0 {
return nil, errors.New("invalid ghost ratio")
}
// Determine the sub-sizes
recentSize := int(float64(size) * recentRatio)
evictSize := int(float64(size) * ghostRatio)
// Allocate the LRUs
recent, err := simplelru.NewLRU[K, V](size, nil)
if err != nil {
return nil, err
}
frequent, err := simplelru.NewLRU[K, V](size, nil)
if err != nil {
return nil, err
}
recentEvict, err := simplelru.NewLRU[K, struct{}](evictSize, nil)
if err != nil {
return nil, err
}
// Initialize the cache
c := &TwoQueueCache[K, V]{
size: size,
recentSize: recentSize,
recentRatio: recentRatio,
ghostRatio: ghostRatio,
recent: recent,
frequent: frequent,
recentEvict: recentEvict,
}
return c, nil
}
// Get looks up a key's value from the cache.
func (c *TwoQueueCache[K, V]) Get(key K) (value V, ok bool) {
c.lock.Lock()
defer c.lock.Unlock()
// Check if this is a frequent value
if val, ok := c.frequent.Get(key); ok {
return val, ok
}
// If the value is contained in recent, then we
// promote it to frequent
if val, ok := c.recent.Peek(key); ok {
c.recent.Remove(key)
c.frequent.Add(key, val)
return val, ok
}
// No hit
return
}
// Add adds a value to the cache.
func (c *TwoQueueCache[K, V]) Add(key K, value V) {
c.lock.Lock()
defer c.lock.Unlock()
// Check if the value is frequently used already,
// and just update the value
if c.frequent.Contains(key) {
c.frequent.Add(key, value)
return
}
// Check if the value is recently used, and promote
// the value into the frequent list
if c.recent.Contains(key) {
c.recent.Remove(key)
c.frequent.Add(key, value)
return
}
// If the value was recently evicted, add it to the
// frequently used list
if c.recentEvict.Contains(key) {
c.ensureSpace(true)
c.recentEvict.Remove(key)
c.frequent.Add(key, value)
return
}
// Add to the recently seen list
c.ensureSpace(false)
c.recent.Add(key, value)
}
// ensureSpace is used to ensure we have space in the cache
func (c *TwoQueueCache[K, V]) ensureSpace(recentEvict bool) {
// If we have space, nothing to do
recentLen := c.recent.Len()
freqLen := c.frequent.Len()
if recentLen+freqLen < c.size {
return
}
// If the recent buffer is larger than
// the target, evict from there
if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) {
k, _, _ := c.recent.RemoveOldest()
c.recentEvict.Add(k, struct{}{})
return
}
// Remove from the frequent list otherwise
c.frequent.RemoveOldest()
}
// Len returns the number of items in the cache.
func (c *TwoQueueCache[K, V]) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return c.recent.Len() + c.frequent.Len()
}
// Resize changes the cache size.
func (c *TwoQueueCache[K, V]) Resize(size int) (evicted int) {
c.lock.Lock()
defer c.lock.Unlock()
// Recalculate the sub-sizes
recentSize := int(float64(size) * c.recentRatio)
evictSize := int(float64(size) * c.ghostRatio)
c.size = size
c.recentSize = recentSize
// ensureSpace
diff := c.recent.Len() + c.frequent.Len() - size
if diff < 0 {
diff = 0
}
for i := 0; i < diff; i++ {
c.ensureSpace(true)
}
// Reallocate the LRUs
c.recent.Resize(size)
c.frequent.Resize(size)
c.recentEvict.Resize(evictSize)
return diff
}
// Keys returns a slice of the keys in the cache.
// The frequently used keys are first in the returned slice.
func (c *TwoQueueCache[K, V]) Keys() []K {
c.lock.RLock()
defer c.lock.RUnlock()
k1 := c.frequent.Keys()
k2 := c.recent.Keys()
return append(k1, k2...)
}
// Values returns a slice of the values in the cache.
// The frequently used values are first in the returned slice.
func (c *TwoQueueCache[K, V]) Values() []V {
c.lock.RLock()
defer c.lock.RUnlock()
v1 := c.frequent.Values()
v2 := c.recent.Values()
return append(v1, v2...)
}
// Remove removes the provided key from the cache.
func (c *TwoQueueCache[K, V]) Remove(key K) {
c.lock.Lock()
defer c.lock.Unlock()
if c.frequent.Remove(key) {
return
}
if c.recent.Remove(key) {
return
}
if c.recentEvict.Remove(key) {
return
}
}
// Purge is used to completely clear the cache.
func (c *TwoQueueCache[K, V]) Purge() {
c.lock.Lock()
defer c.lock.Unlock()
c.recent.Purge()
c.frequent.Purge()
c.recentEvict.Purge()
}
// Contains is used to check if the cache contains a key
// without updating recency or frequency.
func (c *TwoQueueCache[K, V]) Contains(key K) bool {
c.lock.RLock()
defer c.lock.RUnlock()
return c.frequent.Contains(key) || c.recent.Contains(key)
}
// Peek is used to inspect the cache value of a key
// without updating recency or frequency.
func (c *TwoQueueCache[K, V]) Peek(key K) (value V, ok bool) {
c.lock.RLock()
defer c.lock.RUnlock()
if val, ok := c.frequent.Peek(key); ok {
return val, ok
}
return c.recent.Peek(key)
}

364
vendor/github.com/hashicorp/golang-lru/v2/LICENSE generated vendored Normal file
View File

@ -0,0 +1,364 @@
Copyright (c) 2014 HashiCorp, Inc.
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

79
vendor/github.com/hashicorp/golang-lru/v2/README.md generated vendored Normal file
View File

@ -0,0 +1,79 @@
golang-lru
==========
This provides the `lru` package which implements a fixed-size
thread safe LRU cache. It is based on the cache in Groupcache.
Documentation
=============
Full docs are available on [Go Packages](https://pkg.go.dev/github.com/hashicorp/golang-lru/v2)
LRU cache example
=================
```go
package main
import (
"fmt"
"github.com/hashicorp/golang-lru/v2"
)
func main() {
l, _ := lru.New[int, any](128)
for i := 0; i < 256; i++ {
l.Add(i, nil)
}
if l.Len() != 128 {
panic(fmt.Sprintf("bad len: %v", l.Len()))
}
}
```
Expirable LRU cache example
===========================
```go
package main
import (
"fmt"
"time"
"github.com/hashicorp/golang-lru/v2/expirable"
)
func main() {
// make cache with 10ms TTL and 5 max keys
cache := expirable.NewLRU[string, string](5, nil, time.Millisecond*10)
// set value under key1.
cache.Add("key1", "val1")
// get value under key1
r, ok := cache.Get("key1")
// check for OK value
if ok {
fmt.Printf("value before expiration is found: %v, value: %q\n", ok, r)
}
// wait for cache to expire
time.Sleep(time.Millisecond * 12)
// get value under key1 after key expiration
r, ok = cache.Get("key1")
fmt.Printf("value after expiration is found: %v, value: %q\n", ok, r)
// set value under key2, would evict old entry because it is already expired.
cache.Add("key2", "val2")
fmt.Printf("Cache len: %d\n", cache.Len())
// Output:
// value before expiration is found: true, value: "val1"
// value after expiration is found: false, value: ""
// Cache len: 1
}
```

24
vendor/github.com/hashicorp/golang-lru/v2/doc.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
// Package lru provides three different LRU caches of varying sophistication.
//
// Cache is a simple LRU cache. It is based on the LRU implementation in
// groupcache: https://github.com/golang/groupcache/tree/master/lru
//
// TwoQueueCache tracks frequently used and recently used entries separately.
// This avoids a burst of accesses from taking out frequently used entries, at
// the cost of about 2x computational overhead and some extra bookkeeping.
//
// ARCCache is an adaptive replacement cache. It tracks recent evictions as well
// as recent usage in both the frequent and recent caches. Its computational
// overhead is comparable to TwoQueueCache, but the memory overhead is linear
// with the size of the cache.
//
// ARC has been patented by IBM, so do not use it if that is problematic for
// your program. For this reason, it is in a separate go module contained within
// this repository.
//
// All caches in this package take locks while operating, and are therefore
// thread-safe for consumers.
package lru

View File

@ -0,0 +1,142 @@
// Copyright 2009 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_list file.
package internal
import "time"
// Entry is an LRU Entry
type Entry[K comparable, V any] struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Entry[K, V]
// The list to which this element belongs.
list *LruList[K, V]
// The LRU Key of this element.
Key K
// The Value stored with this element.
Value V
// The time this element would be cleaned up, optional
ExpiresAt time.Time
// The expiry bucket item was put in, optional
ExpireBucket uint8
}
// PrevEntry returns the previous list element or nil.
func (e *Entry[K, V]) PrevEntry() *Entry[K, V] {
if p := e.prev; e.list != nil && p != &e.list.root {
return p
}
return nil
}
// LruList represents a doubly linked list.
// The zero Value for LruList is an empty list ready to use.
type LruList[K comparable, V any] struct {
root Entry[K, V] // sentinel list element, only &root, root.prev, and root.next are used
len int // current list Length excluding (this) sentinel element
}
// Init initializes or clears list l.
func (l *LruList[K, V]) Init() *LruList[K, V] {
l.root.next = &l.root
l.root.prev = &l.root
l.len = 0
return l
}
// NewList returns an initialized list.
func NewList[K comparable, V any]() *LruList[K, V] { return new(LruList[K, V]).Init() }
// Length returns the number of elements of list l.
// The complexity is O(1).
func (l *LruList[K, V]) Length() int { return l.len }
// Back returns the last element of list l or nil if the list is empty.
func (l *LruList[K, V]) Back() *Entry[K, V] {
if l.len == 0 {
return nil
}
return l.root.prev
}
// lazyInit lazily initializes a zero List Value.
func (l *LruList[K, V]) lazyInit() {
if l.root.next == nil {
l.Init()
}
}
// insert inserts e after at, increments l.len, and returns e.
func (l *LruList[K, V]) insert(e, at *Entry[K, V]) *Entry[K, V] {
e.prev = at
e.next = at.next
e.prev.next = e
e.next.prev = e
e.list = l
l.len++
return e
}
// insertValue is a convenience wrapper for insert(&Entry{Value: v, ExpiresAt: ExpiresAt}, at).
func (l *LruList[K, V]) insertValue(k K, v V, expiresAt time.Time, at *Entry[K, V]) *Entry[K, V] {
return l.insert(&Entry[K, V]{Value: v, Key: k, ExpiresAt: expiresAt}, at)
}
// Remove removes e from its list, decrements l.len
func (l *LruList[K, V]) Remove(e *Entry[K, V]) V {
e.prev.next = e.next
e.next.prev = e.prev
e.next = nil // avoid memory leaks
e.prev = nil // avoid memory leaks
e.list = nil
l.len--
return e.Value
}
// move moves e to next to at.
func (l *LruList[K, V]) move(e, at *Entry[K, V]) {
if e == at {
return
}
e.prev.next = e.next
e.next.prev = e.prev
e.prev = at
e.next = at.next
e.prev.next = e
e.next.prev = e
}
// PushFront inserts a new element e with value v at the front of list l and returns e.
func (l *LruList[K, V]) PushFront(k K, v V) *Entry[K, V] {
l.lazyInit()
return l.insertValue(k, v, time.Time{}, &l.root)
}
// PushFrontExpirable inserts a new expirable element e with Value v at the front of list l and returns e.
func (l *LruList[K, V]) PushFrontExpirable(k K, v V, expiresAt time.Time) *Entry[K, V] {
l.lazyInit()
return l.insertValue(k, v, expiresAt, &l.root)
}
// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *LruList[K, V]) MoveToFront(e *Entry[K, V]) {
if e.list != l || l.root.next == e {
return
}
// see comment in List.Remove about initialization of l
l.move(e, &l.root)
}

250
vendor/github.com/hashicorp/golang-lru/v2/lru.go generated vendored Normal file
View File

@ -0,0 +1,250 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package lru
import (
"sync"
"github.com/hashicorp/golang-lru/v2/simplelru"
)
const (
// DefaultEvictedBufferSize defines the default buffer size to store evicted key/val
DefaultEvictedBufferSize = 16
)
// Cache is a thread-safe fixed size LRU cache.
type Cache[K comparable, V any] struct {
lru *simplelru.LRU[K, V]
evictedKeys []K
evictedVals []V
onEvictedCB func(k K, v V)
lock sync.RWMutex
}
// New creates an LRU of the given size.
func New[K comparable, V any](size int) (*Cache[K, V], error) {
return NewWithEvict[K, V](size, nil)
}
// NewWithEvict constructs a fixed size cache with the given eviction
// callback.
func NewWithEvict[K comparable, V any](size int, onEvicted func(key K, value V)) (c *Cache[K, V], err error) {
// create a cache with default settings
c = &Cache[K, V]{
onEvictedCB: onEvicted,
}
if onEvicted != nil {
c.initEvictBuffers()
onEvicted = c.onEvicted
}
c.lru, err = simplelru.NewLRU(size, onEvicted)
return
}
func (c *Cache[K, V]) initEvictBuffers() {
c.evictedKeys = make([]K, 0, DefaultEvictedBufferSize)
c.evictedVals = make([]V, 0, DefaultEvictedBufferSize)
}
// onEvicted save evicted key/val and sent in externally registered callback
// outside of critical section
func (c *Cache[K, V]) onEvicted(k K, v V) {
c.evictedKeys = append(c.evictedKeys, k)
c.evictedVals = append(c.evictedVals, v)
}
// Purge is used to completely clear the cache.
func (c *Cache[K, V]) Purge() {
var ks []K
var vs []V
c.lock.Lock()
c.lru.Purge()
if c.onEvictedCB != nil && len(c.evictedKeys) > 0 {
ks, vs = c.evictedKeys, c.evictedVals
c.initEvictBuffers()
}
c.lock.Unlock()
// invoke callback outside of critical section
if c.onEvictedCB != nil {
for i := 0; i < len(ks); i++ {
c.onEvictedCB(ks[i], vs[i])
}
}
}
// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *Cache[K, V]) Add(key K, value V) (evicted bool) {
var k K
var v V
c.lock.Lock()
evicted = c.lru.Add(key, value)
if c.onEvictedCB != nil && evicted {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && evicted {
c.onEvictedCB(k, v)
}
return
}
// Get looks up a key's value from the cache.
func (c *Cache[K, V]) Get(key K) (value V, ok bool) {
c.lock.Lock()
value, ok = c.lru.Get(key)
c.lock.Unlock()
return value, ok
}
// Contains checks if a key is in the cache, without updating the
// recent-ness or deleting it for being stale.
func (c *Cache[K, V]) Contains(key K) bool {
c.lock.RLock()
containKey := c.lru.Contains(key)
c.lock.RUnlock()
return containKey
}
// Peek returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *Cache[K, V]) Peek(key K) (value V, ok bool) {
c.lock.RLock()
value, ok = c.lru.Peek(key)
c.lock.RUnlock()
return value, ok
}
// ContainsOrAdd checks if a key is in the cache without updating the
// recent-ness or deleting it for being stale, and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache[K, V]) ContainsOrAdd(key K, value V) (ok, evicted bool) {
var k K
var v V
c.lock.Lock()
if c.lru.Contains(key) {
c.lock.Unlock()
return true, false
}
evicted = c.lru.Add(key, value)
if c.onEvictedCB != nil && evicted {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && evicted {
c.onEvictedCB(k, v)
}
return false, evicted
}
// PeekOrAdd checks if a key is in the cache without updating the
// recent-ness or deleting it for being stale, and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache[K, V]) PeekOrAdd(key K, value V) (previous V, ok, evicted bool) {
var k K
var v V
c.lock.Lock()
previous, ok = c.lru.Peek(key)
if ok {
c.lock.Unlock()
return previous, true, false
}
evicted = c.lru.Add(key, value)
if c.onEvictedCB != nil && evicted {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && evicted {
c.onEvictedCB(k, v)
}
return
}
// Remove removes the provided key from the cache.
func (c *Cache[K, V]) Remove(key K) (present bool) {
var k K
var v V
c.lock.Lock()
present = c.lru.Remove(key)
if c.onEvictedCB != nil && present {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && present {
c.onEvictedCB(k, v)
}
return
}
// Resize changes the cache size.
func (c *Cache[K, V]) Resize(size int) (evicted int) {
var ks []K
var vs []V
c.lock.Lock()
evicted = c.lru.Resize(size)
if c.onEvictedCB != nil && evicted > 0 {
ks, vs = c.evictedKeys, c.evictedVals
c.initEvictBuffers()
}
c.lock.Unlock()
if c.onEvictedCB != nil && evicted > 0 {
for i := 0; i < len(ks); i++ {
c.onEvictedCB(ks[i], vs[i])
}
}
return evicted
}
// RemoveOldest removes the oldest item from the cache.
func (c *Cache[K, V]) RemoveOldest() (key K, value V, ok bool) {
var k K
var v V
c.lock.Lock()
key, value, ok = c.lru.RemoveOldest()
if c.onEvictedCB != nil && ok {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
}
c.lock.Unlock()
if c.onEvictedCB != nil && ok {
c.onEvictedCB(k, v)
}
return
}
// GetOldest returns the oldest entry
func (c *Cache[K, V]) GetOldest() (key K, value V, ok bool) {
c.lock.RLock()
key, value, ok = c.lru.GetOldest()
c.lock.RUnlock()
return
}
// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *Cache[K, V]) Keys() []K {
c.lock.RLock()
keys := c.lru.Keys()
c.lock.RUnlock()
return keys
}
// Values returns a slice of the values in the cache, from oldest to newest.
func (c *Cache[K, V]) Values() []V {
c.lock.RLock()
values := c.lru.Values()
c.lock.RUnlock()
return values
}
// Len returns the number of items in the cache.
func (c *Cache[K, V]) Len() int {
c.lock.RLock()
length := c.lru.Len()
c.lock.RUnlock()
return length
}

View File

@ -1,4 +1,6 @@
Copyright (c) 2017 The CC Authors. All rights reserved. This license applies to simplelru/list.go
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are
@ -10,7 +12,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the in the documentation and/or other materials provided with the
distribution. distribution.
* Neither the names of the authors nor the names of the * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from contributors may be used to endorse or promote products derived from
this software without specific prior written permission. this software without specific prior written permission.

View File

@ -0,0 +1,177 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package simplelru
import (
"errors"
"github.com/hashicorp/golang-lru/v2/internal"
)
// EvictCallback is used to get a callback when a cache entry is evicted
type EvictCallback[K comparable, V any] func(key K, value V)
// LRU implements a non-thread safe fixed size LRU cache
type LRU[K comparable, V any] struct {
size int
evictList *internal.LruList[K, V]
items map[K]*internal.Entry[K, V]
onEvict EvictCallback[K, V]
}
// NewLRU constructs an LRU of the given size
func NewLRU[K comparable, V any](size int, onEvict EvictCallback[K, V]) (*LRU[K, V], error) {
if size <= 0 {
return nil, errors.New("must provide a positive size")
}
c := &LRU[K, V]{
size: size,
evictList: internal.NewList[K, V](),
items: make(map[K]*internal.Entry[K, V]),
onEvict: onEvict,
}
return c, nil
}
// Purge is used to completely clear the cache.
func (c *LRU[K, V]) Purge() {
for k, v := range c.items {
if c.onEvict != nil {
c.onEvict(k, v.Value)
}
delete(c.items, k)
}
c.evictList.Init()
}
// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
// Check for existing item
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
ent.Value = value
return false
}
// Add new item
ent := c.evictList.PushFront(key, value)
c.items[key] = ent
evict := c.evictList.Length() > c.size
// Verify size not exceeded
if evict {
c.removeOldest()
}
return evict
}
// Get looks up a key's value from the cache.
func (c *LRU[K, V]) Get(key K) (value V, ok bool) {
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
return ent.Value, true
}
return
}
// Contains checks if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func (c *LRU[K, V]) Contains(key K) (ok bool) {
_, ok = c.items[key]
return ok
}
// Peek returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *LRU[K, V]) Peek(key K) (value V, ok bool) {
var ent *internal.Entry[K, V]
if ent, ok = c.items[key]; ok {
return ent.Value, true
}
return
}
// Remove removes the provided key from the cache, returning if the
// key was contained.
func (c *LRU[K, V]) Remove(key K) (present bool) {
if ent, ok := c.items[key]; ok {
c.removeElement(ent)
return true
}
return false
}
// RemoveOldest removes the oldest item from the cache.
func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
if ent := c.evictList.Back(); ent != nil {
c.removeElement(ent)
return ent.Key, ent.Value, true
}
return
}
// GetOldest returns the oldest entry
func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) {
if ent := c.evictList.Back(); ent != nil {
return ent.Key, ent.Value, true
}
return
}
// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *LRU[K, V]) Keys() []K {
keys := make([]K, c.evictList.Length())
i := 0
for ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() {
keys[i] = ent.Key
i++
}
return keys
}
// Values returns a slice of the values in the cache, from oldest to newest.
func (c *LRU[K, V]) Values() []V {
values := make([]V, len(c.items))
i := 0
for ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() {
values[i] = ent.Value
i++
}
return values
}
// Len returns the number of items in the cache.
func (c *LRU[K, V]) Len() int {
return c.evictList.Length()
}
// Resize changes the cache size.
func (c *LRU[K, V]) Resize(size int) (evicted int) {
diff := c.Len() - size
if diff < 0 {
diff = 0
}
for i := 0; i < diff; i++ {
c.removeOldest()
}
c.size = size
return diff
}
// removeOldest removes the oldest item from the cache.
func (c *LRU[K, V]) removeOldest() {
if ent := c.evictList.Back(); ent != nil {
c.removeElement(ent)
}
}
// removeElement is used to remove a given list element from the cache
func (c *LRU[K, V]) removeElement(e *internal.Entry[K, V]) {
c.evictList.Remove(e)
delete(c.items, e.Key)
if c.onEvict != nil {
c.onEvict(e.Key, e.Value)
}
}

View File

@ -0,0 +1,46 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
// Package simplelru provides simple LRU implementation based on build-in container/list.
package simplelru
// LRUCache is the interface for simple LRU cache.
type LRUCache[K comparable, V any] interface {
// Adds a value to the cache, returns true if an eviction occurred and
// updates the "recently used"-ness of the key.
Add(key K, value V) bool
// Returns key's value from the cache and
// updates the "recently used"-ness of the key. #value, isFound
Get(key K) (value V, ok bool)
// Checks if a key exists in cache without updating the recent-ness.
Contains(key K) (ok bool)
// Returns key's value without updating the "recently used"-ness of the key.
Peek(key K) (value V, ok bool)
// Removes a key from the cache.
Remove(key K) bool
// Removes the oldest entry from cache.
RemoveOldest() (K, V, bool)
// Returns the oldest entry from the cache. #key, value, isFound
GetOldest() (K, V, bool)
// Returns a slice of the keys in the cache, from oldest to newest.
Keys() []K
// Values returns a slice of the values in the cache, from oldest to newest.
Values() []V
// Returns the number of items in the cache.
Len() int
// Clears all cache entries.
Purge()
// Resizes cache, returning number evicted
Resize(int) int
}

View File

@ -1,19 +0,0 @@
Copyright (C) 2014 Kevin Ballard
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.

View File

@ -1,36 +0,0 @@
PACKAGE
package shellquote
import "github.com/kballard/go-shellquote"
Shellquote provides utilities for joining/splitting strings using sh's
word-splitting rules.
VARIABLES
var (
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
)
FUNCTIONS
func Join(args ...string) string
Join quotes each argument and joins them with a space. If passed to
/bin/sh, the resulting string will be split back into the original
arguments.
func Split(input string) (words []string, err error)
Split splits a string according to /bin/sh's word-splitting rules. It
supports backslash-escapes, single-quotes, and double-quotes. Notably it
does not support the $'' style of quoting. It also doesn't attempt to
perform any other sort of expansion, including brace expansion, shell
expansion, or pathname expansion.
If the given input has an unterminated quoted string or ends in a
backslash-escape, one of UnterminatedSingleQuoteError,
UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.

View File

@ -1,3 +0,0 @@
// Shellquote provides utilities for joining/splitting strings using sh's
// word-splitting rules.
package shellquote

View File

@ -1,102 +0,0 @@
package shellquote
import (
"bytes"
"strings"
"unicode/utf8"
)
// Join quotes each argument and joins them with a space.
// If passed to /bin/sh, the resulting string will be split back into the
// original arguments.
func Join(args ...string) string {
var buf bytes.Buffer
for i, arg := range args {
if i != 0 {
buf.WriteByte(' ')
}
quote(arg, &buf)
}
return buf.String()
}
const (
specialChars = "\\'\"`${[|&;<>()*?!"
extraSpecialChars = " \t\n"
prefixChars = "~"
)
func quote(word string, buf *bytes.Buffer) {
// We want to try to produce a "nice" output. As such, we will
// backslash-escape most characters, but if we encounter a space, or if we
// encounter an extra-special char (which doesn't work with
// backslash-escaping) we switch over to quoting the whole word. We do this
// with a space because it's typically easier for people to read multi-word
// arguments when quoted with a space rather than with ugly backslashes
// everywhere.
origLen := buf.Len()
if len(word) == 0 {
// oops, no content
buf.WriteString("''")
return
}
cur, prev := word, word
atStart := true
for len(cur) > 0 {
c, l := utf8.DecodeRuneInString(cur)
cur = cur[l:]
if strings.ContainsRune(specialChars, c) || (atStart && strings.ContainsRune(prefixChars, c)) {
// copy the non-special chars up to this point
if len(cur) < len(prev) {
buf.WriteString(prev[0 : len(prev)-len(cur)-l])
}
buf.WriteByte('\\')
buf.WriteRune(c)
prev = cur
} else if strings.ContainsRune(extraSpecialChars, c) {
// start over in quote mode
buf.Truncate(origLen)
goto quote
}
atStart = false
}
if len(prev) > 0 {
buf.WriteString(prev)
}
return
quote:
// quote mode
// Use single-quotes, but if we find a single-quote in the word, we need
// to terminate the string, emit an escaped quote, and start the string up
// again
inQuote := false
for len(word) > 0 {
i := strings.IndexRune(word, '\'')
if i == -1 {
break
}
if i > 0 {
if !inQuote {
buf.WriteByte('\'')
inQuote = true
}
buf.WriteString(word[0:i])
}
word = word[i+1:]
if inQuote {
buf.WriteByte('\'')
inQuote = false
}
buf.WriteString("\\'")
}
if len(word) > 0 {
if !inQuote {
buf.WriteByte('\'')
}
buf.WriteString(word)
buf.WriteByte('\'')
}
}

View File

@ -1,156 +0,0 @@
package shellquote
import (
"bytes"
"errors"
"strings"
"unicode/utf8"
)
var (
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
)
var (
splitChars = " \n\t"
singleChar = '\''
doubleChar = '"'
escapeChar = '\\'
doubleEscapeChars = "$`\"\n\\"
)
// Split splits a string according to /bin/sh's word-splitting rules. It
// supports backslash-escapes, single-quotes, and double-quotes. Notably it does
// not support the $'' style of quoting. It also doesn't attempt to perform any
// other sort of expansion, including brace expansion, shell expansion, or
// pathname expansion.
//
// If the given input has an unterminated quoted string or ends in a
// backslash-escape, one of UnterminatedSingleQuoteError,
// UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
func Split(input string) (words []string, err error) {
var buf bytes.Buffer
words = make([]string, 0)
for len(input) > 0 {
// skip any splitChars at the start
c, l := utf8.DecodeRuneInString(input)
if strings.ContainsRune(splitChars, c) {
input = input[l:]
continue
} else if c == escapeChar {
// Look ahead for escaped newline so we can skip over it
next := input[l:]
if len(next) == 0 {
err = UnterminatedEscapeError
return
}
c2, l2 := utf8.DecodeRuneInString(next)
if c2 == '\n' {
input = next[l2:]
continue
}
}
var word string
word, input, err = splitWord(input, &buf)
if err != nil {
return
}
words = append(words, word)
}
return
}
func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
buf.Reset()
raw:
{
cur := input
for len(cur) > 0 {
c, l := utf8.DecodeRuneInString(cur)
cur = cur[l:]
if c == singleChar {
buf.WriteString(input[0 : len(input)-len(cur)-l])
input = cur
goto single
} else if c == doubleChar {
buf.WriteString(input[0 : len(input)-len(cur)-l])
input = cur
goto double
} else if c == escapeChar {
buf.WriteString(input[0 : len(input)-len(cur)-l])
input = cur
goto escape
} else if strings.ContainsRune(splitChars, c) {
buf.WriteString(input[0 : len(input)-len(cur)-l])
return buf.String(), cur, nil
}
}
if len(input) > 0 {
buf.WriteString(input)
input = ""
}
goto done
}
escape:
{
if len(input) == 0 {
return "", "", UnterminatedEscapeError
}
c, l := utf8.DecodeRuneInString(input)
if c == '\n' {
// a backslash-escaped newline is elided from the output entirely
} else {
buf.WriteString(input[:l])
}
input = input[l:]
}
goto raw
single:
{
i := strings.IndexRune(input, singleChar)
if i == -1 {
return "", "", UnterminatedSingleQuoteError
}
buf.WriteString(input[0:i])
input = input[i+1:]
goto raw
}
double:
{
cur := input
for len(cur) > 0 {
c, l := utf8.DecodeRuneInString(cur)
cur = cur[l:]
if c == doubleChar {
buf.WriteString(input[0 : len(input)-len(cur)-l])
input = cur
goto raw
} else if c == escapeChar {
// bash only supports certain escapes in double-quoted strings
c2, l2 := utf8.DecodeRuneInString(cur)
cur = cur[l2:]
if strings.ContainsRune(doubleEscapeChars, c2) {
buf.WriteString(input[0 : len(input)-len(cur)-l-l2])
if c2 == '\n' {
// newline is special, skip the backslash entirely
} else {
buf.WriteRune(c2)
}
input = cur
}
}
}
return "", "", UnterminatedDoubleQuoteError
}
done:
return buf.String(), input, nil
}

View File

@ -3,7 +3,7 @@
before: before:
hooks: hooks:
- ./gen.sh - ./gen.sh
- go install mvdan.cc/garble@v0.9.3 - go install mvdan.cc/garble@v0.10.1
builds: builds:
- -
@ -92,16 +92,7 @@ builds:
archives: archives:
- -
id: s2-binaries id: s2-binaries
name_template: "s2-{{ .Os }}_{{ .Arch }}_{{ .Version }}" name_template: "s2-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
replacements:
aix: AIX
darwin: OSX
linux: Linux
windows: Windows
386: i386
amd64: x86_64
freebsd: FreeBSD
netbsd: NetBSD
format_overrides: format_overrides:
- goos: windows - goos: windows
format: zip format: zip
@ -125,7 +116,7 @@ changelog:
nfpms: nfpms:
- -
file_name_template: "s2_package_{{ .Version }}_{{ .Os }}_{{ .Arch }}" file_name_template: "s2_package__{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
vendor: Klaus Post vendor: Klaus Post
homepage: https://github.com/klauspost/compress homepage: https://github.com/klauspost/compress
maintainer: Klaus Post <klauspost@gmail.com> maintainer: Klaus Post <klauspost@gmail.com>
@ -134,8 +125,3 @@ nfpms:
formats: formats:
- deb - deb
- rpm - rpm
replacements:
darwin: Darwin
linux: Linux
freebsd: FreeBSD
amd64: x86_64

View File

@ -16,6 +16,47 @@ This package provides various compression algorithms.
# changelog # changelog
* July 1st, 2023 - [v1.16.7](https://github.com/klauspost/compress/releases/tag/v1.16.7)
* zstd: Fix default level first dictionary encode https://github.com/klauspost/compress/pull/829
* s2: add GetBufferCapacity() method by @GiedriusS in https://github.com/klauspost/compress/pull/832
* June 13, 2023 - [v1.16.6](https://github.com/klauspost/compress/releases/tag/v1.16.6)
* zstd: correctly ignore WithEncoderPadding(1) by @ianlancetaylor in https://github.com/klauspost/compress/pull/806
* zstd: Add amd64 match length assembly https://github.com/klauspost/compress/pull/824
* gzhttp: Handle informational headers by @rtribotte in https://github.com/klauspost/compress/pull/815
* s2: Improve Better compression slightly https://github.com/klauspost/compress/pull/663
* Apr 16, 2023 - [v1.16.5](https://github.com/klauspost/compress/releases/tag/v1.16.5)
* zstd: readByte needs to use io.ReadFull by @jnoxon in https://github.com/klauspost/compress/pull/802
* gzip: Fix WriterTo after initial read https://github.com/klauspost/compress/pull/804
* Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4)
* zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784
* zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792
* zstd: Fix amd64 not always detecting corrupt data https://github.com/klauspost/compress/pull/785
* zstd: Various minor improvements by @greatroar in https://github.com/klauspost/compress/pull/788 https://github.com/klauspost/compress/pull/794 https://github.com/klauspost/compress/pull/795
* s2: Fix huge block overflow https://github.com/klauspost/compress/pull/779
* s2: Allow CustomEncoder fallback https://github.com/klauspost/compress/pull/780
* gzhttp: Suppport ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799
* Mar 13, 2023 - [v1.16.1](https://github.com/klauspost/compress/releases/tag/v1.16.1)
* zstd: Speed up + improve best encoder by @greatroar in https://github.com/klauspost/compress/pull/776
* gzhttp: Add optional [BREACH mitigation](https://github.com/klauspost/compress/tree/master/gzhttp#breach-mitigation). https://github.com/klauspost/compress/pull/762 https://github.com/klauspost/compress/pull/768 https://github.com/klauspost/compress/pull/769 https://github.com/klauspost/compress/pull/770 https://github.com/klauspost/compress/pull/767
* s2: Add Intel LZ4s converter https://github.com/klauspost/compress/pull/766
* zstd: Minor bug fixes https://github.com/klauspost/compress/pull/771 https://github.com/klauspost/compress/pull/772 https://github.com/klauspost/compress/pull/773
* huff0: Speed up compress1xDo by @greatroar in https://github.com/klauspost/compress/pull/774
* Feb 26, 2023 - [v1.16.0](https://github.com/klauspost/compress/releases/tag/v1.16.0)
* s2: Add [Dictionary](https://github.com/klauspost/compress/tree/master/s2#dictionaries) support. https://github.com/klauspost/compress/pull/685
* s2: Add Compression Size Estimate. https://github.com/klauspost/compress/pull/752
* s2: Add support for custom stream encoder. https://github.com/klauspost/compress/pull/755
* s2: Add LZ4 block converter. https://github.com/klauspost/compress/pull/748
* s2: Support io.ReaderAt in ReadSeeker. https://github.com/klauspost/compress/pull/747
* s2c/s2sx: Use concurrent decoding. https://github.com/klauspost/compress/pull/746
<details>
<summary>See changes to v1.15.x</summary>
* Jan 21st, 2023 (v1.15.15) * Jan 21st, 2023 (v1.15.15)
* deflate: Improve level 7-9 by @klauspost in https://github.com/klauspost/compress/pull/739 * deflate: Improve level 7-9 by @klauspost in https://github.com/klauspost/compress/pull/739
* zstd: Add delta encoding support by @greatroar in https://github.com/klauspost/compress/pull/728 * zstd: Add delta encoding support by @greatroar in https://github.com/klauspost/compress/pull/728
@ -142,6 +183,8 @@ Stream decompression is now faster on asynchronous, since the goroutine allocati
While the release has been extensively tested, it is recommended to testing when upgrading. While the release has been extensively tested, it is recommended to testing when upgrading.
</details>
<details> <details>
<summary>See changes to v1.14.x</summary> <summary>See changes to v1.14.x</summary>
@ -600,6 +643,9 @@ Here are other packages of good quality and pure Go (no cgo wrappers or autoconv
* [github.com/pierrec/lz4](https://github.com/pierrec/lz4) - strong multithreaded LZ4 compression. * [github.com/pierrec/lz4](https://github.com/pierrec/lz4) - strong multithreaded LZ4 compression.
* [github.com/cosnicolaou/pbzip2](https://github.com/cosnicolaou/pbzip2) - multithreaded bzip2 decompression. * [github.com/cosnicolaou/pbzip2](https://github.com/cosnicolaou/pbzip2) - multithreaded bzip2 decompression.
* [github.com/dsnet/compress](https://github.com/dsnet/compress) - brotli decompression, bzip2 writer. * [github.com/dsnet/compress](https://github.com/dsnet/compress) - brotli decompression, bzip2 writer.
* [github.com/ronanh/intcomp](https://github.com/ronanh/intcomp) - Integer compression.
* [github.com/spenczar/fpc](https://github.com/spenczar/fpc) - Float compression.
* [github.com/minio/zipindex](https://github.com/minio/zipindex) - External ZIP directory index.
# license # license

25
vendor/github.com/klauspost/compress/SECURITY.md generated vendored Normal file
View File

@ -0,0 +1,25 @@
# Security Policy
## Supported Versions
Security updates are applied only to the latest release.
## Vulnerability Definition
A security vulnerability is a bug that with certain input triggers a crash or an infinite loop. Most calls will have varying execution time and only in rare cases will slow operation be considered a security vulnerability.
Corrupted output generally is not considered a security vulnerability, unless independent operations are able to affect each other. Note that not all functionality is re-entrant and safe to use concurrently.
Out-of-memory crashes only applies if the en/decoder uses an abnormal amount of memory, with appropriate options applied, to limit maximum window size, concurrency, etc. However, if you are in doubt you are welcome to file a security issue.
It is assumed that all callers are trusted, meaning internal data exposed through reflection or inspection of returned data structures is not considered a vulnerability.
Vulnerabilities resulting from compiler/assembler errors should be reported upstream. Depending on the severity this package may or may not implement a workaround.
## Reporting a Vulnerability
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
Please disclose it at [security advisory](https://github.com/klauspost/compress/security/advisories/new). If possible please provide a minimal reproducer. If the issue only applies to a single platform, it would be helpful to provide access to that.
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base.

View File

@ -152,12 +152,11 @@ func (b *bitWriter) flushAlign() {
// close will write the alignment bit and write the final byte(s) // close will write the alignment bit and write the final byte(s)
// to the output. // to the output.
func (b *bitWriter) close() error { func (b *bitWriter) close() {
// End mark // End mark
b.addBits16Clean(1, 1) b.addBits16Clean(1, 1)
// flush until next byte. // flush until next byte.
b.flushAlign() b.flushAlign()
return nil
} }
// reset and continue writing by appending to out. // reset and continue writing by appending to out.

View File

@ -199,7 +199,8 @@ func (s *Scratch) compress(src []byte) error {
c2.flush(s.actualTableLog) c2.flush(s.actualTableLog)
c1.flush(s.actualTableLog) c1.flush(s.actualTableLog)
return s.bw.close() s.bw.close()
return nil
} }
// writeCount will write the normalized histogram count to header. // writeCount will write the normalized histogram count to header.

View File

@ -260,7 +260,9 @@ func (s *Scratch) buildDtable() error {
// If the buffer is over-read an error is returned. // If the buffer is over-read an error is returned.
func (s *Scratch) decompress() error { func (s *Scratch) decompress() error {
br := &s.bits br := &s.bits
br.init(s.br.unread()) if err := br.init(s.br.unread()); err != nil {
return err
}
var s1, s2 decoder var s1, s2 decoder
// Initialize and decode first state and symbol. // Initialize and decode first state and symbol.

View File

@ -13,14 +13,6 @@ type bitWriter struct {
out []byte out []byte
} }
// bitMask16 is bitmasks. Has extra to avoid bounds check.
var bitMask16 = [32]uint16{
0, 1, 3, 7, 0xF, 0x1F,
0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF,
0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF} /* up to 16 bits */
// addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. // addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated.
// It will not check if there is space for them, so the caller must ensure that it has flushed recently. // It will not check if there is space for them, so the caller must ensure that it has flushed recently.
func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { func (b *bitWriter) addBits16Clean(value uint16, bits uint8) {
@ -60,6 +52,22 @@ func (b *bitWriter) encTwoSymbols(ct cTable, av, bv byte) {
b.nBits += encA.nBits + encB.nBits b.nBits += encA.nBits + encB.nBits
} }
// encFourSymbols adds up to 32 bits from four symbols.
// It will not check if there is space for them,
// so the caller must ensure that b has been flushed recently.
func (b *bitWriter) encFourSymbols(encA, encB, encC, encD cTableEntry) {
bitsA := encA.nBits
bitsB := bitsA + encB.nBits
bitsC := bitsB + encC.nBits
bitsD := bitsC + encD.nBits
combined := uint64(encA.val) |
(uint64(encB.val) << (bitsA & 63)) |
(uint64(encC.val) << (bitsB & 63)) |
(uint64(encD.val) << (bitsC & 63))
b.bitContainer |= combined << (b.nBits & 63)
b.nBits += bitsD
}
// flush32 will flush out, so there are at least 32 bits available for writing. // flush32 will flush out, so there are at least 32 bits available for writing.
func (b *bitWriter) flush32() { func (b *bitWriter) flush32() {
if b.nBits < 32 { if b.nBits < 32 {
@ -86,10 +94,9 @@ func (b *bitWriter) flushAlign() {
// close will write the alignment bit and write the final byte(s) // close will write the alignment bit and write the final byte(s)
// to the output. // to the output.
func (b *bitWriter) close() error { func (b *bitWriter) close() {
// End mark // End mark
b.addBits16Clean(1, 1) b.addBits16Clean(1, 1)
// flush until next byte. // flush until next byte.
b.flushAlign() b.flushAlign()
return nil
} }

View File

@ -227,10 +227,10 @@ func EstimateSizes(in []byte, s *Scratch) (tableSz, dataSz, reuseSz int, err err
} }
func (s *Scratch) compress1X(src []byte) ([]byte, error) { func (s *Scratch) compress1X(src []byte) ([]byte, error) {
return s.compress1xDo(s.Out, src) return s.compress1xDo(s.Out, src), nil
} }
func (s *Scratch) compress1xDo(dst, src []byte) ([]byte, error) { func (s *Scratch) compress1xDo(dst, src []byte) []byte {
var bw = bitWriter{out: dst} var bw = bitWriter{out: dst}
// N is length divisible by 4. // N is length divisible by 4.
@ -248,8 +248,7 @@ func (s *Scratch) compress1xDo(dst, src []byte) ([]byte, error) {
tmp := src[n : n+4] tmp := src[n : n+4]
// tmp should be len 4 // tmp should be len 4
bw.flush32() bw.flush32()
bw.encTwoSymbols(cTable, tmp[3], tmp[2]) bw.encFourSymbols(cTable[tmp[3]], cTable[tmp[2]], cTable[tmp[1]], cTable[tmp[0]])
bw.encTwoSymbols(cTable, tmp[1], tmp[0])
} }
} else { } else {
for ; n >= 0; n -= 4 { for ; n >= 0; n -= 4 {
@ -261,8 +260,8 @@ func (s *Scratch) compress1xDo(dst, src []byte) ([]byte, error) {
bw.encTwoSymbols(cTable, tmp[1], tmp[0]) bw.encTwoSymbols(cTable, tmp[1], tmp[0])
} }
} }
err := bw.close() bw.close()
return bw.out, err return bw.out
} }
var sixZeros [6]byte var sixZeros [6]byte
@ -284,12 +283,8 @@ func (s *Scratch) compress4X(src []byte) ([]byte, error) {
} }
src = src[len(toDo):] src = src[len(toDo):]
var err error
idx := len(s.Out) idx := len(s.Out)
s.Out, err = s.compress1xDo(s.Out, toDo) s.Out = s.compress1xDo(s.Out, toDo)
if err != nil {
return nil, err
}
if len(s.Out)-idx > math.MaxUint16 { if len(s.Out)-idx > math.MaxUint16 {
// We cannot store the size in the jump table // We cannot store the size in the jump table
return nil, ErrIncompressible return nil, ErrIncompressible
@ -316,7 +311,6 @@ func (s *Scratch) compress4Xp(src []byte) ([]byte, error) {
segmentSize := (len(src) + 3) / 4 segmentSize := (len(src) + 3) / 4
var wg sync.WaitGroup var wg sync.WaitGroup
var errs [4]error
wg.Add(4) wg.Add(4)
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
toDo := src toDo := src
@ -327,15 +321,12 @@ func (s *Scratch) compress4Xp(src []byte) ([]byte, error) {
// Separate goroutine for each block. // Separate goroutine for each block.
go func(i int) { go func(i int) {
s.tmpOut[i], errs[i] = s.compress1xDo(s.tmpOut[i][:0], toDo) s.tmpOut[i] = s.compress1xDo(s.tmpOut[i][:0], toDo)
wg.Done() wg.Done()
}(i) }(i)
} }
wg.Wait() wg.Wait()
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
if errs[i] != nil {
return nil, errs[i]
}
o := s.tmpOut[i] o := s.tmpOut[i]
if len(o) > math.MaxUint16 { if len(o) > math.MaxUint16 {
// We cannot store the size in the jump table // We cannot store the size in the jump table

View File

@ -253,7 +253,7 @@ func (d *Decoder) decompress1X8Bit(dst, src []byte) ([]byte, error) {
switch d.actualTableLog { switch d.actualTableLog {
case 8: case 8:
const shift = 8 - 8 const shift = 0
for br.off >= 4 { for br.off >= 4 {
br.fillFast() br.fillFast()
v := dt[uint8(br.value>>(56+shift))] v := dt[uint8(br.value>>(56+shift))]

View File

@ -87,18 +87,6 @@ func emitCopy(dst []byte, offset, length int) int {
return i + 2 return i + 2
} }
// extendMatch returns the largest k such that k <= len(src) and that
// src[i:i+k-j] and src[j:k] have the same contents.
//
// It assumes that:
//
// 0 <= i && i < j && j <= len(src)
func extendMatch(src []byte, i, j int) int {
for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 {
}
return j
}
func hash(u, shift uint32) uint32 { func hash(u, shift uint32) uint32 {
return (u * 0x1e35a7bd) >> shift return (u * 0x1e35a7bd) >> shift
} }

File diff suppressed because it is too large Load Diff

View File

@ -106,6 +106,25 @@ func MakeDict(data []byte, searchStart []byte) *Dict {
return &d return &d
} }
// MakeDictManual will create a dictionary.
// 'data' must be at least MinDictSize and less than or equal to MaxDictSize.
// A manual first repeat index into data must be provided.
// It must be less than len(data)-8.
func MakeDictManual(data []byte, firstIdx uint16) *Dict {
if len(data) < MinDictSize || int(firstIdx) >= len(data)-8 || len(data) > MaxDictSize {
return nil
}
var d Dict
dict := data
d.dict = dict
if cap(d.dict) < len(d.dict)+16 {
d.dict = append(make([]byte, 0, len(d.dict)+16), d.dict...)
}
d.repeat = int(firstIdx)
return &d
}
// Encode returns the encoded form of src. The returned slice may be a sub- // Encode returns the encoded form of src. The returned slice may be a sub-
// slice of dst if dst was large enough to hold the entire encoded block. // slice of dst if dst was large enough to hold the entire encoded block.
// Otherwise, a newly allocated slice will be returned. // Otherwise, a newly allocated slice will be returned.

File diff suppressed because it is too large Load Diff

View File

@ -742,7 +742,6 @@ searchDict:
x := load64(src, s-2) x := load64(src, s-2)
m2Hash := hash6(x, tableBits) m2Hash := hash6(x, tableBits)
currHash := hash6(x>>8, tableBits) currHash := hash6(x>>8, tableBits)
candidate = int(table[currHash])
table[m2Hash] = uint32(s - 2) table[m2Hash] = uint32(s - 2)
table[currHash] = uint32(s - 1) table[currHash] = uint32(s - 1)
cv = load64(src, s) cv = load64(src, s)

View File

@ -157,7 +157,6 @@ func encodeBlockBetterGo(dst, src []byte) (d int) {
index0 := base + 1 index0 := base + 1
index1 := s - 2 index1 := s - 2
cv = load64(src, s)
for index0 < index1 { for index0 < index1 {
cv0 := load64(src, index0) cv0 := load64(src, index0)
cv1 := load64(src, index1) cv1 := load64(src, index1)
@ -269,18 +268,21 @@ func encodeBlockBetterGo(dst, src []byte) (d int) {
lTable[hash7(cv0, lTableBits)] = uint32(index0) lTable[hash7(cv0, lTableBits)] = uint32(index0)
sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1) sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
// lTable could be postponed, but very minor difference.
lTable[hash7(cv1, lTableBits)] = uint32(index1) lTable[hash7(cv1, lTableBits)] = uint32(index1)
sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1) sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
index0 += 1 index0 += 1
index1 -= 1 index1 -= 1
cv = load64(src, s) cv = load64(src, s)
// index every second long in between. // Index large values sparsely in between.
for index0 < index1 { // We do two starting from different offsets for speed.
index2 := (index0 + index1 + 1) >> 1
for index2 < index1 {
lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2)
index0 += 2 index0 += 2
index1 -= 2 index2 += 2
} }
} }
@ -459,12 +461,14 @@ func encodeBlockBetterSnappyGo(dst, src []byte) (d int) {
index1 -= 1 index1 -= 1
cv = load64(src, s) cv = load64(src, s)
// index every second long in between. // Index large values sparsely in between.
for index0 < index1 { // We do two starting from different offsets for speed.
index2 := (index0 + index1 + 1) >> 1
for index2 < index1 {
lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2)
index0 += 2 index0 += 2
index1 -= 2 index2 += 2
} }
} }
@ -599,7 +603,6 @@ searchDict:
if s >= sLimit { if s >= sLimit {
break searchDict break searchDict
} }
cv = load64(src, s)
// Index in-between // Index in-between
index0 := base + 1 index0 := base + 1
index1 := s - 2 index1 := s - 2
@ -865,12 +868,14 @@ searchDict:
index1 -= 1 index1 -= 1
cv = load64(src, s) cv = load64(src, s)
// index every second long in between. // Index large values sparsely in between.
for index0 < index1 { // We do two starting from different offsets for speed.
index2 := (index0 + index1 + 1) >> 1
for index2 < index1 {
lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2)
index0 += 2 index0 += 2
index1 -= 2 index2 += 2
} }
} }
@ -961,7 +966,6 @@ searchDict:
index0 := base + 1 index0 := base + 1
index1 := s - 2 index1 := s - 2
cv = load64(src, s)
for index0 < index1 { for index0 < index1 {
cv0 := load64(src, index0) cv0 := load64(src, index0)
cv1 := load64(src, index1) cv1 := load64(src, index1)
@ -1079,12 +1083,14 @@ searchDict:
index1 -= 1 index1 -= 1
cv = load64(src, s) cv = load64(src, s)
// index every second long in between. // Index large values sparsely in between.
for index0 < index1 { // We do two starting from different offsets for speed.
index2 := (index0 + index1 + 1) >> 1
for index2 < index1 {
lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0) lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1) lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2)
index0 += 2 index0 += 2
index1 -= 2 index2 += 2
} }
} }

View File

@ -717,3 +717,11 @@ func cvtLZ4BlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) {
func cvtLZ4BlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) { func cvtLZ4BlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) {
panic("cvtLZ4BlockSnappyAsm should be unreachable") panic("cvtLZ4BlockSnappyAsm should be unreachable")
} }
func cvtLZ4sBlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) {
panic("cvtLZ4sBlockAsm should be unreachable")
}
func cvtLZ4sBlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) {
panic("cvtLZ4sBlockSnappyAsm should be unreachable")
}

View File

@ -212,7 +212,17 @@ func matchLen(a []byte, b []byte) int
//go:noescape //go:noescape
func cvtLZ4BlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) func cvtLZ4BlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int)
// cvtLZ4Block converts an LZ4 block to S2 // cvtLZ4sBlock converts an LZ4s block to S2
//
//go:noescape
func cvtLZ4sBlockAsm(dst []byte, src []byte) (uncompressed int, dstUsed int)
// cvtLZ4Block converts an LZ4 block to Snappy
// //
//go:noescape //go:noescape
func cvtLZ4BlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int) func cvtLZ4BlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int)
// cvtLZ4sBlock converts an LZ4s block to Snappy
//
//go:noescape
func cvtLZ4sBlockSnappyAsm(dst []byte, src []byte) (uncompressed int, dstUsed int)

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More