Update dependencies

This update includes a newer version of the RTMP server that supports
the enhances RTMP specification, i.e. HEVC, VP9, and AV1.
This commit is contained in:
Ingo Oppermann 2024-01-12 12:35:07 +01:00
parent d1f3538217
commit 574ebdf277
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
562 changed files with 14318 additions and 16394 deletions

78
go.mod
View File

@ -3,33 +3,33 @@ module github.com/datarhei/core/v16
go 1.18 go 1.18
require ( require (
github.com/99designs/gqlgen v0.17.36 github.com/99designs/gqlgen v0.17.42
github.com/Masterminds/semver/v3 v3.2.1 github.com/Masterminds/semver/v3 v3.2.1
github.com/atrox/haikunatorgo/v2 v2.0.1 github.com/atrox/haikunatorgo/v2 v2.0.1
github.com/caddyserver/certmagic v0.19.2 github.com/caddyserver/certmagic v0.20.0
github.com/datarhei/gosrt v0.5.5 github.com/datarhei/gosrt v0.5.5
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a github.com/datarhei/joy4 v0.0.0-20240103155326-704dff2c27fb
github.com/go-playground/validator/v10 v10.15.3 github.com/go-playground/validator/v10 v10.16.0
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/golang-jwt/jwt/v5 v5.0.0 github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/uuid v1.3.1 github.com/google/uuid v1.5.0
github.com/invopop/jsonschema v0.4.0 github.com/invopop/jsonschema v0.4.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/labstack/echo-jwt v0.0.0-20221127215225-c84d41a71003 github.com/labstack/echo-jwt v0.0.0-20221127215225-c84d41a71003
github.com/labstack/echo/v4 v4.11.1 github.com/labstack/echo/v4 v4.11.4
github.com/lithammer/shortuuid/v4 v4.0.0 github.com/lithammer/shortuuid/v4 v4.0.0
github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-isatty v0.0.20
github.com/minio/minio-go/v7 v7.0.63 github.com/minio/minio-go/v7 v7.0.66
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3 github.com/prep/average v0.0.0-20200506183628-d26c465f48c3
github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_golang v1.18.0
github.com/shirou/gopsutil/v3 v3.23.8 github.com/shirou/gopsutil/v3 v3.23.12
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/swaggo/echo-swagger v1.4.1 github.com/swaggo/echo-swagger v1.4.1
github.com/swaggo/swag v1.16.2 github.com/swaggo/swag v1.16.2
github.com/vektah/gqlparser/v2 v2.5.8 github.com/vektah/gqlparser/v2 v2.5.10
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0
go.uber.org/zap v1.25.0 go.uber.org/zap v1.26.0
golang.org/x/mod v0.12.0 golang.org/x/mod v0.14.0
) )
require ( require (
@ -41,34 +41,33 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/ghodss/yaml v1.0.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/spec v0.20.9 // indirect github.com/go-openapi/spec v0.20.14 // indirect
github.com/go-openapi/swag v0.22.4 // indirect github.com/go-openapi/swag v0.22.7 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/gorilla/websocket v1.5.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.6 // indirect
github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/labstack/gommon v0.4.0 // indirect github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/libdns/libdns v0.2.1 // indirect github.com/libdns/libdns v0.2.1 // indirect
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mholt/acmez v1.2.0 // indirect github.com/mholt/acmez v1.2.0 // indirect
github.com/miekg/dns v1.1.55 // indirect github.com/miekg/dns v1.1.57 // indirect
github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
@ -76,16 +75,17 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect github.com/prometheus/procfs v0.12.0 // indirect
github.com/rs/xid v1.5.0 // indirect github.com/rs/xid v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sosodev/duration v1.2.0 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.7.0 // indirect
github.com/urfave/cli/v2 v2.25.5 // indirect github.com/urfave/cli/v2 v2.25.5 // 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
@ -95,13 +95,13 @@ require (
github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.15.0 // indirect golang.org/x/crypto v0.18.0 // indirect
golang.org/x/net v0.14.0 // indirect golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.14.0 // indirect golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.12.0 // indirect golang.org/x/tools v0.17.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.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

206
go.sum
View File

@ -1,33 +1,32 @@
github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go= github.com/99designs/gqlgen v0.17.42 h1:BVWDOb2VVHQC5k3m6oa0XhDnxltLLrU4so7x/u39Zu4=
github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4= github.com/99designs/gqlgen v0.17.42/go.mod h1:GQ6SyMhwFbgHR0a8r2Wn8fYgEwPxxmndLFPhU63+cJE=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/atrox/haikunatorgo/v2 v2.0.1 h1:FCVx2KL2YvZtI1rI9WeEHxeLRrKGr0Dd4wfCJiUXupc= github.com/atrox/haikunatorgo/v2 v2.0.1 h1:FCVx2KL2YvZtI1rI9WeEHxeLRrKGr0Dd4wfCJiUXupc=
github.com/atrox/haikunatorgo/v2 v2.0.1/go.mod h1:BBQmx2o+1Z5poziaHRgddAZKOpijwfKdAmMnSYlFK70= github.com/atrox/haikunatorgo/v2 v2.0.1/go.mod h1:BBQmx2o+1Z5poziaHRgddAZKOpijwfKdAmMnSYlFK70=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYhJeJ2aZxADI2tGADS15AzIF8MQ8XAhT4= github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYhJeJ2aZxADI2tGADS15AzIF8MQ8XAhT4=
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI= github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0= github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8= github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/datarhei/gosrt v0.5.5 h1:4Xx4v7pn/rz6EaWhZRE37fROW+rry0y4yBHZBkq5d8g= github.com/datarhei/gosrt v0.5.5 h1:4Xx4v7pn/rz6EaWhZRE37fROW+rry0y4yBHZBkq5d8g=
github.com/datarhei/gosrt v0.5.5/go.mod h1:In1zba2/999S1d+ENIJ0h9s3alHO3FvCNrIpykxYbEE= github.com/datarhei/gosrt v0.5.5/go.mod h1:In1zba2/999S1d+ENIJ0h9s3alHO3FvCNrIpykxYbEE=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo= github.com/datarhei/joy4 v0.0.0-20240103155326-704dff2c27fb h1:zzF0wtKJKMIDlqHfjBl5WgTFO3LeKBoJNInyOrkJZPc=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw= github.com/datarhei/joy4 v0.0.0-20240103155326-704dff2c27fb/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw=
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 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=
@ -35,59 +34,48 @@ github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0=
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo= github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA= github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
@ -99,25 +87,20 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
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.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
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/labstack/echo-jwt v0.0.0-20221127215225-c84d41a71003 h1:FyalHKl9hnJvhNbrABJXXjC2hG7gvIF0ioW9i0xHNQU= github.com/labstack/echo-jwt v0.0.0-20221127215225-c84d41a71003 h1:FyalHKl9hnJvhNbrABJXXjC2hG7gvIF0ioW9i0xHNQU=
github.com/labstack/echo-jwt v0.0.0-20221127215225-c84d41a71003/go.mod h1:ovRFgyKvi73jQIFCWz9ByQwzhIyohkzY0MFAlPGyr8Q= github.com/labstack/echo-jwt v0.0.0-20221127215225-c84d41a71003/go.mod h1:ovRFgyKvi73jQIFCWz9ByQwzhIyohkzY0MFAlPGyr8Q=
github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
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/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
@ -125,30 +108,25 @@ github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSO
github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik= github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0=
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ= github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw=
github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4= github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@ -158,7 +136,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
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 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -167,40 +144,38 @@ github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3g
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3 h1:Y7qCvg282QmlyrVQuL2fgGwebuw7zvfnRym09r+dUGc= github.com/prep/average v0.0.0-20200506183628-d26c465f48c3 h1:Y7qCvg282QmlyrVQuL2fgGwebuw7zvfnRym09r+dUGc=
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3/go.mod h1:0ZE5gcyWKS151WBDIpmLshHY0l+3edpuKnBUWVVbWKk= github.com/prep/average v0.0.0-20200506183628-d26c465f48c3/go.mod h1:0ZE5gcyWKS151WBDIpmLshHY0l+3edpuKnBUWVVbWKk=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= 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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE= github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sosodev/duration v1.2.0 h1:pqK/FLSjsAADWY74SyWDCjOcd5l7H8GSnnOGEB9A1Us=
github.com/sosodev/duration v1.2.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/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.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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 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=
@ -210,19 +185,20 @@ github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4= github.com/vektah/gqlparser/v2 v2.5.10 h1:6zSM4azXC9u4Nxy5YmdmGu4uKamfwsdKTwp5zsEealU=
github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME= github.com/vektah/gqlparser/v2 v2.5.10/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -243,21 +219,17 @@ github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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-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-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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -265,32 +237,24 @@ 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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

Binary file not shown.

View File

@ -5,10 +5,484 @@ 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
<a name="unreleased"></a> <a name="unreleased"></a>
## [Unreleased](https://github.com/99designs/gqlgen/compare/v0.17.35...HEAD) ## [Unreleased](https://github.com/99designs/gqlgen/compare/v0.17.41...HEAD)
<!-- end of if --> <!-- end of if -->
<!-- end of CommitGroups --> <!-- end of CommitGroups -->
<a name="v0.17.41"></a>
## [v0.17.41](https://github.com/99designs/gqlgen/compare/v0.17.40...v0.17.41) - 2023-12-03
- <a href="https://github.com/99designs/gqlgen/commit/fe60938c55308b1cd5562556cdb976771cfcc6cc"><tt>fe60938c</tt></a> release v0.17.41
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/5e98a16a3a5a5678f1b6481275d81f52f9462f90"><tt>5e98a16a</tt></a> fix fieldset.New bug when prefix slice has len < cap (<a href="https://github.com/99designs/gqlgen/pull/2851">#2851</a>)</summary>
* fix fieldset.New bug when prefix slice has len < cap
* ignore gocritic warning
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/bd9657f3e50b7b9642c05039a5364ce2262faaf4"><tt>bd9657f3</tt></a> Improve ResolverImplementer.Implment (<a href="https://github.com/99designs/gqlgen/pull/2850">#2850</a>)</summary>
* improve resolver implement render
* add error when multiple implementors
* add initial test
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/cb3c1c890e5a255776df9577c80b0c15218cf254"><tt>cb3c1c89</tt></a> Updated apollo sandbox (<a href="https://github.com/99designs/gqlgen/pull/2849">#2849</a>)</summary>
Added all supported options to new window.EmbeddedSandbox object
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/eb5cea7206767bda3582040fd9e4c98174aaa6b1"><tt>eb5cea72</tt></a> Small template fix to save space in the generated file (<a href="https://github.com/99designs/gqlgen/pull/2841">#2841</a>)</summary>
* Small template fix to save space in the generated file
* Re-generate
---------
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/c0ca5091a10417c34192da4d3c064a0fed2a7fdb"><tt>c0ca5091</tt></a> Omittable can now be serialized as json (<a href="https://github.com/99designs/gqlgen/pull/2839">#2839</a>)
- <a href="https://github.com/99designs/gqlgen/commit/dcb7619111642cc82a21b8e80ce1300213af1368"><tt>dcb76191</tt></a> fix: sample program indentation (<a href="https://github.com/99designs/gqlgen/pull/2840">#2840</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/132ec1ce579e9ce3dc772afebf9703c1403d588e"><tt>132ec1ce</tt></a> Updated GraphiQL 3.0.1 => 3.0.6 (<a href="https://github.com/99designs/gqlgen/pull/2837">#2837</a>)</summary>
* Updated GraphiQL 3.0.1 => 3.0.6
* Added unit tests to cover integrity of playgrounds
* Updated vulnerable dependency
* Close response body
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/917407005eb4198aa43875984fb77caeaa7fca36"><tt>91740700</tt></a> v0.17.40 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.40"></a>
## [v0.17.40](https://github.com/99designs/gqlgen/compare/v0.17.39...v0.17.40) - 2023-10-24
- <a href="https://github.com/99designs/gqlgen/commit/c5ad14bf6c4203cefee7888e35e1f07e3a46e77a"><tt>c5ad14bf</tt></a> release v0.17.40
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/74e918f9271dbd045a0425e1f6ddb4804e30404b"><tt>74e918f9</tt></a> Map based input types fields are now coerced to the right type (<a href="https://github.com/99designs/gqlgen/pull/2830">#2830</a>)</summary>
* Input maps now unmarshals and checks nested fields
* Added unit tests
* Tested required fields in input maps
* Docs updated with disclaimer
* Added test for nested inputs
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/1e5fa72a260d2313cf2d2f3315c5f885aa209acd"><tt>1e5fa72a</tt></a> Bump [@babel](https://github.com/babel)/traverse from 7.22.5 to 7.23.2 in /integration (<a href="https://github.com/99designs/gqlgen/pull/2831">#2831</a>)</summary>
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)
---
updated-dependencies:
dependency-type: indirect
...
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/e5c17d63948dd20879e4a9db68863689842ab49a"><tt>e5c17d63</tt></a> resolver: fix case-insensitive file name collision (<a href="https://github.com/99designs/gqlgen/pull/2829">#2829</a>)</summary>
for compatibility with Windows/Mac OS X, Go does not allow files with
identical case-insensitive names.
this commit changes the key of the 'files' map to use the lower case
file name, keeping the original file name as a property on the File
object.
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/9c5fc3024574d0b954962d2e6453fa7489ba6327"><tt>9c5fc302</tt></a> v0.17.39 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.39"></a>
## [v0.17.39](https://github.com/99designs/gqlgen/compare/v0.17.38...v0.17.39) - 2023-10-05
- <a href="https://github.com/99designs/gqlgen/commit/eed94e8caddb251cc6c180062b26708b8b06dd35"><tt>eed94e8c</tt></a> release v0.17.39
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/a863d6451eb1bb54673a86de79004f2182501ac1"><tt>a863d645</tt></a> Add [@interfaceObject](https://github.com/interfaceObject) and [@composeDirective](https://github.com/composeDirective) at Federation 2 directive lists. (<a href="https://github.com/99designs/gqlgen/pull/2821">#2821</a>)</summary>
This commit just adding those directives into the list.
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/3ff523ba5f4564432f7c8c0631ccc3d25754a407"><tt>3ff523ba</tt></a> Bump postcss from 8.4.24 to 8.4.31 in /integration (<a href="https://github.com/99designs/gqlgen/pull/2819">#2819</a>)</summary>
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.24 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.24...8.4.31)
---
updated-dependencies:
- dependency-name: postcss
dependency-type: indirect
...
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/5925864212bb286e22a3027c3900aa99874cc7ad"><tt>59258642</tt></a> Adding duration scalar conforming to ISO8601 standard (<a href="https://github.com/99designs/gqlgen/pull/2800">#2800</a>)</summary>
* Adding duration scalar
* simple Duration scalar docs
* using MarshalString, to add proper double quotes
* adding deps and running go mod tidy on _examples
* Re-organize imports
* Fix test now that imports are sorted
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/37f8e4ebb45d5a7edac5e8f6a241d842b4b9cfb1"><tt>37f8e4eb</tt></a> Add ability to not fail when pong is not received. (<a href="https://github.com/99designs/gqlgen/pull/2815">#2815</a>)</summary>
I also changed how the read deadline set works a little, the reason for
this is that the protocol allows for pong to be sent without a ping.
So setting a read deadline on receiving pong isn't great. Instead we
should always set the read deadline on sending ping. Though to do this
we need to know whether we have received a pong or not. Because if we
set the read deadline when the previous ping still hasn't received the
pong. Then it will never hit the deadline.
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/89ac736ffe346e9ee95b12b4db467d1a802fe0ac"><tt>89ac736f</tt></a> Store parsed Schema on ExecutableSchema rather than use global variable (<a href="https://github.com/99designs/gqlgen/pull/2811">#2811</a>)</summary>
* codegen: executableSchema schema configurable
* feat
* feat
* codegen: add schema property on Config and executableSchema
* fix: fmt
* regenerate
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/919aded6be6cd722c202214e7a8183ed56762501"><tt>919aded6</tt></a> Add a pong only keep alive for the new protcol (<a href="https://github.com/99designs/gqlgen/pull/2814">#2814</a>)</summary>
The protocol allows for this and this eliminates the potential for
over-agressive triggers of the read deadline set for using the ping/pong
flow.
https://github.com/enisdenjo/graphql-ws/blob/50d5a512d0d7252d41c079e6716b884a191b1ddc/PROTOCOL.md#pong
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/001c296a39ee981d415f5079ce0a8827e5d65874"><tt>001c296a</tt></a> Update auto-generated files with latest results. (<a href="https://github.com/99designs/gqlgen/pull/2813">#2813</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/f6fa3aae778926668b0787605c0eab125a800c1e"><tt>f6fa3aae</tt></a> Consider go type name when autobinding (<a href="https://github.com/99designs/gqlgen/pull/2812">#2812</a>)</summary>
Currently, generated schema type names are normalized, for instance - SomeTYPE in the schema will be generated as SomeType in the model.
When autobinding, however, we only consider the schema type name when searching for it in the relevant package(s),
thus type names that differ post normalizations aren't auto-bound properly and are instead re-generated.
This commit suggests a fix where we'd try to autobind for both the schema type name (first, to maintain back compat), or the go type name if the former isn't found.
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/4e8d8c70ce216824e506ad3d77b005f56c3ed144"><tt>4e8d8c70</tt></a> Feature: Support Apollo Federation Auth Directives (<a href="https://github.com/99designs/gqlgen/pull/2809">#2809</a>)</summary>
* local version working
* gofmt
* tabs
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/66709d89f0fd45eca823382d3a95020ef5c7485c"><tt>66709d89</tt></a> feat: update getting-started CreateTodo mutationResolver (<a href="https://github.com/99designs/gqlgen/pull/2810">#2810</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/2c9f9c5f828bdda8aa03c4be7adf1d0c9abaa8f6"><tt>2c9f9c5f</tt></a> fix: CodeGen for omit_slice_element_pointers and GetMany Entity Resolvers (<a href="https://github.com/99designs/gqlgen/pull/2802">#2802</a>)</summary>
* remove ! from reps definition
* adding tests
* fixing tests
* adding documentation
* addressing lint
* commit after go gonerate
* gofmt
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/3e2393f3afe1f3f530c34f4dbbece47bde6e736b"><tt>3e2393f3</tt></a> add close flag into wsConnection to avoid duplicate calls of CloseFunc (<a href="https://github.com/99designs/gqlgen/pull/2803">#2803</a>)</summary>
* add close flag into wsConnection to avoid duplicate calls of CloseFunc
* add test
* Fix linter error
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/af4d3943308b7a84464771e5ed6b578ba40ee0c3"><tt>af4d3943</tt></a> Allow WebsocketInitFunc to add payload to Ack (<a href="https://github.com/99designs/gqlgen/pull/4">#4</a>) (<a href="https://github.com/99designs/gqlgen/pull/2791">#2791</a>)</summary>
* Allow WebsocketInitFunc to add payload to Ack
The connection ACK message in the protocol for both
graphql-ws and graphql-transport-ws allows for a payload in the
connection ack message.
We really wanted to use this to establish better telemetry in our use of
websockets in graphql.
* Fix lint error in test
* Switch argument ordering.
---------
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/f3b860336bfcc7307713420e703ee5badec029a4"><tt>f3b86033</tt></a> v0.17.38 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.38"></a>
## [v0.17.38](https://github.com/99designs/gqlgen/compare/v0.17.37...v0.17.38) - 2023-09-19
- <a href="https://github.com/99designs/gqlgen/commit/d9077fac9c2320b28744fb43ab1d00c4d50f0af8"><tt>d9077fac</tt></a> release v0.17.38
- <a href="https://github.com/99designs/gqlgen/commit/5f452ce2dbfc4171fc021c10de11b435a60c8955"><tt>5f452ce2</tt></a> Update gqlparser to 2.5.10 (<a href="https://github.com/99designs/gqlgen/pull/2798">#2798</a>)
- <a href="https://github.com/99designs/gqlgen/commit/c89860bdd01827ad0f7a3f2020935e2ae115ecb7"><tt>c89860bd</tt></a> refactor: return `null` instead of zero value uuid (<a href="https://github.com/99designs/gqlgen/pull/2794">#2794</a>)
- <a href="https://github.com/99designs/gqlgen/commit/625ca2e58aba47cd540f0fb47e048a743a896906"><tt>625ca2e5</tt></a> Make it possible to pass UI headers (<a href="https://github.com/99designs/gqlgen/pull/2793">#2793</a>)
- <a href="https://github.com/99designs/gqlgen/commit/fceb33111bef2bbbf14468d78888098ac8968351"><tt>fceb3311</tt></a> Fix rand int docs link in Getting Started (<a href="https://github.com/99designs/gqlgen/pull/2789">#2789</a>)
- <a href="https://github.com/99designs/gqlgen/commit/f01d580501ddca4cf20b6fbf7f937be8509466bf"><tt>f01d5805</tt></a> Add new changelog (<a href="https://github.com/99designs/gqlgen/pull/2787">#2787</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/9930e5740e0670a69bfd4397d87222b49cf7f0e6"><tt>9930e574</tt></a> Ability to use forceGenerate and extraFields together (<a href="https://github.com/99designs/gqlgen/pull/2788">#2788</a>)</summary>
* Ability to user forceGenerate and extraFields together
* Some docs for forceGenerate added
---------
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/f90ac05e42fcc97addda2d32e3cc16565f55ed98"><tt>f90ac05e</tt></a> v0.17.37 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.37"></a>
## [v0.17.37](https://github.com/99designs/gqlgen/compare/v0.17.36...v0.17.37) - 2023-09-08
- <a href="https://github.com/99designs/gqlgen/commit/ccae370e96cbca6ce5deaabf28a6d57e3b181b3b"><tt>ccae370e</tt></a> release v0.17.37
- <a href="https://github.com/99designs/gqlgen/commit/6505f8be0d99593376d6c9a0dea00af5e3c018ea"><tt>6505f8be</tt></a> Update gqlparser (<a href="https://github.com/99designs/gqlgen/pull/2785">#2785</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/153ec470d993a39c1656fc45432740f1d3dd10ea"><tt>153ec470</tt></a> add uuid type (<a href="https://github.com/99designs/gqlgen/pull/2751">#2751</a>) (closes <a href="https://github.com/99designs/gqlgen/issues/2749"> #2749</a>)</summary>
* add uuid type
* add uuid example
* add uuid scalar doc
* strconv.Quote
* Apply suggestions from code review
* fix
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/fa4711801c59d27db887a50e1f8393006268194e"><tt>fa471180</tt></a> ForceGenerate parameter to [@goModel](https://github.com/goModel) added. (<a href="https://github.com/99designs/gqlgen/pull/2780">#2780</a>)</summary>
* forceGenerate to docs added
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/11bb9b1890fd9e5909f06feba7b2985bf09785c9"><tt>11bb9b18</tt></a> codegen: add support for `go_build_tags` option in gqlgen.yaml (<a href="https://github.com/99designs/gqlgen/pull/2784">#2784</a>)</summary>
* codegen: support go_build_tags option in gqlgen.yaml
* chore: added test
* docs/content: update config example
* chore: more comment
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/bee47dcf1f3edb95e7a77e612ea27d85a417b12c"><tt>bee47dcf</tt></a> fix flaky test TestSubscriptions (<a href="https://github.com/99designs/gqlgen/pull/2779">#2779</a>)</summary>
* fix flaky test TestSubscriptions
* update other copy of the test
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/a41f4daad66cb0c102bfa09d7bff5a057d378197"><tt>a41f4daa</tt></a> docs: short-lived loader (<a href="https://github.com/99designs/gqlgen/pull/2778">#2778</a>)
- <a href="https://github.com/99designs/gqlgen/commit/cc4e0ba28375e0ec39c586485fa138c28e5bfcba"><tt>cc4e0ba2</tt></a> ensure HasOperationContext checks for nil (<a href="https://github.com/99designs/gqlgen/pull/2776">#2776</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/a1ca220477092398efc8e587dd653f5cb967d0e9"><tt>a1ca2204</tt></a> fix typo in TESTING.md server path (<a href="https://github.com/99designs/gqlgen/pull/2774">#2774</a>)</summary>
following TESTING.md instructions, I got an error:
"stat ./server/server.go: no such file or directory"
server.go path is: integration/server/cmd/integration/server.go
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/1cde8c3fab65847a6e8d7f423215b786b47c19df"><tt>1cde8c3f</tt></a> return internal types in schema introspection (<a href="https://github.com/99designs/gqlgen/pull/2773">#2773</a>)</summary>
according to graphql spec:
```
types: return the set of all named types contained within this schema.
Any named type which can be found through a field of any introspection type must be included in this set.
```
source: https://github.com/graphql/graphql-spec/blob/main/spec/Section%204%20--%20Introspection.md#the-__schema-type
some clients libs (like HotChocolate for C#) depends on this behavior.
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/065aea3efa27f0662eb2f62ef2dd131e153009ba"><tt>065aea3e</tt></a> Fix gqlgen truncates tag value with colon (<a href="https://github.com/99designs/gqlgen/pull/2759">#2759</a>)
- <a href="https://github.com/99designs/gqlgen/commit/d6270e4f4fd951c71ee2a2b997d7098b04e07976"><tt>d6270e4f</tt></a> Update subsciptions documentation to correctly close channel (<a href="https://github.com/99designs/gqlgen/pull/2753">#2753</a>)
- <a href="https://github.com/99designs/gqlgen/commit/2d8673a691deffe6ddd1f4d5f013a52dc91aef91"><tt>2d8673a6</tt></a> Add Model references to Interface (<a href="https://github.com/99designs/gqlgen/pull/2738">#2738</a>)
- <a href="https://github.com/99designs/gqlgen/commit/790d7a7571865b8b8324557e1a565f40c23217c8"><tt>790d7a75</tt></a> Allow GraphiQL headers to be set when creating the playground handler (<a href="https://github.com/99designs/gqlgen/pull/2740">#2740</a>) (closes <a href="https://github.com/99designs/gqlgen/issues/2739"> #2739</a>)
- <a href="https://github.com/99designs/gqlgen/commit/0eb95dc4315fc5f74431df03e008283cf9ec0c35"><tt>0eb95dc4</tt></a> v0.17.36 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.36"></a>
## [v0.17.36](https://github.com/99designs/gqlgen/compare/v0.17.35...v0.17.36) - 2023-07-27
- <a href="https://github.com/99designs/gqlgen/commit/bd6cfd3108818cd060a06491052c36fb6f5e4be3"><tt>bd6cfd31</tt></a> release v0.17.36
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/60ec0d86847c97cd45c6e431d49b7ccf50efeeb9"><tt>60ec0d86</tt></a> Fix plugin template resolution (<a href="https://github.com/99designs/gqlgen/pull/2733">#2733</a>) (closes <a href="https://github.com/99designs/gqlgen/issues/2262"> #2262</a>)</summary>
- According to the documentation comment for [templates.Options], if the
`Template` and `TemplateFS` fields are empty, it `Render` should find
the `.gotpl` files from the calling plugin. However, it looks like
helper function. This results in broken behavior in consumers such as
[infiotinc/gqlgenc](https://github.com/infiotinc/gqlgenc) when they
use the latest version of `gqlgen` as instead of finding the template
from the plugin, the test template from this package is used which
outputs only: `this is my test package`.
- The cause for this is that `runtime.Caller` was still only skipping
one stack level which means that it was finding the `Render` function
instead of its caller.
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/76d444c15fc1d98538648a229f41ed7b4573bad7"><tt>76d444c1</tt></a> Make models configurable via template (<a href="https://github.com/99designs/gqlgen/pull/2730">#2730</a>)
- <a href="https://github.com/99designs/gqlgen/commit/abe3ffde59d6a0d942a9ac2d1b657287f75efa67"><tt>abe3ffde</tt></a> Don't set the package variable for the new Resolver Template (<a href="https://github.com/99designs/gqlgen/pull/2725">#2725</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/febf9566224b3e3ad0e32903fd46d9005d6a7748"><tt>febf9566</tt></a> Make the resolver implementation configurable via a new template resolver.gotpl (<a href="https://github.com/99designs/gqlgen/pull/2720">#2720</a>)</summary>
* Make an optional resolver.gotpl ResolverTemplate to implement a custom resolver
* Add test
* Add documetation for the new resolver option
* Change the tab to spaces
* remove unecessary test assertion :/
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/bda3026043f187bf00f039d1c3648d5b1a8e4998"><tt>bda30260</tt></a> Fixed Data Loader docs (<a href="https://github.com/99designs/gqlgen/pull/2723">#2723</a>)</summary>
Also updated to v7
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/16c9eb64ed04218e927a74199a57f951bfb452b1"><tt>16c9eb64</tt></a> Fix docs (<a href="https://github.com/99designs/gqlgen/pull/2722">#2722</a>)</summary>
* docs: fix variable names in dataloader sample
* fix: request-scoped middleware
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/b233a01bf7eeb9f9ddc590a7dd72058da28d88df"><tt>b233a01b</tt></a> docs: update dataloader docs (<a href="https://github.com/99designs/gqlgen/pull/2719">#2719</a>)</summary>
* docs: update example
* docs: update example
* fix: import
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/cccc7389c10cdd4ef4eb7b9541dedb0f9cf74a97"><tt>cccc7389</tt></a> Added go mod tidy to quick start guide (<a href="https://github.com/99designs/gqlgen/pull/2718">#2718</a>) (closes <a href="https://github.com/99designs/gqlgen/issues/2717"> #2717</a>, <a href="https://github.com/99designs/gqlgen/issues/2651"> #2651</a>, <a href="https://github.com/99designs/gqlgen/issues/2641"> #2641</a>, <a href="https://github.com/99designs/gqlgen/issues/2614"> #2614</a>, <a href="https://github.com/99designs/gqlgen/issues/2576"> #2576</a>)
- <a href="https://github.com/99designs/gqlgen/commit/9adc7b81ac27c157e597d369e4499353f0f6b0b0"><tt>9adc7b81</tt></a> Update gqlparser to v2.5.8 (<a href="https://github.com/99designs/gqlgen/pull/2716">#2716</a>)
- <a href="https://github.com/99designs/gqlgen/commit/b442fbf457498c8ad318578b570c5a6b64cab452"><tt>b442fbf4</tt></a> Post v0.17.35 changelog update
- <a href="https://github.com/99designs/gqlgen/commit/57c121994b1f843f62f99ada71a707e4f263a9df"><tt>57c12199</tt></a> v0.17.35 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.35"></a> <a name="v0.17.35"></a>
## [v0.17.35](https://github.com/99designs/gqlgen/compare/v0.17.34...v0.17.35) - 2023-07-15 ## [v0.17.35](https://github.com/99designs/gqlgen/compare/v0.17.34...v0.17.35) - 2023-07-15
- <a href="https://github.com/99designs/gqlgen/commit/05006bf1dcb9451b0960d07fd68370145c2697b4"><tt>05006bf1</tt></a> release v0.17.35 - <a href="https://github.com/99designs/gqlgen/commit/05006bf1dcb9451b0960d07fd68370145c2697b4"><tt>05006bf1</tt></a> release v0.17.35

View File

@ -25,7 +25,7 @@ Setting up the integration environment is a little tricky:
```bash ```bash
cd integration cd integration
go generate ./... go generate ./...
go run ./server/server.go go run ./server/cmd/integration/server.go
``` ```
in another terminal in another terminal
```bash ```bash

View File

@ -5,9 +5,10 @@ import (
"go/types" "go/types"
"strings" "strings"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates" "github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast"
) )
type ArgSet struct { type ArgSet struct {

View File

@ -5,12 +5,12 @@ import (
"fmt" "fmt"
"go/token" "go/token"
"go/types" "go/types"
"strings"
"github.com/vektah/gqlparser/v2/ast"
"golang.org/x/tools/go/packages" "golang.org/x/tools/go/packages"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/internal/code" "github.com/99designs/gqlgen/internal/code"
"github.com/vektah/gqlparser/v2/ast"
) )
var ErrTypeNotFound = errors.New("unable to find type") var ErrTypeNotFound = errors.New("unable to find type")
@ -203,6 +203,7 @@ type TypeReference struct {
IsOmittable bool // Is the type wrapped with Omittable IsOmittable bool // Is the type wrapped with Omittable
IsContext bool // Is the Marshaler/Unmarshaller the context version; applies to either the method or interface variety. IsContext bool // Is the Marshaler/Unmarshaller the context version; applies to either the method or interface variety.
PointersInUmarshalInput bool // Inverse values and pointers in return. PointersInUmarshalInput bool // Inverse values and pointers in return.
IsRoot bool // Is the type a root level definition such as Query, Mutation or Subscription
} }
func (ref *TypeReference) Elem() *TypeReference { func (ref *TypeReference) Elem() *TypeReference {
@ -274,6 +275,10 @@ func (ref *TypeReference) IsScalar() bool {
return ref.Definition.Kind == ast.Scalar return ref.Definition.Kind == ast.Scalar
} }
func (ref *TypeReference) IsMap() bool {
return ref.GO == MapType
}
func (ref *TypeReference) UniquenessKey() string { func (ref *TypeReference) UniquenessKey() string {
nullability := "O" nullability := "O"
if ref.GQL.NonNull { if ref.GQL.NonNull {
@ -285,7 +290,7 @@ func (ref *TypeReference) UniquenessKey() string {
// Fix for #896 // Fix for #896
elemNullability = "ᚄ" elemNullability = "ᚄ"
} }
return nullability + ref.Definition.Name + "2" + TypeIdentifier(ref.GO) + elemNullability return nullability + ref.Definition.Name + "2" + templates.TypeIdentifier(ref.GO) + elemNullability
} }
func (ref *TypeReference) MarshalFunc() string { func (ref *TypeReference) MarshalFunc() string {
@ -391,6 +396,7 @@ func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret
Definition: def, Definition: def,
GQL: schemaType, GQL: schemaType,
GO: MapType, GO: MapType,
IsRoot: b.cfg.IsRoot(def),
}, nil }, nil
} }
@ -402,6 +408,7 @@ func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret
Definition: def, Definition: def,
GQL: schemaType, GQL: schemaType,
GO: InterfaceType, GO: InterfaceType,
IsRoot: b.cfg.IsRoot(def),
}, nil }, nil
} }
@ -413,6 +420,7 @@ func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret
ref := &TypeReference{ ref := &TypeReference{
Definition: def, Definition: def,
GQL: schemaType, GQL: schemaType,
IsRoot: b.cfg.IsRoot(def),
} }
obj, err := b.FindObject(pkgName, typeName) obj, err := b.FindObject(pkgName, typeName)
@ -540,41 +548,3 @@ func basicUnderlying(it types.Type) *types.Basic {
return nil return nil
} }
var pkgReplacer = strings.NewReplacer(
"/", "ᚋ",
".", "ᚗ",
"-", "ᚑ",
"~", "א",
)
func TypeIdentifier(t types.Type) string {
res := ""
for {
switch it := t.(type) {
case *types.Pointer:
t.Underlying()
res += "ᚖ"
t = it.Elem()
case *types.Slice:
res += "ᚕ"
t = it.Elem()
case *types.Named:
res += pkgReplacer.Replace(it.Obj().Pkg().Path())
res += "ᚐ"
res += it.Obj().Name()
return res
case *types.Basic:
res += it.Name()
return res
case *types.Map:
res += "map"
return res
case *types.Interface:
res += "interface"
return res
default:
panic(fmt.Errorf("unexpected type %T", it))
}
}
}

View File

@ -3,6 +3,7 @@ package config
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"go/types"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
@ -10,10 +11,13 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/99designs/gqlgen/internal/code"
"github.com/vektah/gqlparser/v2" "github.com/vektah/gqlparser/v2"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"golang.org/x/tools/go/packages"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/internal/code"
) )
type Config struct { type Config struct {
@ -26,6 +30,7 @@ type Config struct {
Models TypeMap `yaml:"models,omitempty"` Models TypeMap `yaml:"models,omitempty"`
StructTag string `yaml:"struct_tag,omitempty"` StructTag string `yaml:"struct_tag,omitempty"`
Directives map[string]DirectiveConfig `yaml:"directives,omitempty"` Directives map[string]DirectiveConfig `yaml:"directives,omitempty"`
GoBuildTags StringList `yaml:"go_build_tags,omitempty"`
GoInitialisms GoInitialismsConfig `yaml:"go_initialisms,omitempty"` GoInitialisms GoInitialismsConfig `yaml:"go_initialisms,omitempty"`
OmitSliceElementPointers bool `yaml:"omit_slice_element_pointers,omitempty"` OmitSliceElementPointers bool `yaml:"omit_slice_element_pointers,omitempty"`
OmitGetters bool `yaml:"omit_getters,omitempty"` OmitGetters bool `yaml:"omit_getters,omitempty"`
@ -211,7 +216,9 @@ func CompleteConfig(config *Config) error {
func (c *Config) Init() error { func (c *Config) Init() error {
if c.Packages == nil { if c.Packages == nil {
c.Packages = &code.Packages{} c.Packages = code.NewPackages(
code.WithBuildTags(c.GoBuildTags...),
)
} }
if c.Schema == nil { if c.Schema == nil {
@ -257,6 +264,10 @@ func (c *Config) ReloadAllPackages() {
c.Packages.ReloadAll(c.packageList()...) c.Packages.ReloadAll(c.packageList()...)
} }
func (c *Config) IsRoot(def *ast.Definition) bool {
return def == c.Schema.Query || def == c.Schema.Mutation || def == c.Schema.Subscription
}
func (c *Config) injectTypesFromSchema() error { func (c *Config) injectTypesFromSchema() error {
c.Directives["goModel"] = DirectiveConfig{ c.Directives["goModel"] = DirectiveConfig{
SkipRuntime: true, SkipRuntime: true,
@ -271,7 +282,7 @@ func (c *Config) injectTypesFromSchema() error {
} }
for _, schemaType := range c.Schema.Types { for _, schemaType := range c.Schema.Types {
if schemaType == c.Schema.Query || schemaType == c.Schema.Mutation || schemaType == c.Schema.Subscription { if c.IsRoot(schemaType) {
continue continue
} }
@ -281,6 +292,7 @@ func (c *Config) injectTypesFromSchema() error {
c.Models.Add(schemaType.Name, mv.(string)) c.Models.Add(schemaType.Name, mv.(string))
} }
} }
if ma := bd.Arguments.ForName("models"); ma != nil { if ma := bd.Arguments.ForName("models"); ma != nil {
if mvs, err := ma.Value.Value(nil); err == nil { if mvs, err := ma.Value.Value(nil); err == nil {
for _, mv := range mvs.([]interface{}) { for _, mv := range mvs.([]interface{}) {
@ -288,6 +300,12 @@ func (c *Config) injectTypesFromSchema() error {
} }
} }
} }
if fg := bd.Arguments.ForName("forceGenerate"); fg != nil {
if mv, err := fg.Value.Value(nil); err == nil {
c.Models.ForceGenerate(schemaType.Name, mv.(bool))
}
}
} }
if schemaType.Kind == ast.Object || schemaType.Kind == ast.InputObject { if schemaType.Kind == ast.Object || schemaType.Kind == ast.InputObject {
@ -329,8 +347,9 @@ func (c *Config) injectTypesFromSchema() error {
} }
type TypeMapEntry struct { type TypeMapEntry struct {
Model StringList `yaml:"model"` Model StringList `yaml:"model,omitempty"`
Fields map[string]TypeMapField `yaml:"fields,omitempty"` ForceGenerate bool `yaml:"forceGenerate,omitempty"`
Fields map[string]TypeMapField `yaml:"fields,omitempty"`
// Key is the Go name of the field. // Key is the Go name of the field.
ExtraFields map[string]ModelExtraField `yaml:"extraFields,omitempty"` ExtraFields map[string]ModelExtraField `yaml:"extraFields,omitempty"`
@ -529,6 +548,12 @@ func (tm TypeMap) Add(name string, goType string) {
tm[name] = modelCfg tm[name] = modelCfg
} }
func (tm TypeMap) ForceGenerate(name string, forceGenerate bool) {
modelCfg := tm[name]
modelCfg.ForceGenerate = forceGenerate
tm[name] = modelCfg
}
type DirectiveConfig struct { type DirectiveConfig struct {
SkipRuntime bool `yaml:"skip_runtime"` SkipRuntime bool `yaml:"skip_runtime"`
} }
@ -583,7 +608,7 @@ func (c *Config) autobind() error {
ps := c.Packages.LoadAll(c.AutoBind...) ps := c.Packages.LoadAll(c.AutoBind...)
for _, t := range c.Schema.Types { for _, t := range c.Schema.Types {
if c.Models.UserDefined(t.Name) { if c.Models.UserDefined(t.Name) || c.Models[t.Name].ForceGenerate {
continue continue
} }
@ -591,14 +616,20 @@ func (c *Config) autobind() error {
if p == nil || p.Module == nil { if p == nil || p.Module == nil {
return fmt.Errorf("unable to load %s - make sure you're using an import path to a package that exists", c.AutoBind[i]) return fmt.Errorf("unable to load %s - make sure you're using an import path to a package that exists", c.AutoBind[i])
} }
if t := p.Types.Scope().Lookup(t.Name); t != nil {
c.Models.Add(t.Name(), t.Pkg().Path()+"."+t.Name()) autobindType := c.lookupAutobindType(p, t)
if autobindType != nil {
c.Models.Add(t.Name, autobindType.Pkg().Path()+"."+autobindType.Name())
break break
} }
} }
} }
for i, t := range c.Models { for i, t := range c.Models {
if t.ForceGenerate {
continue
}
for j, m := range t.Model { for j, m := range t.Model {
pkg, typename := code.PkgAndType(m) pkg, typename := code.PkgAndType(m)
@ -622,6 +653,17 @@ func (c *Config) autobind() error {
return nil return nil
} }
func (c *Config) lookupAutobindType(p *packages.Package, schemaType *ast.Definition) types.Object {
// Try binding to either the original schema type name, or the normalized go type name
for _, lookupName := range []string{schemaType.Name, templates.ToGo(schemaType.Name)} {
if t := p.Types.Scope().Lookup(lookupName); t != nil {
return t
}
}
return nil
}
func (c *Config) injectBuiltins() { func (c *Config) injectBuiltins() {
builtins := TypeMap{ builtins := TypeMap{
"__Directive": {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Directive"}}, "__Directive": {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Directive"}},
@ -671,7 +713,9 @@ func (c *Config) injectBuiltins() {
func (c *Config) LoadSchema() error { func (c *Config) LoadSchema() error {
if c.Packages != nil { if c.Packages != nil {
c.Packages = &code.Packages{} c.Packages = code.NewPackages(
code.WithBuildTags(c.GoBuildTags...),
)
} }
if err := c.check(); err != nil { if err := c.check(); err != nil {

View File

@ -1,62 +1,10 @@
package config package config
import "strings" import (
"strings"
// commonInitialisms is a set of common initialisms. "github.com/99designs/gqlgen/codegen/templates"
// Only add entries that are highly unlikely to be non-initialisms. )
// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
var commonInitialisms = map[string]bool{
"ACL": true,
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"CSV": true,
"DNS": true,
"EOF": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ICMP": true,
"ID": true,
"IP": true,
"JSON": true,
"KVK": true,
"LHS": true,
"PDF": true,
"PGP": true,
"QPS": true,
"QR": true,
"RAM": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SQL": true,
"SSH": true,
"SVG": true,
"TCP": true,
"TLS": true,
"TTL": true,
"UDP": true,
"UI": true,
"UID": true,
"URI": true,
"URL": true,
"UTF8": true,
"UUID": true,
"VM": true,
"XML": true,
"XMPP": true,
"XSRF": true,
"XSS": true,
}
// GetInitialisms returns the initialisms to capitalize in Go names. If unchanged, default initialisms will be returned
var GetInitialisms = func() map[string]bool {
return commonInitialisms
}
// GoInitialismsConfig allows to modify the default behavior of naming Go methods, types and properties // GoInitialismsConfig allows to modify the default behavior of naming Go methods, types and properties
type GoInitialismsConfig struct { type GoInitialismsConfig struct {
@ -69,7 +17,7 @@ type GoInitialismsConfig struct {
// setInitialisms adjustes GetInitialisms based on its settings. // setInitialisms adjustes GetInitialisms based on its settings.
func (i GoInitialismsConfig) setInitialisms() { func (i GoInitialismsConfig) setInitialisms() {
toUse := i.determineGoInitialisms() toUse := i.determineGoInitialisms()
GetInitialisms = func() map[string]bool { templates.GetInitialisms = func() map[string]bool {
return toUse return toUse
} }
} }
@ -82,8 +30,8 @@ func (i GoInitialismsConfig) determineGoInitialisms() (initialismsToUse map[stri
initialismsToUse[strings.ToUpper(initialism)] = true initialismsToUse[strings.ToUpper(initialism)] = true
} }
} else { } else {
initialismsToUse = make(map[string]bool, len(commonInitialisms)+len(i.Initialisms)) initialismsToUse = make(map[string]bool, len(templates.CommonInitialisms)+len(i.Initialisms))
for initialism, value := range commonInitialisms { for initialism, value := range templates.CommonInitialisms {
initialismsToUse[strings.ToUpper(initialism)] = value initialismsToUse[strings.ToUpper(initialism)] = value
} }
for _, initialism := range i.Initialisms { for _, initialism := range i.Initialisms {

View File

@ -5,8 +5,9 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/templates"
) )
type DirectiveList map[string]*Directive type DirectiveList map[string]*Directive

View File

@ -10,11 +10,12 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"golang.org/x/text/cases" "golang.org/x/text/cases"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
) )
type Field struct { type Field struct {
@ -152,6 +153,11 @@ func (b *builder) bindField(obj *Object, f *Field) (errret error) {
switch target := target.(type) { switch target := target.(type) {
case nil: case nil:
// Skips creating a resolver for any root types
if b.Config.IsRoot(b.Schema.Types[f.Type.Name()]) {
return nil
}
objPos := b.Binder.TypePosition(obj.Type) objPos := b.Binder.TypePosition(obj.Type)
return fmt.Errorf( return fmt.Errorf(
"%s:%d adding resolver method for %s.%s, nothing matched", "%s:%d adding resolver method for %s.%s, nothing matched",

View File

@ -16,49 +16,59 @@ func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Contex
ret = {{ $null }} ret = {{ $null }}
} }
}() }()
{{- if $.AllDirectives.LocationDirectives "FIELD" }} {{- if $field.TypeReference.IsRoot }}
resTmp := ec._fieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) { {{- if $field.TypeReference.IsPtr }}
{{ template "field" $field }} res := &{{ $field.TypeReference.Elem.GO | ref }}{}
}) {{- else }}
{{ else }} res := {{ $field.TypeReference.GO | ref }}{}
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
{{ template "field" $field }}
})
if err != nil {
ec.Error(ctx, err)
return {{ $null }}
}
{{- end }}
if resTmp == nil {
{{- if $field.TypeReference.GQL.NonNull }}
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
{{- end }} {{- end }}
return {{ $null }}
}
{{- if $object.Stream }}
return func(ctx context.Context) graphql.Marshaler {
select {
case res, ok := <-resTmp.(<-chan {{$field.TypeReference.GO | ref}}):
if !ok {
return nil
}
return graphql.WriterFunc(func(w io.Writer) {
w.Write([]byte{'{'})
graphql.MarshalString(field.Alias).MarshalGQL(w)
w.Write([]byte{':'})
ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res).MarshalGQL(w)
w.Write([]byte{'}'})
})
case <-ctx.Done():
return nil
}
}
{{- else }}
res := resTmp.({{$field.TypeReference.GO | ref}})
fc.Result = res fc.Result = res
return ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res) return ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res)
{{- else}}
{{- if $.AllDirectives.LocationDirectives "FIELD" }}
resTmp := ec._fieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) {
{{ template "field" $field }}
})
{{ else }}
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
{{ template "field" $field }}
})
if err != nil {
ec.Error(ctx, err)
return {{ $null }}
}
{{- end }}
if resTmp == nil {
{{- if $field.TypeReference.GQL.NonNull }}
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
{{- end }}
return {{ $null }}
}
{{- if $object.Stream }}
return func(ctx context.Context) graphql.Marshaler {
select {
case res, ok := <-resTmp.(<-chan {{$field.TypeReference.GO | ref}}):
if !ok {
return nil
}
return graphql.WriterFunc(func(w io.Writer) {
w.Write([]byte{'{'})
graphql.MarshalString(field.Alias).MarshalGQL(w)
w.Write([]byte{':'})
ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res).MarshalGQL(w)
w.Write([]byte{'}'})
})
case <-ctx.Done():
return nil
}
}
{{- else }}
res := resTmp.({{$field.TypeReference.GO | ref}})
fc.Result = res
return ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res)
{{- end }}
{{- end }} {{- end }}
} }

View File

@ -9,9 +9,10 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates" "github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast"
) )
//go:embed *.gotpl //go:embed *.gotpl

View File

@ -18,6 +18,7 @@
// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. // NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.
func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
return &executableSchema{ return &executableSchema{
schema: cfg.Schema,
resolvers: cfg.Resolvers, resolvers: cfg.Resolvers,
directives: cfg.Directives, directives: cfg.Directives,
complexity: cfg.Complexity, complexity: cfg.Complexity,
@ -25,6 +26,7 @@
} }
type Config struct { type Config struct {
Schema *ast.Schema
Resolvers ResolverRoot Resolvers ResolverRoot
Directives DirectiveRoot Directives DirectiveRoot
Complexity ComplexityRoot Complexity ComplexityRoot
@ -93,12 +95,16 @@
{{ if eq .Config.Exec.Layout "single-file" }} {{ if eq .Config.Exec.Layout "single-file" }}
type executableSchema struct { type executableSchema struct {
schema *ast.Schema
resolvers ResolverRoot resolvers ResolverRoot
directives DirectiveRoot directives DirectiveRoot
complexity ComplexityRoot complexity ComplexityRoot
} }
func (e *executableSchema) Schema() *ast.Schema { func (e *executableSchema) Schema() *ast.Schema {
if e.schema != nil {
return e.schema
}
return parsedSchema return parsedSchema
} }
@ -268,14 +274,14 @@
if ec.DisableIntrospection { if ec.DisableIntrospection {
return nil, errors.New("introspection disabled") return nil, errors.New("introspection disabled")
} }
return introspection.WrapSchema(parsedSchema), nil return introspection.WrapSchema(ec.Schema()), nil
} }
func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { func (ec *executionContext) introspectType(name string) (*introspection.Type, error) {
if ec.DisableIntrospection { if ec.DisableIntrospection {
return nil, errors.New("introspection disabled") return nil, errors.New("introspection disabled")
} }
return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil
} }
{{if .HasEmbeddableSources }} {{if .HasEmbeddableSources }}

View File

@ -5,7 +5,11 @@
{{- $it = "&it" }} {{- $it = "&it" }}
{{- end }} {{- end }}
func (ec *executionContext) unmarshalInput{{ .Name }}(ctx context.Context, obj interface{}) ({{ if .PointersInUmarshalInput }}*{{ end }}{{.Type | ref}}, error) { func (ec *executionContext) unmarshalInput{{ .Name }}(ctx context.Context, obj interface{}) ({{ if .PointersInUmarshalInput }}*{{ end }}{{.Type | ref}}, error) {
var it {{.Type | ref}} {{- if $input.IsMap }}
it := make(map[string]interface{}, len(obj.(map[string]interface{})))
{{- else }}
var it {{.Type | ref}}
{{- end }}
asMap := map[string]interface{}{} asMap := map[string]interface{}{}
for k, v := range obj.(map[string]interface{}) { for k, v := range obj.(map[string]interface{}) {
asMap[k] = v asMap[k] = v
@ -27,8 +31,10 @@
switch k { switch k {
{{- range $field := .Fields }} {{- range $field := .Fields }}
case {{$field.Name|quote}}: case {{$field.Name|quote}}:
var err error {{- $lhs := (printf "it.%s" $field.GoFieldName) }}
{{- if $input.IsMap }}
{{- $lhs = (printf "it[%q]" $field.Name) }}
{{- end }}
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField({{$field.Name|quote}})) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField({{$field.Name|quote}}))
{{- if $field.ImplDirectives }} {{- if $field.ImplDirectives }}
directive0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $field.TypeReference.UnmarshalFunc }}(ctx, v) } directive0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $field.TypeReference.UnmarshalFunc }}(ctx, v) }
@ -44,18 +50,18 @@
} }
{{- else }} {{- else }}
{{- if $field.TypeReference.IsOmittable }} {{- if $field.TypeReference.IsOmittable }}
it.{{$field.GoFieldName}} = graphql.OmittableOf(data) {{ $lhs }} = graphql.OmittableOf(data)
{{- else }} {{- else }}
it.{{$field.GoFieldName}} = data {{ $lhs }} = data
{{- end }} {{- end }}
{{- end }} {{- end }}
{{- if $field.TypeReference.IsNilable }} {{- if $field.TypeReference.IsNilable }}
{{- if not $field.IsResolver }} {{- if not $field.IsResolver }}
} else if tmp == nil { } else if tmp == nil {
{{- if $field.TypeReference.IsOmittable }} {{- if $field.TypeReference.IsOmittable }}
it.{{$field.GoFieldName}} = graphql.OmittableOf[{{ $field.TypeReference.GO | ref }}](nil) {{ $lhs }} = graphql.OmittableOf[{{ $field.TypeReference.GO | ref }}](nil)
{{- else }} {{- else }}
it.{{$field.GoFieldName}} = nil {{ $lhs }} = nil
{{- end }} {{- end }}
{{- end }} {{- end }}
{{- end }} {{- end }}
@ -78,9 +84,9 @@
return {{$it}}, err return {{$it}}, err
} }
{{- if $field.TypeReference.IsOmittable }} {{- if $field.TypeReference.IsOmittable }}
it.{{$field.GoFieldName}} = graphql.OmittableOf(data) {{ $lhs }} = graphql.OmittableOf(data)
{{- else }} {{- else }}
it.{{$field.GoFieldName}} = data {{ $lhs }} = data
{{- end }} {{- end }}
{{- end }} {{- end }}
{{- end }} {{- end }}

View File

@ -3,6 +3,7 @@ package codegen
import ( import (
"fmt" "fmt"
"go/types" "go/types"
"sort"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
@ -40,7 +41,13 @@ func (b *builder) buildInterface(typ *ast.Definition) (*Interface, error) {
return nil, fmt.Errorf("%s is not an interface", i.Type) return nil, fmt.Errorf("%s is not an interface", i.Type)
} }
for _, implementor := range b.Schema.GetPossibleTypes(typ) { // Sort so that more specific types are evaluated first.
implementors := b.Schema.GetPossibleTypes(typ)
sort.Slice(implementors, func(i, j int) bool {
return len(implementors[i].Interfaces) > len(implementors[j].Interfaces)
})
for _, implementor := range implementors {
obj, err := b.Binder.DefaultUserObject(implementor.Name) obj, err := b.Binder.DefaultUserObject(implementor.Name)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s has no backing go type", implementor.Name) return nil, fmt.Errorf("%s has no backing go type", implementor.Name)

View File

@ -7,10 +7,11 @@ import (
"strings" "strings"
"unicode" "unicode"
"github.com/99designs/gqlgen/codegen/config"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"golang.org/x/text/cases" "golang.org/x/text/cases"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/99designs/gqlgen/codegen/config"
) )
type GoFieldType int type GoFieldType int
@ -44,7 +45,7 @@ func (b *builder) buildObject(typ *ast.Definition) (*Object, error) {
caser := cases.Title(language.English, cases.NoLower) caser := cases.Title(language.English, cases.NoLower)
obj := &Object{ obj := &Object{
Definition: typ, Definition: typ,
Root: b.Schema.Query == typ || b.Schema.Mutation == typ || b.Schema.Subscription == typ, Root: b.Config.IsRoot(typ),
DisableConcurrency: typ == b.Schema.Mutation, DisableConcurrency: typ == b.Schema.Mutation,
Stream: typ == b.Schema.Subscription, Stream: typ == b.Schema.Subscription,
Directives: dirs, Directives: dirs,
@ -112,8 +113,8 @@ func (o *Object) HasResolvers() bool {
} }
func (o *Object) HasUnmarshal() bool { func (o *Object) HasUnmarshal() bool {
if o.Type == config.MapType { if o.IsMap() {
return true return false
} }
for i := 0; i < o.Type.(*types.Named).NumMethods(); i++ { for i := 0; i < o.Type.(*types.Named).NumMethods(); i++ {
if o.Type.(*types.Named).Method(i).Name() == "UnmarshalGQL" { if o.Type.(*types.Named).Method(i).Name() == "UnmarshalGQL" {
@ -149,6 +150,10 @@ func (o *Object) IsReserved() bool {
return strings.HasPrefix(o.Definition.Name, "__") return strings.HasPrefix(o.Definition.Name, "__")
} }
func (o *Object) IsMap() bool {
return o.Type == config.MapType
}
func (o *Object) Description() string { func (o *Object) Description() string {
return o.Definition.Description return o.Definition.Description
} }

View File

@ -17,6 +17,7 @@
// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. // NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.
func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
return &executableSchema{ return &executableSchema{
schema: cfg.Schema,
resolvers: cfg.Resolvers, resolvers: cfg.Resolvers,
directives: cfg.Directives, directives: cfg.Directives,
complexity: cfg.Complexity, complexity: cfg.Complexity,
@ -24,6 +25,7 @@ func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
} }
type Config struct { type Config struct {
Schema *ast.Schema
Resolvers ResolverRoot Resolvers ResolverRoot
Directives DirectiveRoot Directives DirectiveRoot
Complexity ComplexityRoot Complexity ComplexityRoot
@ -66,12 +68,16 @@ type ComplexityRoot struct {
} }
type executableSchema struct { type executableSchema struct {
schema *ast.Schema
resolvers ResolverRoot resolvers ResolverRoot
directives DirectiveRoot directives DirectiveRoot
complexity ComplexityRoot complexity ComplexityRoot
} }
func (e *executableSchema) Schema() *ast.Schema { func (e *executableSchema) Schema() *ast.Schema {
if e.schema != nil {
return e.schema
}
return parsedSchema return parsedSchema
} }
@ -241,14 +247,14 @@ func (ec *executionContext) introspectSchema() (*introspection.Schema, error) {
if ec.DisableIntrospection { if ec.DisableIntrospection {
return nil, errors.New("introspection disabled") return nil, errors.New("introspection disabled")
} }
return introspection.WrapSchema(parsedSchema), nil return introspection.WrapSchema(ec.Schema()), nil
} }
func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { func (ec *executionContext) introspectType(name string) (*introspection.Type, error) {
if ec.DisableIntrospection { if ec.DisableIntrospection {
return nil, errors.New("introspection disabled") return nil, errors.New("introspection disabled")
} }
return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil
} }

View File

@ -17,7 +17,6 @@ import (
"text/template" "text/template"
"unicode" "unicode"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/internal/code" "github.com/99designs/gqlgen/internal/code"
"github.com/99designs/gqlgen/internal/imports" "github.com/99designs/gqlgen/internal/imports"
) )
@ -202,7 +201,7 @@ func Funcs() template.FuncMap {
"rawQuote": rawQuote, "rawQuote": rawQuote,
"dump": Dump, "dump": Dump,
"ref": ref, "ref": ref,
"ts": config.TypeIdentifier, "ts": TypeIdentifier,
"call": Call, "call": Call,
"prefixLines": prefixLines, "prefixLines": prefixLines,
"notNil": notNil, "notNil": notNil,
@ -465,7 +464,7 @@ func wordWalker(str string, f func(*wordInfo)) {
} }
i++ i++
initialisms := config.GetInitialisms() initialisms := GetInitialisms()
// [w,i) is a word. // [w,i) is a word.
word := string(runes[w:i]) word := string(runes[w:i])
if !eow && initialisms[word] && !unicode.IsLower(runes[i]) { if !eow && initialisms[word] && !unicode.IsLower(runes[i]) {
@ -668,3 +667,97 @@ func write(filename string, b []byte, packages *code.Packages) error {
return nil return nil
} }
var pkgReplacer = strings.NewReplacer(
"/", "ᚋ",
".", "ᚗ",
"-", "ᚑ",
"~", "א",
)
func TypeIdentifier(t types.Type) string {
res := ""
for {
switch it := t.(type) {
case *types.Pointer:
t.Underlying()
res += "ᚖ"
t = it.Elem()
case *types.Slice:
res += "ᚕ"
t = it.Elem()
case *types.Named:
res += pkgReplacer.Replace(it.Obj().Pkg().Path())
res += "ᚐ"
res += it.Obj().Name()
return res
case *types.Basic:
res += it.Name()
return res
case *types.Map:
res += "map"
return res
case *types.Interface:
res += "interface"
return res
default:
panic(fmt.Errorf("unexpected type %T", it))
}
}
}
// CommonInitialisms is a set of common initialisms.
// Only add entries that are highly unlikely to be non-initialisms.
// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
var CommonInitialisms = map[string]bool{
"ACL": true,
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"CSV": true,
"DNS": true,
"EOF": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ICMP": true,
"ID": true,
"IP": true,
"JSON": true,
"KVK": true,
"LHS": true,
"PDF": true,
"PGP": true,
"QPS": true,
"QR": true,
"RAM": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SQL": true,
"SSH": true,
"SVG": true,
"TCP": true,
"TLS": true,
"TTL": true,
"UDP": true,
"UI": true,
"UID": true,
"URI": true,
"URL": true,
"UTF8": true,
"UUID": true,
"VM": true,
"XML": true,
"XMPP": true,
"XSRF": true,
"XSS": true,
}
// GetInitialisms returns the initialisms to capitalize in Go names. If unchanged, default initialisms will be returned
var GetInitialisms = func() map[string]bool {
return CommonInitialisms
}

View File

@ -59,8 +59,6 @@
{{- else}} {{- else}}
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
{{- end }} {{- end }}
{{- else if eq ($type.GO | ref) "map[string]interface{}" }}
return v.(map[string]interface{}), nil
{{- else if $type.IsMarshaler }} {{- else if $type.IsMarshaler }}
{{- if and $type.IsNilable $type.Elem }} {{- if and $type.IsNilable $type.Elem }}
var res = new({{ $type.Elem.GO | ref }}) var res = new({{ $type.Elem.GO | ref }})
@ -75,7 +73,7 @@
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
{{- else }} {{- else }}
res, err := ec.unmarshalInput{{ $type.GQL.Name }}(ctx, v) res, err := ec.unmarshalInput{{ $type.GQL.Name }}(ctx, v)
{{- if and $type.IsNilable (not $type.PointersInUmarshalInput) }} {{- if and $type.IsNilable (not $type.IsMap) (not $type.PointersInUmarshalInput) }}
return &res, graphql.ErrorOnPath(ctx, err) return &res, graphql.ErrorOnPath(ctx, err)
{{- else if and (not $type.IsNilable) $type.PointersInUmarshalInput }} {{- else if and (not $type.IsNilable) $type.PointersInUmarshalInput }}
return *res, graphql.ErrorOnPath(ctx, err) return *res, graphql.ErrorOnPath(ctx, err)
@ -185,6 +183,13 @@
{{- else }} {{- else }}
return res return res
{{- end }} {{- end }}
{{- else if $type.IsRoot }}
{{- if eq $type.Definition.Name "Subscription" }}
res := ec._{{$type.Definition.Name}}(ctx, sel)
return res(ctx)
{{- else }}
return ec._{{$type.Definition.Name}}(ctx, sel)
{{- end }}
{{- else }} {{- else }}
return ec._{{$type.Definition.Name}}(ctx, sel, {{ if not $type.IsNilable}}&{{end}} v) return ec._{{$type.Definition.Name}}(ctx, sel, {{ if not $type.IsNilable}}&{{end}} v)
{{- end }} {{- end }}

View File

@ -1,8 +1,9 @@
package complexity package complexity
import ( import (
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/graphql"
) )
func Calculate(es graphql.ExecutableSchema, op *ast.OperationDefinition, vars map[string]interface{}) int { func Calculate(es graphql.ExecutableSchema, op *ast.OperationDefinition, vars map[string]interface{}) int {

View File

@ -73,8 +73,8 @@ func WithOperationContext(ctx context.Context, rc *OperationContext) context.Con
// //
// Some errors can happen outside of an operation, eg json unmarshal errors. // Some errors can happen outside of an operation, eg json unmarshal errors.
func HasOperationContext(ctx context.Context) bool { func HasOperationContext(ctx context.Context) bool {
_, ok := ctx.Value(operationCtx).(*OperationContext) val, ok := ctx.Value(operationCtx).(*OperationContext)
return ok return ok && val != nil
} }
// This is just a convenient wrapper method for CollectFields // This is just a convenient wrapper method for CollectFields

27
vendor/github.com/99designs/gqlgen/graphql/duration.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package graphql
import (
"fmt"
"time"
dur "github.com/sosodev/duration"
)
// UnmarshalDuration returns the duration from a string in ISO8601 format
func UnmarshalDuration(v interface{}) (time.Duration, error) {
input, ok := v.(string)
if !ok {
return 0, fmt.Errorf("input must be a string")
}
d2, err := dur.Parse(input)
if err != nil {
return 0, err
}
return d2.ToTimeDuration(), nil
}
// MarshalDuration returns the duration on ISO8601 format
func MarshalDuration(d time.Duration) Marshaler {
return MarshalString(dur.Format(d))
}

View File

@ -3,12 +3,13 @@ package executor
import ( import (
"context" "context"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
"github.com/vektah/gqlparser/v2/parser" "github.com/vektah/gqlparser/v2/parser"
"github.com/vektah/gqlparser/v2/validator" "github.com/vektah/gqlparser/v2/validator"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
) )
// Executor executes graphql queries against a schema. // Executor executes graphql queries against a schema.

View File

@ -46,8 +46,8 @@ func (m *FieldSet) Dispatch(ctx context.Context) {
for _, d := range m.delayed[1:] { for _, d := range m.delayed[1:] {
wg.Add(1) wg.Add(1)
go func(d delayedResult) { go func(d delayedResult) {
defer wg.Done()
m.Values[d.i] = d.f(ctx) m.Values[d.i] = d.f(ctx)
wg.Done()
}(d) }(d)
} }

View File

@ -6,12 +6,11 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/99designs/gqlgen/graphql/errcode" "github.com/mitchellh/mapstructure"
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
"github.com/mitchellh/mapstructure" "github.com/99designs/gqlgen/graphql/errcode"
) )
const ( const (

View File

@ -4,10 +4,11 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/complexity" "github.com/99designs/gqlgen/complexity"
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode" "github.com/99designs/gqlgen/graphql/errcode"
"github.com/vektah/gqlparser/v2/gqlerror"
) )
const errComplexityLimit = "COMPLEXITY_LIMIT_EXCEEDED" const errComplexityLimit = "COMPLEXITY_LIMIT_EXCEEDED"

View File

@ -3,8 +3,9 @@ package extension
import ( import (
"context" "context"
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
) )
// EnableIntrospection enables clients to reflect all of the types available on the graph. // EnableIntrospection enables clients to reflect all of the types available on the graph.

View File

@ -3,8 +3,9 @@ package lru
import ( import (
"context" "context"
"github.com/99designs/gqlgen/graphql"
lru "github.com/hashicorp/golang-lru/v2" lru "github.com/hashicorp/golang-lru/v2"
"github.com/99designs/gqlgen/graphql"
) )
type LRU struct { type LRU struct {

View File

@ -7,12 +7,13 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/executor" "github.com/99designs/gqlgen/graphql/executor"
"github.com/99designs/gqlgen/graphql/handler/extension" "github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/99designs/gqlgen/graphql/handler/lru" "github.com/99designs/gqlgen/graphql/handler/lru"
"github.com/99designs/gqlgen/graphql/handler/transport" "github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/vektah/gqlparser/v2/gqlerror"
) )
type ( type (

View File

@ -5,8 +5,9 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
) )
// SendError sends a best effort error to a raw response writer. It assumes the client can understand the standard // SendError sends a best effort error to a raw response writer. It assumes the client can understand the standard

View File

@ -7,10 +7,11 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
) )
// GET implements the GET side of the default HTTP transport // GET implements the GET side of the default HTTP transport

View File

@ -5,8 +5,9 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
) )
func writeJson(w io.Writer, response *graphql.Response) { func writeJson(w io.Writer, response *graphql.Response) {

View File

@ -12,10 +12,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
) )
type ( type (
@ -26,7 +27,17 @@ type (
ErrorFunc WebsocketErrorFunc ErrorFunc WebsocketErrorFunc
CloseFunc WebsocketCloseFunc CloseFunc WebsocketCloseFunc
KeepAlivePingInterval time.Duration KeepAlivePingInterval time.Duration
PongOnlyInterval time.Duration
PingPongInterval time.Duration PingPongInterval time.Duration
/* If PingPongInterval has a non-0 duration, then when the server sends a ping
* it sets a ReadDeadline of PingPongInterval*2 and if the client doesn't respond
* with pong before that deadline is reached then the connection will die with a
* 1006 error code.
*
* MissingPongOk if true, tells the server to not use a ReadDeadline such that a
* missing/slow pong response from the client doesn't kill the connection.
*/
MissingPongOk bool
didInjectSubprotocols bool didInjectSubprotocols bool
} }
@ -38,13 +49,16 @@ type (
active map[string]context.CancelFunc active map[string]context.CancelFunc
mu sync.Mutex mu sync.Mutex
keepAliveTicker *time.Ticker keepAliveTicker *time.Ticker
pongOnlyTicker *time.Ticker
pingPongTicker *time.Ticker pingPongTicker *time.Ticker
receivedPong bool
exec graphql.GraphExecutor exec graphql.GraphExecutor
closed bool
initPayload InitPayload initPayload InitPayload
} }
WebsocketInitFunc func(ctx context.Context, initPayload InitPayload) (context.Context, error) WebsocketInitFunc func(ctx context.Context, initPayload InitPayload) (context.Context, *InitPayload, error)
WebsocketErrorFunc func(ctx context.Context, err error) WebsocketErrorFunc func(ctx context.Context, err error)
// Callback called when websocket is closed. // Callback called when websocket is closed.
@ -179,8 +193,10 @@ func (c *wsConnection) init() bool {
} }
} }
var initAckPayload *InitPayload = nil
if c.InitFunc != nil { if c.InitFunc != nil {
ctx, err := c.InitFunc(c.ctx, c.initPayload) var ctx context.Context
ctx, initAckPayload, err = c.InitFunc(c.ctx, c.initPayload)
if err != nil { if err != nil {
c.sendConnectionError(err.Error()) c.sendConnectionError(err.Error())
c.close(websocket.CloseNormalClosure, "terminated") c.close(websocket.CloseNormalClosure, "terminated")
@ -189,7 +205,15 @@ func (c *wsConnection) init() bool {
c.ctx = ctx c.ctx = ctx
} }
c.write(&message{t: connectionAckMessageType}) if initAckPayload != nil {
initJsonAckPayload, err := json.Marshal(*initAckPayload)
if err != nil {
panic(err)
}
c.write(&message{t: connectionAckMessageType, payload: initJsonAckPayload})
} else {
c.write(&message{t: connectionAckMessageType})
}
c.write(&message{t: keepAliveMessageType}) c.write(&message{t: keepAliveMessageType})
case connectionCloseMessageType: case connectionCloseMessageType:
c.close(websocket.CloseNormalClosure, "terminated") c.close(websocket.CloseNormalClosure, "terminated")
@ -228,16 +252,28 @@ func (c *wsConnection) run() {
go c.keepAlive(ctx) go c.keepAlive(ctx)
} }
// If we're running in graphql-transport-ws mode, create a timer that will trigger a
// just a pong message every interval
if c.conn.Subprotocol() == graphqltransportwsSubprotocol && c.PongOnlyInterval != 0 {
c.mu.Lock()
c.pongOnlyTicker = time.NewTicker(c.PongOnlyInterval)
c.mu.Unlock()
go c.keepAlivePongOnly(ctx)
}
// If we're running in graphql-transport-ws mode, create a timer that will // If we're running in graphql-transport-ws mode, create a timer that will
// trigger a ping message every interval // trigger a ping message every interval and expect a pong!
if c.conn.Subprotocol() == graphqltransportwsSubprotocol && c.PingPongInterval != 0 { if c.conn.Subprotocol() == graphqltransportwsSubprotocol && c.PingPongInterval != 0 {
c.mu.Lock() c.mu.Lock()
c.pingPongTicker = time.NewTicker(c.PingPongInterval) c.pingPongTicker = time.NewTicker(c.PingPongInterval)
c.mu.Unlock() c.mu.Unlock()
// Note: when the connection is closed by this deadline, the client if !c.MissingPongOk {
// will receive an "invalid close code" // Note: when the connection is closed by this deadline, the client
c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) // will receive an "invalid close code"
c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval))
}
go c.ping(ctx) go c.ping(ctx)
} }
@ -272,7 +308,11 @@ func (c *wsConnection) run() {
case pingMessageType: case pingMessageType:
c.write(&message{t: pongMessageType, payload: m.payload}) c.write(&message{t: pongMessageType, payload: m.payload})
case pongMessageType: case pongMessageType:
c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) c.mu.Lock()
c.receivedPong = true
c.mu.Unlock()
// Clear ReadTimeout -- 0 time val clears.
c.conn.SetReadDeadline(time.Time{})
default: default:
c.sendConnectionError("unexpected message %s", m.t) c.sendConnectionError("unexpected message %s", m.t)
c.close(websocket.CloseProtocolError, "unexpected message") c.close(websocket.CloseProtocolError, "unexpected message")
@ -281,6 +321,18 @@ func (c *wsConnection) run() {
} }
} }
func (c *wsConnection) keepAlivePongOnly(ctx context.Context) {
for {
select {
case <-ctx.Done():
c.pongOnlyTicker.Stop()
return
case <-c.pongOnlyTicker.C:
c.write(&message{t: pongMessageType, payload: json.RawMessage{}})
}
}
}
func (c *wsConnection) keepAlive(ctx context.Context) { func (c *wsConnection) keepAlive(ctx context.Context) {
for { for {
select { select {
@ -301,6 +353,14 @@ func (c *wsConnection) ping(ctx context.Context) {
return return
case <-c.pingPongTicker.C: case <-c.pingPongTicker.C:
c.write(&message{t: pingMessageType, payload: json.RawMessage{}}) c.write(&message{t: pingMessageType, payload: json.RawMessage{}})
// The initial deadline for this method is set in run()
// if we have not yet received a pong, don't reset the deadline.
c.mu.Lock()
if !c.MissingPongOk && c.receivedPong {
c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval))
}
c.receivedPong = false
c.mu.Unlock()
} }
} }
} }
@ -431,10 +491,15 @@ func (c *wsConnection) sendConnectionError(format string, args ...interface{}) {
func (c *wsConnection) close(closeCode int, message string) { func (c *wsConnection) close(closeCode int, message string) {
c.mu.Lock() c.mu.Lock()
if c.closed {
c.mu.Unlock()
return
}
_ = c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, message)) _ = c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, message))
for _, closer := range c.active { for _, closer := range c.active {
closer() closer()
} }
c.closed = true
c.mu.Unlock() c.mu.Unlock()
_ = c.conn.Close() _ = c.conn.Close()

View File

@ -2,7 +2,6 @@ package introspection
import ( import (
"sort" "sort"
"strings"
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
) )
@ -22,9 +21,6 @@ func (s *Schema) Types() []Type {
typeIndex := map[string]Type{} typeIndex := map[string]Type{}
typeNames := make([]string, 0, len(s.schema.Types)) typeNames := make([]string, 0, len(s.schema.Types))
for _, typ := range s.schema.Types { for _, typ := range s.schema.Types {
if strings.HasPrefix(typ.Name, "__") {
continue
}
typeNames = append(typeNames, typ.Name) typeNames = append(typeNames, typ.Name)
typeIndex[typ.Name] = *WrapTypeFromDef(s.schema, typ) typeIndex[typ.Name] = *WrapTypeFromDef(s.schema, typ)
} }

View File

@ -1,5 +1,7 @@
package graphql package graphql
import "encoding/json"
// Omittable is a wrapper around a value that also stores whether it is set // Omittable is a wrapper around a value that also stores whether it is set
// or not. // or not.
type Omittable[T any] struct { type Omittable[T any] struct {
@ -7,6 +9,11 @@ type Omittable[T any] struct {
set bool set bool
} }
var (
_ json.Marshaler = Omittable[struct{}]{}
_ json.Unmarshaler = (*Omittable[struct{}])(nil)
)
func OmittableOf[T any](value T) Omittable[T] { func OmittableOf[T any](value T) Omittable[T] {
return Omittable[T]{ return Omittable[T]{
value: value, value: value,
@ -33,3 +40,19 @@ func (o Omittable[T]) ValueOK() (T, bool) {
func (o Omittable[T]) IsSet() bool { func (o Omittable[T]) IsSet() bool {
return o.set return o.set
} }
func (o Omittable[T]) MarshalJSON() ([]byte, error) {
if !o.set {
return []byte("null"), nil
}
return json.Marshal(o.value)
}
func (o *Omittable[T]) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, &o.value)
if err != nil {
return err
}
o.set = true
return nil
}

View File

@ -1,10 +1,13 @@
package playground package playground
import ( import (
"encoding/json"
"fmt"
"html/template" "html/template"
"net/http" "net/http"
) )
// NOTE: New version available at https://embeddable-sandbox.cdn.apollographql.com/ -->
var apolloSandboxPage = template.Must(template.New("ApolloSandbox").Parse(`<!doctype html> var apolloSandboxPage = template.Must(template.New("ApolloSandbox").Parse(`<!doctype html>
<html> <html>
@ -23,40 +26,161 @@ var apolloSandboxPage = template.Must(template.New("ApolloSandbox").Parse(`<!doc
<body> <body>
<div style="width: 100vw; height: 100vh;" id='embedded-sandbox'></div> <div style="width: 100vw; height: 100vh;" id='embedded-sandbox'></div>
<!-- NOTE: New version available at https://embeddable-sandbox.cdn.apollographql.com/ --> <script rel="preload" as="script" crossorigin="anonymous" integrity="{{.mainSRI}}" type="text/javascript" src="https://embeddable-sandbox.cdn.apollographql.com/02e2da0fccbe0240ef03d2396d6c98559bab5b06/embeddable-sandbox.umd.production.min.js"></script>
<script rel="preload" as="script" crossorigin="anonymous" integrity="{{.mainSRI}}" type="text/javascript" src="https://embeddable-sandbox.cdn.apollographql.com/7212121cad97028b007e974956dc951ce89d683c/embeddable-sandbox.umd.production.min.js"></script>
<script> <script>
{{- if .endpointIsAbsolute}} {{- if .endpointIsAbsolute}}
const url = {{.endpoint}}; const url = {{.endpoint}};
{{- else}} {{- else}}
const url = location.protocol + '//' + location.host + {{.endpoint}}; const url = location.protocol + '//' + location.host + {{.endpoint}};
{{- end}} {{- end}}
<!-- See https://www.apollographql.com/docs/graphos/explorer/sandbox/#options --> const options = JSON.parse({{.options}})
new window.EmbeddedSandbox({ new window.EmbeddedSandbox({
target: '#embedded-sandbox', target: '#embedded-sandbox',
initialEndpoint: url, initialEndpoint: url,
persistExplorerState: true, persistExplorerState: true,
initialState: { ...options,
includeCookies: true, });
pollForSchemaUpdates: false,
}
});
</script> </script>
</body> </body>
</html>`)) </html>`))
// ApolloSandboxHandler responsible for setting up the altair playground // ApolloSandboxHandler responsible for setting up the apollo sandbox playground
func ApolloSandboxHandler(title, endpoint string) http.HandlerFunc { func ApolloSandboxHandler(title, endpoint string, opts ...ApolloSandboxOption) http.HandlerFunc {
options := &apolloSandboxOptions{
HideCookieToggle: true,
InitialState: apolloSandboxInitialState{
IncludeCookies: true,
PollForSchemaUpdates: false,
},
}
for _, opt := range opts {
opt(options)
}
optionsBytes, err := json.Marshal(options)
if err != nil {
panic(fmt.Errorf("failed to marshal apollo sandbox options: %w", err))
}
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
err := apolloSandboxPage.Execute(w, map[string]interface{}{ err := apolloSandboxPage.Execute(w, map[string]interface{}{
"title": title, "title": title,
"endpoint": endpoint, "endpoint": endpoint,
"endpointIsAbsolute": endpointHasScheme(endpoint), "endpointIsAbsolute": endpointHasScheme(endpoint),
"mainSRI": "sha256-ldbSJ7EovavF815TfCN50qKB9AMvzskb9xiG71bmg2I=", "mainSRI": "sha256-pYhw/8TGkZxk960PMMpDtjhw9YtKXUzGv6XQQaMJSh8=",
"options": string(optionsBytes),
}) })
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
} }
// See https://www.apollographql.com/docs/graphos/explorer/sandbox/#options -->
type apolloSandboxOptions struct {
HideCookieToggle bool `json:"hideCookieToggle"`
EndpointIsEditable bool `json:"endpointIsEditable"`
InitialState apolloSandboxInitialState `json:"initialState,omitempty"`
}
type apolloSandboxInitialState struct {
IncludeCookies bool `json:"includeCookies"`
Document string `json:"document,omitempty"`
Variables map[string]any `json:"variables,omitempty"`
Headers map[string]any `json:"headers,omitempty"`
CollectionId string `json:"collectionId,omitempty"`
OperationId string `json:"operationId,omitempty"`
PollForSchemaUpdates bool `json:"pollForSchemaUpdates"`
SharedHeaders map[string]any `json:"sharedHeaders,omitempty"`
}
type ApolloSandboxOption func(options *apolloSandboxOptions)
// WithApolloSandboxHideCookieToggle By default, the embedded Sandbox does not show the Include cookies toggle in its connection settings.
//
// Set hideCookieToggle to false to enable users of your embedded Sandbox instance to toggle the Include cookies setting.
func WithApolloSandboxHideCookieToggle(hideCookieToggle bool) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.HideCookieToggle = hideCookieToggle
}
}
// WithApolloSandboxEndpointIsEditable By default, the embedded Sandbox has a URL input box that is editable by users.
//
// Set endpointIsEditable to false to prevent users of your embedded Sandbox instance from changing the endpoint URL.
func WithApolloSandboxEndpointIsEditable(endpointIsEditable bool) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.EndpointIsEditable = endpointIsEditable
}
}
// WithApolloSandboxInitialStateIncludeCookies Set this value to true if you want the Sandbox to pass { credentials: 'include' } for its requests by default.
//
// If you set hideCookieToggle to false, users can override this default setting with the Include cookies toggle. (By default, the embedded Sandbox does not show the Include cookies toggle in its connection settings.)
//
// If you also pass the handleRequest option, this option is ignored.
//
// Read more about the fetch API and credentials here https://developer.mozilla.org/en-US/docs/Web/API/fetch#credentials
func WithApolloSandboxInitialStateIncludeCookies(includeCookies bool) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.IncludeCookies = includeCookies
}
}
// WithApolloSandboxInitialStateDocument Document operation to populate in the Sandbox's editor on load.
//
// If you omit this, the Sandbox initially loads an example query based on your schema.
func WithApolloSandboxInitialStateDocument(document string) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.Document = document
}
}
// WithApolloSandboxInitialStateVariables Variables containing initial variable values to populate in the Sandbox on load.
//
// If provided, these variables should apply to the initial query you provide for document.
func WithApolloSandboxInitialStateVariables(variables map[string]any) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.Variables = variables
}
}
// WithApolloSandboxInitialStateHeaders Headers containing initial variable values to populate in the Sandbox on load.
//
// If provided, these variables should apply to the initial query you provide for document.
func WithApolloSandboxInitialStateHeaders(headers map[string]any) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.Headers = headers
}
}
// WithApolloSandboxInitialStateCollectionIdAndOperationId The ID of a collection, paired with an operation ID to populate in the Sandbox on load.
//
// You can find these values from a registered graph in Studio by clicking the ... menu next to an operation in the Explorer of that graph and selecting View operation details.
func WithApolloSandboxInitialStateCollectionIdAndOperationId(collectionId, operationId string) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.CollectionId = collectionId
options.InitialState.OperationId = operationId
}
}
// WithApolloSandboxInitialStatePollForSchemaUpdates If true, the embedded Sandbox periodically polls your initialEndpoint for schema updates.
//
// The default value is false.
func WithApolloSandboxInitialStatePollForSchemaUpdates(pollForSchemaUpdates bool) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.PollForSchemaUpdates = pollForSchemaUpdates
}
}
// WithApolloSandboxInitialStateSharedHeaders Headers that are applied by default to every operation executed by the embedded Sandbox.
//
// Users can disable the application of these headers, but they can't modify their values.
//
// The embedded Sandbox always includes these headers in its introspection queries to your initialEndpoint.
func WithApolloSandboxInitialStateSharedHeaders(sharedHeaders map[string]any) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.SharedHeaders = sharedHeaders
}
}

View File

@ -58,13 +58,24 @@ var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
const wsProto = location.protocol == 'https:' ? 'wss:' : 'ws:'; const wsProto = location.protocol == 'https:' ? 'wss:' : 'ws:';
const subscriptionUrl = wsProto + '//' + location.host + {{.endpoint}}; const subscriptionUrl = wsProto + '//' + location.host + {{.endpoint}};
{{- end}} {{- end}}
{{- if .fetcherHeaders}}
const fetcherHeaders = {{.fetcherHeaders}};
{{- else}}
const fetcherHeaders = undefined;
{{- end}}
{{- if .uiHeaders}}
const uiHeaders = {{.uiHeaders}};
{{- else}}
const uiHeaders = undefined;
{{- end}}
const fetcher = GraphiQL.createFetcher({ url, subscriptionUrl }); const fetcher = GraphiQL.createFetcher({ url, subscriptionUrl, headers: fetcherHeaders });
ReactDOM.render( ReactDOM.render(
React.createElement(GraphiQL, { React.createElement(GraphiQL, {
fetcher: fetcher, fetcher: fetcher,
isHeadersEditorEnabled: true, isHeadersEditorEnabled: true,
shouldPersistHeaders: true shouldPersistHeaders: true,
headers: JSON.stringify(uiHeaders, null, 2)
}), }),
document.getElementById('graphiql'), document.getElementById('graphiql'),
); );
@ -75,16 +86,25 @@ var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
// Handler responsible for setting up the playground // Handler responsible for setting up the playground
func Handler(title string, endpoint string) http.HandlerFunc { func Handler(title string, endpoint string) http.HandlerFunc {
return HandlerWithHeaders(title, endpoint, nil, nil)
}
// HandlerWithHeaders sets up the playground.
// fetcherHeaders are used by the playground's fetcher instance and will not be visible in the UI.
// uiHeaders are default headers that will show up in the UI headers editor.
func HandlerWithHeaders(title string, endpoint string, fetcherHeaders map[string]string, uiHeaders map[string]string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html; charset=UTF-8") w.Header().Add("Content-Type", "text/html; charset=UTF-8")
err := page.Execute(w, map[string]interface{}{ err := page.Execute(w, map[string]interface{}{
"title": title, "title": title,
"endpoint": endpoint, "endpoint": endpoint,
"fetcherHeaders": fetcherHeaders,
"uiHeaders": uiHeaders,
"endpointIsAbsolute": endpointHasScheme(endpoint), "endpointIsAbsolute": endpointHasScheme(endpoint),
"subscriptionEndpoint": getSubscriptionEndpoint(endpoint), "subscriptionEndpoint": getSubscriptionEndpoint(endpoint),
"version": "3.0.1", "version": "3.0.6",
"cssSRI": "sha256-wTzfn13a+pLMB5rMeysPPR1hO7x0SwSeQI+cnw7VdbE=", "cssSRI": "sha256-wTzfn13a+pLMB5rMeysPPR1hO7x0SwSeQI+cnw7VdbE=",
"jsSRI": "sha256-dLnxjV+d2rFUCtYKjbPy413/8O+Ahy7QqAhaPNlL8fk=", "jsSRI": "sha256-eNxH+Ah7Z9up9aJYTQycgyNuy953zYZwE9Rqf5rH+r4=",
"reactSRI": "sha256-S0lp+k7zWUMk2ixteM6HZvu8L9Eh//OVrt+ZfbCpmgY=", "reactSRI": "sha256-S0lp+k7zWUMk2ixteM6HZvu8L9Eh//OVrt+ZfbCpmgY=",
"reactDOMSRI": "sha256-IXWO0ITNDjfnNXIu5POVfqlgYoop36bDzhodR6LW5Pc=", "reactDOMSRI": "sha256-IXWO0ITNDjfnNXIu5POVfqlgYoop36bDzhodR6LW5Pc=",
}) })

25
vendor/github.com/99designs/gqlgen/graphql/uuid.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
package graphql
import (
"fmt"
"github.com/google/uuid"
)
func MarshalUUID(id uuid.UUID) Marshaler {
if id == uuid.Nil {
return Null
}
return MarshalString(id.String())
}
func UnmarshalUUID(v any) (uuid.UUID, error) {
switch v := v.(type) {
case string:
return uuid.Parse(v)
case []byte:
return uuid.ParseBytes(v)
default:
return uuid.Nil, fmt.Errorf("%T is not a uuid", v)
}
}

View File

@ -1,3 +1,3 @@
package graphql package graphql
const Version = "v0.17.36" const Version = "v0.17.42"

View File

@ -28,15 +28,37 @@ var mode = packages.NeedName |
packages.NeedModule | packages.NeedModule |
packages.NeedDeps packages.NeedDeps
// Packages is a wrapper around x/tools/go/packages that maintains a (hopefully prewarmed) cache of packages type (
// that can be invalidated as writes are made and packages are known to change. // Packages is a wrapper around x/tools/go/packages that maintains a (hopefully prewarmed) cache of packages
type Packages struct { // that can be invalidated as writes are made and packages are known to change.
packages map[string]*packages.Package Packages struct {
importToName map[string]string packages map[string]*packages.Package
loadErrors []error importToName map[string]string
loadErrors []error
buildFlags []string
numLoadCalls int // stupid test steam. ignore. numLoadCalls int // stupid test steam. ignore.
numNameCalls int // stupid test steam. ignore. numNameCalls int // stupid test steam. ignore.
}
// Option is a function that can be passed to NewPackages to configure the package loader
Option func(p *Packages)
)
// WithBuildTags adds build tags to the packages.Load call
func WithBuildTags(tags ...string) func(p *Packages) {
return func(p *Packages) {
p.buildFlags = append(p.buildFlags, "-tags", strings.Join(tags, ","))
}
}
// NewPackages creates a new packages cache
// It will load all packages in the current module, and any packages that are passed to Load or LoadAll
func NewPackages(opts ...Option) *Packages {
p := &Packages{}
for _, opt := range opts {
opt(p)
}
return p
} }
func (p *Packages) CleanupUserPackages() { func (p *Packages) CleanupUserPackages() {
@ -47,8 +69,8 @@ func (p *Packages) CleanupUserPackages() {
modInfo = nil modInfo = nil
} }
}) })
// Don't cleanup github.com/99designs/gqlgen prefixed packages,
// Don't cleanup github.com/99designs/gqlgen prefixed packages, they haven't changed and do not need to be reloaded // they haven't changed and do not need to be reloaded
if modInfo != nil { if modInfo != nil {
var toRemove []string var toRemove []string
for k := range p.packages { for k := range p.packages {
@ -56,7 +78,6 @@ func (p *Packages) CleanupUserPackages() {
toRemove = append(toRemove, k) toRemove = append(toRemove, k)
} }
} }
for _, k := range toRemove { for _, k := range toRemove {
delete(p.packages, k) delete(p.packages, k)
} }
@ -91,7 +112,10 @@ func (p *Packages) LoadAll(importPaths ...string) []*packages.Package {
if len(missing) > 0 { if len(missing) > 0 {
p.numLoadCalls++ p.numLoadCalls++
pkgs, err := packages.Load(&packages.Config{Mode: mode}, missing...) pkgs, err := packages.Load(&packages.Config{
Mode: mode,
BuildFlags: p.buildFlags,
}, missing...)
if err != nil { if err != nil {
p.loadErrors = append(p.loadErrors, err) p.loadErrors = append(p.loadErrors, err)
} }
@ -140,7 +164,10 @@ func (p *Packages) LoadWithTypes(importPath string) *packages.Package {
pkg := p.Load(importPath) pkg := p.Load(importPath)
if pkg == nil || pkg.TypesInfo == nil { if pkg == nil || pkg.TypesInfo == nil {
p.numLoadCalls++ p.numLoadCalls++
pkgs, err := packages.Load(&packages.Config{Mode: mode}, importPath) pkgs, err := packages.Load(&packages.Config{
Mode: mode,
BuildFlags: p.buildFlags,
}, importPath)
if err != nil { if err != nil {
p.loadErrors = append(p.loadErrors, err) p.loadErrors = append(p.loadErrors, err)
return nil return nil
@ -173,7 +200,10 @@ func (p *Packages) NameForPackage(importPath string) string {
if pkg == nil { if pkg == nil {
// otherwise do a name only lookup for it but don't put it in the package cache. // otherwise do a name only lookup for it but don't put it in the package cache.
p.numNameCalls++ p.numNameCalls++
pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName}, importPath) pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedName,
BuildFlags: p.buildFlags,
}, importPath)
if err != nil { if err != nil {
p.loadErrors = append(p.loadErrors, err) p.loadErrors = append(p.loadErrors, err)
} else { } else {

View File

@ -10,10 +10,10 @@ import (
"go/token" "go/token"
"strings" "strings"
"github.com/99designs/gqlgen/internal/code"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/imports" "golang.org/x/tools/imports"
"github.com/99designs/gqlgen/internal/code"
) )
type visitFn func(node ast.Node) type visitFn func(node ast.Node)

View File

@ -10,8 +10,9 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/99designs/gqlgen/internal/code"
"golang.org/x/tools/go/packages" "golang.org/x/tools/go/packages"
"github.com/99designs/gqlgen/internal/code"
) )
type Rewriter struct { type Rewriter struct {

View File

@ -1,5 +1,7 @@
package main package main
//go:generate go generate ./_examples/...
import ( import (
"bytes" "bytes"
_ "embed" _ "embed"
@ -12,12 +14,13 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/urfave/cli/v2"
"github.com/99designs/gqlgen/api" "github.com/99designs/gqlgen/api"
"github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/internal/code" "github.com/99designs/gqlgen/internal/code"
"github.com/99designs/gqlgen/plugin/servergen" "github.com/99designs/gqlgen/plugin/servergen"
"github.com/urfave/cli/v2"
) )
//go:embed init-templates/schema.graphqls //go:embed init-templates/schema.graphqls

View File

@ -3,10 +3,11 @@ package federation
import ( import (
"go/types" "go/types"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates" "github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/plugin/federation/fieldset" "github.com/99designs/gqlgen/plugin/federation/fieldset"
"github.com/vektah/gqlparser/v2/ast"
) )
// Entity represents a federated type // Entity represents a federated type

View File

@ -59,6 +59,9 @@ func (f *federation) MutateConfig(cfg *config.Config) error {
"_Any": { "_Any": {
Model: config.StringList{"github.com/99designs/gqlgen/graphql.Map"}, Model: config.StringList{"github.com/99designs/gqlgen/graphql.Map"},
}, },
"federation__Scope": {
Model: config.StringList{"github.com/99designs/gqlgen/graphql.String"},
},
} }
for typeName, entry := range builtins { for typeName, entry := range builtins {
@ -80,6 +83,10 @@ func (f *federation) MutateConfig(cfg *config.Config) error {
cfg.Directives["tag"] = config.DirectiveConfig{SkipRuntime: true} cfg.Directives["tag"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["override"] = config.DirectiveConfig{SkipRuntime: true} cfg.Directives["override"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["inaccessible"] = config.DirectiveConfig{SkipRuntime: true} cfg.Directives["inaccessible"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["authenticated"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["requiresScopes"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["interfaceObject"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["composeDirective"] = config.DirectiveConfig{SkipRuntime: true}
} }
return nil return nil
@ -101,6 +108,7 @@ func (f *federation) InjectSourceEarly() *ast.Source {
` `
} else if f.Version == 2 { } else if f.Version == 2 {
input += ` input += `
directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive @composeDirective(name: String!) repeatable on SCHEMA directive @composeDirective(name: String!) repeatable on SCHEMA
directive @extends on OBJECT | INTERFACE directive @extends on OBJECT | INTERFACE
directive @external on OBJECT | FIELD_DEFINITION directive @external on OBJECT | FIELD_DEFINITION
@ -121,6 +129,12 @@ func (f *federation) InjectSourceEarly() *ast.Source {
directive @override(from: String!) on FIELD_DEFINITION directive @override(from: String!) on FIELD_DEFINITION
directive @provides(fields: FieldSet!) on FIELD_DEFINITION directive @provides(fields: FieldSet!) on FIELD_DEFINITION
directive @requires(fields: FieldSet!) on FIELD_DEFINITION directive @requires(fields: FieldSet!) on FIELD_DEFINITION
directive @requiresScopes(scopes: [[federation__Scope!]!]!) on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
directive @shareable repeatable on FIELD_DEFINITION | OBJECT directive @shareable repeatable on FIELD_DEFINITION | OBJECT
directive @tag(name: String!) repeatable on directive @tag(name: String!) repeatable on
| ARGUMENT_DEFINITION | ARGUMENT_DEFINITION
@ -135,6 +149,7 @@ func (f *federation) InjectSourceEarly() *ast.Source {
| UNION | UNION
scalar _Any scalar _Any
scalar FieldSet scalar FieldSet
scalar federation__Scope
` `
} }
return &ast.Source{ return &ast.Source{
@ -170,10 +185,14 @@ func (f *federation) InjectSourceLate(schema *ast.Schema) *ast.Source {
} }
entityResolverInputDefinitions += "input " + r.InputTypeName + " {\n" entityResolverInputDefinitions += "input " + r.InputTypeName + " {\n"
for _, keyField := range r.KeyFields { for _, keyField := range r.KeyFields {
entityResolverInputDefinitions += fmt.Sprintf("\t%s: %s\n", keyField.Field.ToGo(), keyField.Definition.Type.String()) entityResolverInputDefinitions += fmt.Sprintf(
"\t%s: %s\n",
keyField.Field.ToGo(),
keyField.Definition.Type.String(),
)
} }
entityResolverInputDefinitions += "}" entityResolverInputDefinitions += "}"
resolvers += fmt.Sprintf("\t%s(reps: [%s!]!): [%s]\n", r.ResolverName, r.InputTypeName, e.Name) resolvers += fmt.Sprintf("\t%s(reps: [%s]!): [%s]\n", r.ResolverName, r.InputTypeName, e.Name)
} else { } else {
resolverArgs := "" resolverArgs := ""
for _, keyField := range r.KeyFields { for _, keyField := range r.KeyFields {

View File

@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen" "github.com/99designs/gqlgen/codegen"
"github.com/99designs/gqlgen/codegen/templates" "github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast"
) )
// Set represents a FieldSet that is used in federation directives @key and @requires. // Set represents a FieldSet that is used in federation directives @key and @requires.
@ -133,7 +134,7 @@ func parseUnnestedKeyFieldSet(raw string, prefix []string) Set {
ret := Set{} ret := Set{}
for _, s := range strings.Fields(raw) { for _, s := range strings.Fields(raw) {
next := append(prefix[:], s) //nolint:gocritic // slicing out on purpose next := append(prefix[0:len(prefix):len(prefix)], s) //nolint:gocritic // set cap=len in order to force slice reallocation
ret = append(ret, next) ret = append(ret, next)
} }
return ret return ret

View File

@ -37,3 +37,6 @@ func (r *entityResolver) FindManyMultiHellosByName(ctx context.Context, reps []*
/// <Your code to resolve the list of items> /// <Your code to resolve the list of items>
} }
``` ```
**Note:**
If you are using `omit_slice_element_pointers: true` option in your config yaml, your `GetMany` resolver will still generate in the example above the same signature `FindManyMultiHellosByName(ctx context.Context, reps []*generated.ManyMultiHellosByNameInput) ([]*generated.MultiHello, error)`. But all other instances will continue to honor `omit_slice_element_pointers: true`

View File

@ -9,10 +9,11 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates" "github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/plugin" "github.com/99designs/gqlgen/plugin"
"github.com/vektah/gqlparser/v2/ast"
) )
//go:embed models.gotpl //go:embed models.gotpl
@ -52,6 +53,7 @@ type Interface struct {
Fields []*Field Fields []*Field
Implements []string Implements []string
OmitCheck bool OmitCheck bool
Models []*Object
} }
type Object struct { type Object struct {
@ -129,9 +131,18 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error {
OmitCheck: cfg.OmitInterfaceChecks, OmitCheck: cfg.OmitInterfaceChecks,
} }
// if the interface has a key directive as an entity interface, allow it to implement _Entity
if schemaType.Directives.ForName("key") != nil {
it.Implements = append(it.Implements, "_Entity")
}
b.Interfaces = append(b.Interfaces, it) b.Interfaces = append(b.Interfaces, it)
case ast.Object, ast.InputObject: case ast.Object, ast.InputObject:
if schemaType == cfg.Schema.Query || schemaType == cfg.Schema.Mutation || schemaType == cfg.Schema.Subscription { if cfg.IsRoot(schemaType) {
b.Models = append(b.Models, &Object{
Description: schemaType.Description,
Name: schemaType.Name,
})
continue continue
} }
@ -162,6 +173,7 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error {
uniqueMap[iface] = true uniqueMap[iface] = true
} }
} }
} }
b.Models = append(b.Models, it) b.Models = append(b.Models, it)
@ -200,6 +212,18 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error {
cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name)) cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name))
} }
for _, it := range b.Interfaces { for _, it := range b.Interfaces {
// On a given interface we want to keep a reference to all the models that implement it
for _, model := range b.Models {
for _, impl := range model.Implements {
if impl == it.Name {
// check if this isn't an implementation of an entity interface
if impl != "_Entity" {
// If this model has an implementation, add it to the Interface's Models
it.Models = append(it.Models, model)
}
}
}
}
cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name)) cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name))
} }
for _, it := range b.Scalars { for _, it := range b.Scalars {
@ -558,17 +582,19 @@ func removeDuplicateTags(t string) string {
continue continue
} }
processed[kv[0]] = true key := kv[0]
value := strings.Join(kv[1:], ":")
processed[key] = true
if len(returnTags) > 0 { if len(returnTags) > 0 {
returnTags = " " + returnTags returnTags = " " + returnTags
} }
isContained := containsInvalidSpace(kv[1]) isContained := containsInvalidSpace(value)
if isContained { if isContained {
panic(fmt.Errorf("tag value should not contain any leading or trailing spaces: %s", kv[1])) panic(fmt.Errorf("tag value should not contain any leading or trailing spaces: %s", value))
} }
returnTags = kv[0] + ":" + kv[1] + returnTags returnTags = key + ":" + value + returnTags
} }
return returnTags return returnTags

View File

@ -3,9 +3,10 @@
package plugin package plugin
import ( import (
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen" "github.com/99designs/gqlgen/codegen"
"github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/codegen/config"
"github.com/vektah/gqlparser/v2/ast"
) )
type Plugin interface { type Plugin interface {
@ -30,7 +31,7 @@ type LateSourceInjector interface {
InjectSourceLate(schema *ast.Schema) *ast.Source InjectSourceLate(schema *ast.Schema) *ast.Source
} }
// Implementer is used to generate code inside resolvers // ResolverImplementer is used to generate code inside resolvers
type ResolverImplementer interface { type ResolverImplementer interface {
Implement(field *codegen.Field) string Implement(field *codegen.Field) string
} }

View File

@ -68,7 +68,7 @@ func (m *Plugin) generateSingleFile(data *codegen.Data) error {
continue continue
} }
resolver := Resolver{o, f, nil, "", `panic("not implemented")`} resolver := Resolver{o, f, nil, "", `panic("not implemented")`, nil}
file.Resolvers = append(file.Resolvers, &resolver) file.Resolvers = append(file.Resolvers, &resolver)
} }
} }
@ -110,9 +110,12 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error {
for _, o := range objects { for _, o := range objects {
if o.HasResolvers() { if o.HasResolvers() {
fn := gqlToResolverName(data.Config.Resolver.Dir(), o.Position.Src.Name, data.Config.Resolver.FilenameTemplate) fnCase := gqlToResolverName(data.Config.Resolver.Dir(), o.Position.Src.Name, data.Config.Resolver.FilenameTemplate)
fn := strings.ToLower(fnCase)
if files[fn] == nil { if files[fn] == nil {
files[fn] = &File{} files[fn] = &File{
name: fnCase,
}
} }
caser := cases.Title(language.English, cases.NoLower) caser := cases.Title(language.English, cases.NoLower)
@ -127,48 +130,46 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error {
structName := templates.LcFirst(o.Name) + templates.UcFirst(data.Config.Resolver.Type) structName := templates.LcFirst(o.Name) + templates.UcFirst(data.Config.Resolver.Type)
comment := strings.TrimSpace(strings.TrimLeft(rewriter.GetMethodComment(structName, f.GoFieldName), `\`)) comment := strings.TrimSpace(strings.TrimLeft(rewriter.GetMethodComment(structName, f.GoFieldName), `\`))
implementation := strings.TrimSpace(rewriter.GetMethodBody(structName, f.GoFieldName)) implementation := strings.TrimSpace(rewriter.GetMethodBody(structName, f.GoFieldName))
if implementation == "" { if implementation == "" {
// Check for Implementer Plugin // use default implementation, if no implementation was previously used
var resolver_implementer plugin.ResolverImplementer implementation = fmt.Sprintf("panic(fmt.Errorf(\"not implemented: %v - %v\"))", f.GoFieldName, f.Name)
var exists bool
for _, p := range data.Plugins {
if p_cast, ok := p.(plugin.ResolverImplementer); ok {
resolver_implementer = p_cast
exists = true
break
}
}
if exists {
implementation = resolver_implementer.Implement(f)
} else {
implementation = fmt.Sprintf("panic(fmt.Errorf(\"not implemented: %v - %v\"))", f.GoFieldName, f.Name)
}
} }
resolver := Resolver{o, f, rewriter.GetPrevDecl(structName, f.GoFieldName), comment, implementation, nil}
resolver := Resolver{o, f, rewriter.GetPrevDecl(structName, f.GoFieldName), comment, implementation} var implExists bool
fn := gqlToResolverName(data.Config.Resolver.Dir(), f.Position.Src.Name, data.Config.Resolver.FilenameTemplate) for _, p := range data.Plugins {
rImpl, ok := p.(plugin.ResolverImplementer)
if !ok {
continue
}
if implExists {
return fmt.Errorf("multiple plugins implement ResolverImplementer")
}
implExists = true
resolver.ImplementationRender = rImpl.Implement
}
fnCase := gqlToResolverName(data.Config.Resolver.Dir(), f.Position.Src.Name, data.Config.Resolver.FilenameTemplate)
fn := strings.ToLower(fnCase)
if files[fn] == nil { if files[fn] == nil {
files[fn] = &File{} files[fn] = &File{
name: fnCase,
}
} }
files[fn].Resolvers = append(files[fn].Resolvers, &resolver) files[fn].Resolvers = append(files[fn].Resolvers, &resolver)
} }
} }
for filename, file := range files { for _, file := range files {
file.imports = rewriter.ExistingImports(filename) file.imports = rewriter.ExistingImports(file.name)
file.RemainingSource = rewriter.RemainingSource(filename) file.RemainingSource = rewriter.RemainingSource(file.name)
} }
newResolverTemplate := resolverTemplate newResolverTemplate := resolverTemplate
if data.Config.Resolver.ResolverTemplate != "" { if data.Config.Resolver.ResolverTemplate != "" {
newResolverTemplate = readResolverTemplate(data.Config.Resolver.ResolverTemplate) newResolverTemplate = readResolverTemplate(data.Config.Resolver.ResolverTemplate)
} }
for filename, file := range files { for _, file := range files {
resolverBuild := &ResolverBuild{ resolverBuild := &ResolverBuild{
File: file, File: file,
PackageName: data.Config.Resolver.Package, PackageName: data.Config.Resolver.Package,
@ -192,7 +193,7 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error {
err := templates.Render(templates.Options{ err := templates.Render(templates.Options{
PackageName: data.Config.Resolver.Package, PackageName: data.Config.Resolver.Package,
FileNotice: fileNotice.String(), FileNotice: fileNotice.String(),
Filename: filename, Filename: file.name,
Data: resolverBuild, Data: resolverBuild,
Packages: data.Config.Packages, Packages: data.Config.Packages,
Template: newResolverTemplate, Template: newResolverTemplate,
@ -230,6 +231,7 @@ type ResolverBuild struct {
} }
type File struct { type File struct {
name string
// These are separated because the type definition of the resolver object may live in a different file from the // These are separated because the type definition of the resolver object may live in a different file from the
// resolver method implementations, for example when extending a type in a different graphql schema file // resolver method implementations, for example when extending a type in a different graphql schema file
Objects []*codegen.Object Objects []*codegen.Object
@ -250,11 +252,19 @@ func (f *File) Imports() string {
} }
type Resolver struct { type Resolver struct {
Object *codegen.Object Object *codegen.Object
Field *codegen.Field Field *codegen.Field
PrevDecl *ast.FuncDecl PrevDecl *ast.FuncDecl
Comment string Comment string
Implementation string ImplementationStr string
ImplementationRender func(r *codegen.Field) string
}
func (r *Resolver) Implementation() string {
if r.ImplementationRender != nil {
return r.ImplementationRender(r.Field)
}
return r.ImplementationStr
} }
func gqlToResolverName(base string, gqlname, filenameTmpl string) string { func gqlToResolverName(base string, gqlname, filenameTmpl string) string {

View File

@ -233,6 +233,9 @@ tlsConfig, err := certmagic.TLS([]string{"example.com"})
if err != nil { if err != nil {
return err return err
} }
// be sure to customize NextProtos if serving a specific
// application protocol after the TLS handshake, for example:
tlsConfig.NextProtos = append([]string{"h2", "http/1.1"}, tlsConfig.NextProtos...)
``` ```

View File

@ -155,8 +155,8 @@ var AttemptsCtxKey retryStateCtxKey
// front. We figure that intermittent errors would be // front. We figure that intermittent errors would be
// resolved after the first retry, but any errors after // resolved after the first retry, but any errors after
// that would probably require at least a few minutes // that would probably require at least a few minutes
// to clear up: either for DNS to propagate, for the // or hours to clear up: either for DNS to propagate, for
// administrator to fix their DNS or network properties, // the administrator to fix their DNS or network config,
// or some other external factor needs to change. We // or some other external factor needs to change. We
// chose intervals that we think will be most useful // chose intervals that we think will be most useful
// without introducing unnecessary delay. The last // without introducing unnecessary delay. The last
@ -168,13 +168,26 @@ var retryIntervals = []time.Duration{
2 * time.Minute, 2 * time.Minute,
5 * time.Minute, // elapsed: 10 min 5 * time.Minute, // elapsed: 10 min
10 * time.Minute, 10 * time.Minute,
20 * time.Minute, 10 * time.Minute,
10 * time.Minute,
20 * time.Minute, // elapsed: 1 hr 20 * time.Minute, // elapsed: 1 hr
20 * time.Minute,
20 * time.Minute,
20 * time.Minute, // elapsed: 2 hr
30 * time.Minute, 30 * time.Minute,
30 * time.Minute, // elapsed: 2 hr 30 * time.Minute, // elapsed: 3 hr
30 * time.Minute,
30 * time.Minute, // elapsed: 4 hr
30 * time.Minute,
30 * time.Minute, // elapsed: 5 hr
1 * time.Hour, // elapsed: 6 hr
1 * time.Hour, 1 * time.Hour,
3 * time.Hour, // elapsed: 6 hr 1 * time.Hour, // elapsed: 8 hr
6 * time.Hour, // for up to maxRetryDuration 2 * time.Hour,
2 * time.Hour, // elapsed: 12 hr
3 * time.Hour,
3 * time.Hour, // elapsed: 18 hr
6 * time.Hour, // repeat for up to maxRetryDuration
} }
// maxRetryDuration is the maximum duration to try // maxRetryDuration is the maximum duration to try

View File

@ -410,7 +410,8 @@ func SubjectIsIP(subj string) bool {
func SubjectIsInternal(subj string) bool { func SubjectIsInternal(subj string) bool {
return subj == "localhost" || return subj == "localhost" ||
strings.HasSuffix(subj, ".localhost") || strings.HasSuffix(subj, ".localhost") ||
strings.HasSuffix(subj, ".local") strings.HasSuffix(subj, ".local") ||
strings.HasSuffix(subj, ".home.arpa")
} }
// MatchWildcard returns true if subject (a candidate DNS name) // MatchWildcard returns true if subject (a candidate DNS name)

View File

@ -268,7 +268,7 @@ type OnDemandConfig struct {
// whether a certificate can be obtained or renewed // whether a certificate can be obtained or renewed
// for the given name. If an error is returned, the // for the given name. If an error is returned, the
// request will be denied. // request will be denied.
DecisionFunc func(name string) error DecisionFunc func(ctx context.Context, name string) error
// Sources for getting new, unmanaged certificates. // Sources for getting new, unmanaged certificates.
// They will be invoked only during TLS handshakes // They will be invoked only during TLS handshakes

View File

@ -50,7 +50,7 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif
} }
func (cfg *Config) GetCertificateWithContext(ctx context.Context, clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { func (cfg *Config) GetCertificateWithContext(ctx context.Context, clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
if err := cfg.emit(ctx, "tls_get_certificate", map[string]any{"client_hello": clientHello}); err != nil { if err := cfg.emit(ctx, "tls_get_certificate", map[string]any{"client_hello": clientHelloWithoutConn(clientHello)}); err != nil {
cfg.Logger.Error("TLS handshake aborted by event handler", cfg.Logger.Error("TLS handshake aborted by event handler",
zap.String("server_name", clientHello.ServerName), zap.String("server_name", clientHello.ServerName),
zap.String("remote", clientHello.Conn.RemoteAddr().String()), zap.String("remote", clientHello.Conn.RemoteAddr().String()),
@ -58,6 +58,12 @@ func (cfg *Config) GetCertificateWithContext(ctx context.Context, clientHello *t
return nil, fmt.Errorf("handshake aborted by event handler: %w", err) return nil, fmt.Errorf("handshake aborted by event handler: %w", err)
} }
if ctx == nil {
// tests can't set context on a tls.ClientHelloInfo because it's unexported :(
ctx = context.Background()
}
ctx = context.WithValue(ctx, ClientHelloInfoCtxKey, clientHello)
// special case: serve up the certificate for a TLS-ALPN ACME challenge // special case: serve up the certificate for a TLS-ALPN ACME challenge
// (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05) // (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05)
for _, proto := range clientHello.SupportedProtos { for _, proto := range clientHello.SupportedProtos {
@ -313,8 +319,8 @@ func (cfg *Config) getCertDuringHandshake(ctx context.Context, hello *tls.Client
// Make sure a certificate is allowed for the given name. If not, it doesn't // Make sure a certificate is allowed for the given name. If not, it doesn't
// make sense to try loading one from storage (issue #185), getting it from a // make sense to try loading one from storage (issue #185), getting it from a
// certificate manager, or obtaining one from an issuer. // certificate manager, or obtaining one from an issuer.
if err := cfg.checkIfCertShouldBeObtained(name, false); err != nil { if err := cfg.checkIfCertShouldBeObtained(ctx, name, false); err != nil {
return Certificate{}, fmt.Errorf("certificate is not allowed for server name %s: %v", name, err) return Certificate{}, fmt.Errorf("certificate is not allowed for server name %s: %w", name, err)
} }
// If an external Manager is configured, try to get it from them. // If an external Manager is configured, try to get it from them.
@ -438,7 +444,7 @@ func (cfg *Config) optionalMaintenance(ctx context.Context, log *zap.Logger, cer
// checkIfCertShouldBeObtained checks to see if an on-demand TLS certificate // checkIfCertShouldBeObtained checks to see if an on-demand TLS certificate
// should be obtained for a given domain based upon the config settings. If // should be obtained for a given domain based upon the config settings. If
// a non-nil error is returned, do not issue a new certificate for name. // a non-nil error is returned, do not issue a new certificate for name.
func (cfg *Config) checkIfCertShouldBeObtained(name string, requireOnDemand bool) error { func (cfg *Config) checkIfCertShouldBeObtained(ctx context.Context, name string, requireOnDemand bool) error {
if requireOnDemand && cfg.OnDemand == nil { if requireOnDemand && cfg.OnDemand == nil {
return fmt.Errorf("not configured for on-demand certificate issuance") return fmt.Errorf("not configured for on-demand certificate issuance")
} }
@ -447,7 +453,7 @@ func (cfg *Config) checkIfCertShouldBeObtained(name string, requireOnDemand bool
} }
if cfg.OnDemand != nil { if cfg.OnDemand != nil {
if cfg.OnDemand.DecisionFunc != nil { if cfg.OnDemand.DecisionFunc != nil {
if err := cfg.OnDemand.DecisionFunc(name); err != nil { if err := cfg.OnDemand.DecisionFunc(ctx, name); err != nil {
return fmt.Errorf("decision func: %w", err) return fmt.Errorf("decision func: %w", err)
} }
return nil return nil
@ -685,7 +691,7 @@ func (cfg *Config) renewDynamicCertificate(ctx context.Context, hello *tls.Clien
defer cancel() defer cancel()
// Make sure a certificate for this name should be renewed on-demand // Make sure a certificate for this name should be renewed on-demand
err := cfg.checkIfCertShouldBeObtained(name, true) err := cfg.checkIfCertShouldBeObtained(ctx, name, true)
if err != nil { if err != nil {
// if not, remove from cache (it will be deleted from storage later) // if not, remove from cache (it will be deleted from storage later)
cfg.certCache.mu.Lock() cfg.certCache.mu.Lock()
@ -875,3 +881,52 @@ var (
certLoadWaitChans = make(map[string]chan struct{}) certLoadWaitChans = make(map[string]chan struct{})
certLoadWaitChansMu sync.Mutex certLoadWaitChansMu sync.Mutex
) )
type serializableClientHello struct {
CipherSuites []uint16
ServerName string
SupportedCurves []tls.CurveID
SupportedPoints []uint8
SignatureSchemes []tls.SignatureScheme
SupportedProtos []string
SupportedVersions []uint16
RemoteAddr, LocalAddr net.Addr // values copied from the Conn as they are still useful/needed
conn net.Conn // unexported so it's not serialized
}
// clientHelloWithoutConn returns the data from the ClientHelloInfo without the
// pesky exported Conn field, which often causes an error when serializing because
// the underlying type may be unserializable.
func clientHelloWithoutConn(hello *tls.ClientHelloInfo) serializableClientHello {
if hello == nil {
return serializableClientHello{}
}
var remote, local net.Addr
if hello.Conn != nil {
remote = hello.Conn.RemoteAddr()
local = hello.Conn.LocalAddr()
}
return serializableClientHello{
CipherSuites: hello.CipherSuites,
ServerName: hello.ServerName,
SupportedCurves: hello.SupportedCurves,
SupportedPoints: hello.SupportedPoints,
SignatureSchemes: hello.SignatureSchemes,
SupportedProtos: hello.SupportedProtos,
SupportedVersions: hello.SupportedVersions,
RemoteAddr: remote,
LocalAddr: local,
conn: hello.Conn,
}
}
type helloInfoCtxKey string
// ClientHelloInfoCtxKey is the key by which the ClientHelloInfo can be extracted from
// a context.Context within a DecisionFunc. However, be advised that it is best practice
// that the decision whether to obtain a certificate is be based solely on the name,
// not other properties of the specific connection/client requesting the connection.
// For example, it is not adviseable to use a client's IP address to decide whether to
// allow a certificate. Instead, the ClientHello can be useful for logging, etc.
const ClientHelloInfoCtxKey helloInfoCtxKey = "certmagic:ClientHelloInfo"

View File

@ -17,9 +17,11 @@ package certmagic
import ( import (
"context" "context"
"crypto/x509" "crypto/x509"
"encoding/json"
"encoding/pem" "encoding/pem"
"errors"
"fmt" "fmt"
"log" "io/fs"
"path" "path"
"runtime" "runtime"
"strings" "strings"
@ -390,30 +392,117 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
// CleanStorageOptions specifies how to clean up a storage unit. // CleanStorageOptions specifies how to clean up a storage unit.
type CleanStorageOptions struct { type CleanStorageOptions struct {
OCSPStaples bool // Optional custom logger.
Logger *zap.Logger
// Optional ID of the instance initiating the cleaning.
InstanceID string
// If set, cleaning will be skipped if it was performed
// more recently than this interval.
Interval time.Duration
// Whether to clean cached OCSP staples.
OCSPStaples bool
// Whether to cleanup expired certificates, and if so,
// how long to let them stay after they've expired.
ExpiredCerts bool ExpiredCerts bool
ExpiredCertGracePeriod time.Duration ExpiredCertGracePeriod time.Duration
} }
// CleanStorage removes assets which are no longer useful, // CleanStorage removes assets which are no longer useful,
// according to opts. // according to opts.
func CleanStorage(ctx context.Context, storage Storage, opts CleanStorageOptions) { func CleanStorage(ctx context.Context, storage Storage, opts CleanStorageOptions) error {
const (
lockName = "storage_clean"
storageKey = "last_clean.json"
)
if opts.Logger == nil {
opts.Logger = defaultLogger.Named("clean_storage")
}
opts.Logger = opts.Logger.With(zap.Any("storage", storage))
// storage cleaning should be globally exclusive
if err := storage.Lock(ctx, lockName); err != nil {
return fmt.Errorf("unable to acquire %s lock: %v", lockName, err)
}
defer func() {
if err := storage.Unlock(ctx, lockName); err != nil {
opts.Logger.Error("unable to release lock", zap.Error(err))
return
}
}()
// cleaning should not happen more often than the interval
if opts.Interval > 0 {
lastCleanBytes, err := storage.Load(ctx, storageKey)
if !errors.Is(err, fs.ErrNotExist) {
if err != nil {
return fmt.Errorf("loading last clean timestamp: %v", err)
}
var lastClean lastCleanPayload
err = json.Unmarshal(lastCleanBytes, &lastClean)
if err != nil {
return fmt.Errorf("decoding last clean data: %v", err)
}
lastTLSClean := lastClean["tls"]
if time.Since(lastTLSClean.Timestamp) < opts.Interval {
nextTime := time.Now().Add(opts.Interval)
opts.Logger.Warn("storage cleaning happened too recently; skipping for now",
zap.String("instance", lastTLSClean.InstanceID),
zap.Time("try_again", nextTime),
zap.Duration("try_again_in", time.Until(nextTime)),
)
return nil
}
}
}
opts.Logger.Info("cleaning storage unit")
if opts.OCSPStaples { if opts.OCSPStaples {
err := deleteOldOCSPStaples(ctx, storage) err := deleteOldOCSPStaples(ctx, storage, opts.Logger)
if err != nil { if err != nil {
log.Printf("[ERROR] Deleting old OCSP staples: %v", err) opts.Logger.Error("deleting old OCSP staples", zap.Error(err))
} }
} }
if opts.ExpiredCerts { if opts.ExpiredCerts {
err := deleteExpiredCerts(ctx, storage, opts.ExpiredCertGracePeriod) err := deleteExpiredCerts(ctx, storage, opts.Logger, opts.ExpiredCertGracePeriod)
if err != nil { if err != nil {
log.Printf("[ERROR] Deleting expired certificates: %v", err) opts.Logger.Error("deleting expired certificates staples", zap.Error(err))
} }
} }
// TODO: delete stale locks? // TODO: delete stale locks?
// update the last-clean time
lastCleanBytes, err := json.Marshal(lastCleanPayload{
"tls": lastCleaned{
Timestamp: time.Now(),
InstanceID: opts.InstanceID,
},
})
if err != nil {
return fmt.Errorf("encoding last cleaned info: %v", err)
}
if err := storage.Store(ctx, storageKey, lastCleanBytes); err != nil {
return fmt.Errorf("storing last clean info: %v", err)
}
return nil
} }
func deleteOldOCSPStaples(ctx context.Context, storage Storage) error { type lastCleanPayload map[string]lastCleaned
type lastCleaned struct {
Timestamp time.Time `json:"timestamp"`
InstanceID string `json:"instance_id,omitempty"`
}
func deleteOldOCSPStaples(ctx context.Context, storage Storage, logger *zap.Logger) error {
ocspKeys, err := storage.List(ctx, prefixOCSP, false) ocspKeys, err := storage.List(ctx, prefixOCSP, false)
if err != nil { if err != nil {
// maybe just hasn't been created yet; no big deal // maybe just hasn't been created yet; no big deal
@ -428,7 +517,7 @@ func deleteOldOCSPStaples(ctx context.Context, storage Storage) error {
} }
ocspBytes, err := storage.Load(ctx, key) ocspBytes, err := storage.Load(ctx, key)
if err != nil { if err != nil {
log.Printf("[ERROR] While deleting old OCSP staples, unable to load staple file: %v", err) logger.Error("while deleting old OCSP staples, unable to load staple file", zap.Error(err))
continue continue
} }
resp, err := ocsp.ParseResponse(ocspBytes, nil) resp, err := ocsp.ParseResponse(ocspBytes, nil)
@ -436,7 +525,7 @@ func deleteOldOCSPStaples(ctx context.Context, storage Storage) error {
// contents are invalid; delete it // contents are invalid; delete it
err = storage.Delete(ctx, key) err = storage.Delete(ctx, key)
if err != nil { if err != nil {
log.Printf("[ERROR] Purging corrupt staple file %s: %v", key, err) logger.Error("purging corrupt staple file", zap.String("storage_key", key), zap.Error(err))
} }
continue continue
} }
@ -444,14 +533,14 @@ func deleteOldOCSPStaples(ctx context.Context, storage Storage) error {
// response has expired; delete it // response has expired; delete it
err = storage.Delete(ctx, key) err = storage.Delete(ctx, key)
if err != nil { if err != nil {
log.Printf("[ERROR] Purging expired staple file %s: %v", key, err) logger.Error("purging expired staple file", zap.String("storage_key", key), zap.Error(err))
} }
} }
} }
return nil return nil
} }
func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.Duration) error { func deleteExpiredCerts(ctx context.Context, storage Storage, logger *zap.Logger, gracePeriod time.Duration) error {
issuerKeys, err := storage.List(ctx, prefixCerts, false) issuerKeys, err := storage.List(ctx, prefixCerts, false)
if err != nil { if err != nil {
// maybe just hasn't been created yet; no big deal // maybe just hasn't been created yet; no big deal
@ -461,7 +550,7 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
for _, issuerKey := range issuerKeys { for _, issuerKey := range issuerKeys {
siteKeys, err := storage.List(ctx, issuerKey, false) siteKeys, err := storage.List(ctx, issuerKey, false)
if err != nil { if err != nil {
log.Printf("[ERROR] Listing contents of %s: %v", issuerKey, err) logger.Error("listing contents", zap.String("issuer_key", issuerKey), zap.Error(err))
continue continue
} }
@ -475,7 +564,7 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
siteAssets, err := storage.List(ctx, siteKey, false) siteAssets, err := storage.List(ctx, siteKey, false)
if err != nil { if err != nil {
log.Printf("[ERROR] Listing contents of %s: %v", siteKey, err) logger.Error("listing site contents", zap.String("site_key", siteKey), zap.Error(err))
continue continue
} }
@ -498,18 +587,23 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
} }
if expiredTime := time.Since(expiresAt(cert)); expiredTime >= gracePeriod { if expiredTime := time.Since(expiresAt(cert)); expiredTime >= gracePeriod {
log.Printf("[INFO] Certificate %s expired %s ago; cleaning up", assetKey, expiredTime) logger.Info("certificate expired beyond grace period; cleaning up",
zap.String("asset_key", assetKey),
zap.Duration("expired_for", expiredTime),
zap.Duration("grace_period", gracePeriod))
baseName := strings.TrimSuffix(assetKey, ".crt") baseName := strings.TrimSuffix(assetKey, ".crt")
for _, relatedAsset := range []string{ for _, relatedAsset := range []string{
assetKey, assetKey,
baseName + ".key", baseName + ".key",
baseName + ".json", baseName + ".json",
} { } {
log.Printf("[INFO] Deleting %s because resource expired", relatedAsset) logger.Info("deleting asset because resource expired", zap.String("asset_key", relatedAsset))
err := storage.Delete(ctx, relatedAsset) err := storage.Delete(ctx, relatedAsset)
if err != nil { if err != nil {
log.Printf("[ERROR] Cleaning up asset related to expired certificate for %s: %s: %v", logger.Error("could not clean up asset related to expired certificate",
baseName, relatedAsset, err) zap.String("base_name", baseName),
zap.String("related_asset", relatedAsset),
zap.Error(err))
} }
} }
} }
@ -521,7 +615,7 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
continue continue
} }
if len(siteAssets) == 0 { if len(siteAssets) == 0 {
log.Printf("[INFO] Deleting %s because key is empty", siteKey) logger.Info("deleting site folder because key is empty", zap.String("site_key", siteKey))
err := storage.Delete(ctx, siteKey) err := storage.Delete(ctx, siteKey)
if err != nil { if err != nil {
return fmt.Errorf("deleting empty site folder %s: %v", siteKey, err) return fmt.Errorf("deleting empty site folder %s: %v", siteKey, err)

View File

@ -629,21 +629,23 @@ func robustTryListen(addr string) (net.Listener, error) {
return nil, nil return nil, nil
} }
// hmm, we couldn't connect to the socket, so something else must // Hmm, we couldn't connect to the socket, so something else must
// be wrong, right? wrong!! we've had reports across multiple OSes // be wrong, right? wrong!! Apparently if a port is bound by another
// now that sometimes connections fail even though the OS told us // listener with a specific host, i.e. 'x:1234', we cannot bind to
// that the address was already in use; either the listener is // ':1234' -- it is considered a conflict, but 'y:1234' is not.
// fluctuating between open and closed very, very quickly, or the // I guess we need to assume the conflicting listener is properly
// OS is inconsistent and contradicting itself; I have been unable // configured and continue. But we should tell the user to specify
// to reproduce this, so I'm now resorting to hard-coding substring // the correct ListenHost to avoid conflict or at least so we can
// matching in error messages as a really hacky and unreliable // know that the user is intentional about that port and hopefully
// safeguard against this, until we can idenify exactly what was // has an ACME solver on it.
// happening; see the following threads for more info: //
// History:
// https://caddy.community/t/caddy-retry-error/7317 // https://caddy.community/t/caddy-retry-error/7317
// https://caddy.community/t/v2-upgrade-to-caddy2-failing-with-errors/7423 // https://caddy.community/t/v2-upgrade-to-caddy2-failing-with-errors/7423
// https://github.com/caddyserver/certmagic/issues/250
if strings.Contains(listenErr.Error(), "address already in use") || if strings.Contains(listenErr.Error(), "address already in use") ||
strings.Contains(listenErr.Error(), "one usage of each socket address") { strings.Contains(listenErr.Error(), "one usage of each socket address") {
log.Printf("[WARNING] OS reports a contradiction: %v - but we cannot connect to it, with this error: %v; continuing anyway 🤞 (I don't know what causes this... if you do, please help?)", listenErr, connectErr) log.Printf("[WARNING] %v - be sure to set the ACMEIssuer.ListenHost field; assuming conflicting listener is correctly configured and continuing", listenErr)
return nil, nil return nil, nil
} }
} }

View File

@ -133,7 +133,7 @@ type Locker interface {
// is released or becomes stale. // is released or becomes stale.
// //
// If the named lock represents an idempotent operation, callers // If the named lock represents an idempotent operation, callers
// should awlays check to make sure the work still needs to be // should always check to make sure the work still needs to be
// completed after acquiring the lock. You never know if another // completed after acquiring the lock. You never know if another
// process already completed the task while you were waiting to // process already completed the task while you were waiting to
// acquire it. // acquire it.

View File

@ -116,6 +116,9 @@ type CodecType uint32
var ( var (
H264 = MakeVideoCodecType(avCodecTypeMagic + 1) H264 = MakeVideoCodecType(avCodecTypeMagic + 1)
HEVC = MakeVideoCodecType(avCodecTypeMagic + 2)
VP9 = MakeVideoCodecType(avCodecTypeMagic + 3)
AV1 = MakeVideoCodecType(avCodecTypeMagic + 4)
AAC = MakeAudioCodecType(avCodecTypeMagic + 1) AAC = MakeAudioCodecType(avCodecTypeMagic + 1)
PCM_MULAW = MakeAudioCodecType(avCodecTypeMagic + 2) PCM_MULAW = MakeAudioCodecType(avCodecTypeMagic + 2)
PCM_ALAW = MakeAudioCodecType(avCodecTypeMagic + 3) PCM_ALAW = MakeAudioCodecType(avCodecTypeMagic + 3)
@ -126,10 +129,16 @@ var (
const codecTypeAudioBit = 0x1 const codecTypeAudioBit = 0x1
const codecTypeOtherBits = 1 const codecTypeOtherBits = 1
func (self CodecType) String() string { func (c CodecType) String() string {
switch self { switch c {
case H264: case H264:
return "H264" return "H264"
case HEVC:
return "HEVC"
case VP9:
return "VP9"
case AV1:
return "AV1"
case AAC: case AAC:
return "AAC" return "AAC"
case PCM_MULAW: case PCM_MULAW:
@ -144,12 +153,12 @@ func (self CodecType) String() string {
return "" return ""
} }
func (self CodecType) IsAudio() bool { func (c CodecType) IsAudio() bool {
return self&codecTypeAudioBit != 0 return c&codecTypeAudioBit != 0
} }
func (self CodecType) IsVideo() bool { func (c CodecType) IsVideo() bool {
return self&codecTypeAudioBit == 0 return c&codecTypeAudioBit == 0
} }
// Make a new audio codec type. // Make a new audio codec type.
@ -169,7 +178,7 @@ const avCodecTypeMagic = 233333
// CodecData is some important bytes for initializing audio/video decoder, // CodecData is some important bytes for initializing audio/video decoder,
// can be converted to VideoCodecData or AudioCodecData using: // can be converted to VideoCodecData or AudioCodecData using:
// //
// codecdata.(AudioCodecData) or codecdata.(VideoCodecData) // codecdata.(AudioCodecData) or codecdata.(VideoCodecData)
// //
// for H264, CodecData is AVCDecoderConfigure bytes, includes SPS/PPS. // for H264, CodecData is AVCDecoderConfigure bytes, includes SPS/PPS.
type CodecData interface { type CodecData interface {

View File

@ -2,8 +2,9 @@
package pktque package pktque
import ( import (
"github.com/datarhei/joy4/av"
"time" "time"
"github.com/datarhei/joy4/av"
) )
type Filter interface { type Filter interface {

View File

@ -3,10 +3,11 @@ package aacparser
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits"
"io" "io"
"time" "time"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits"
) )
// copied from libavcodec/mpeg4audio.h // copied from libavcodec/mpeg4audio.h

View File

@ -0,0 +1,44 @@
package av1parser
import (
"github.com/datarhei/joy4/av"
)
type CodecData struct {
Record []byte
IsMpeg2TS bool
}
func (codec CodecData) Type() av.CodecType {
return av.AV1
}
func (codec CodecData) AV1DecoderConfRecordBytes() []byte {
return codec.Record
}
func (codec CodecData) AV1VideoDescriptorBytes() []byte {
return codec.Record
}
func (codec CodecData) Width() int {
return 0
}
func (codec CodecData) Height() int {
return 0
}
func NewCodecDataFromAV1DecoderConfRecord(record []byte) (data CodecData, err error) {
data.Record = record
data.IsMpeg2TS = false
return
}
func NewCodecDataFromAV1VideoDescriptor(record []byte) (data CodecData, err error) {
data.Record = record
data.IsMpeg2TS = true
return
}

View File

@ -3,6 +3,7 @@ package h264parser
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/datarhei/joy4/av" "github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits" "github.com/datarhei/joy4/utils/bits"
"github.com/datarhei/joy4/utils/bits/pio" "github.com/datarhei/joy4/utils/bits/pio"

View File

@ -0,0 +1,695 @@
package hevcparser
// based on https://github.com/deepch/vdk/blob/v0.0.21/codec/h265parser/parser.go
import (
"bytes"
"errors"
"fmt"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits"
"github.com/datarhei/joy4/utils/bits/pio"
)
type SPSInfo struct {
ProfileIdc uint
LevelIdc uint
CropLeft uint
CropRight uint
CropTop uint
CropBottom uint
Width uint
Height uint
PicWidthInLumaSamples uint
PicHeightInLumaSamples uint
generalProfileSpace uint
generalTierFlag uint
generalProfileIDC uint
generalProfileCompatibilityFlags uint32
generalConstraintIndicatorFlags uint64
generalLevelIDC uint
}
const (
NAL_UNIT_CODED_SLICE_TRAIL_N = 0
NAL_UNIT_CODED_SLICE_TRAIL_R = 1
NAL_UNIT_CODED_SLICE_TSA_N = 2
NAL_UNIT_CODED_SLICE_TSA_R = 3
NAL_UNIT_CODED_SLICE_STSA_N = 4
NAL_UNIT_CODED_SLICE_STSA_R = 5
NAL_UNIT_CODED_SLICE_RADL_N = 6
NAL_UNIT_CODED_SLICE_RADL_R = 7
NAL_UNIT_CODED_SLICE_RASL_N = 8
NAL_UNIT_CODED_SLICE_RASL_R = 9
NAL_UNIT_RESERVED_VCL_N10 = 10
NAL_UNIT_RESERVED_VCL_R11 = 11
NAL_UNIT_RESERVED_VCL_N12 = 12
NAL_UNIT_RESERVED_VCL_R13 = 13
NAL_UNIT_RESERVED_VCL_N14 = 14
NAL_UNIT_RESERVED_VCL_R15 = 15
NAL_UNIT_CODED_SLICE_BLA_W_LP = 16
NAL_UNIT_CODED_SLICE_BLA_W_RADL = 17
NAL_UNIT_CODED_SLICE_BLA_N_LP = 18
NAL_UNIT_CODED_SLICE_IDR_W_RADL = 19
NAL_UNIT_CODED_SLICE_IDR_N_LP = 20
NAL_UNIT_CODED_SLICE_CRA = 21
NAL_UNIT_RESERVED_IRAP_VCL22 = 22
NAL_UNIT_RESERVED_IRAP_VCL23 = 23
NAL_UNIT_RESERVED_VCL24 = 24
NAL_UNIT_RESERVED_VCL25 = 25
NAL_UNIT_RESERVED_VCL26 = 26
NAL_UNIT_RESERVED_VCL27 = 27
NAL_UNIT_RESERVED_VCL28 = 28
NAL_UNIT_RESERVED_VCL29 = 29
NAL_UNIT_RESERVED_VCL30 = 30
NAL_UNIT_RESERVED_VCL31 = 31
NAL_UNIT_VPS = 32
NAL_UNIT_SPS = 33
NAL_UNIT_PPS = 34
NAL_UNIT_ACCESS_UNIT_DELIMITER = 35
NAL_UNIT_EOS = 36
NAL_UNIT_EOB = 37
NAL_UNIT_FILLER_DATA = 38
NAL_UNIT_PREFIX_SEI = 39
NAL_UNIT_SUFFIX_SEI = 40
NAL_UNIT_RESERVED_NVCL41 = 41
NAL_UNIT_RESERVED_NVCL42 = 42
NAL_UNIT_RESERVED_NVCL43 = 43
NAL_UNIT_RESERVED_NVCL44 = 44
NAL_UNIT_RESERVED_NVCL45 = 45
NAL_UNIT_RESERVED_NVCL46 = 46
NAL_UNIT_RESERVED_NVCL47 = 47
NAL_UNIT_UNSPECIFIED_48 = 48
NAL_UNIT_UNSPECIFIED_49 = 49
NAL_UNIT_UNSPECIFIED_50 = 50
NAL_UNIT_UNSPECIFIED_51 = 51
NAL_UNIT_UNSPECIFIED_52 = 52
NAL_UNIT_UNSPECIFIED_53 = 53
NAL_UNIT_UNSPECIFIED_54 = 54
NAL_UNIT_UNSPECIFIED_55 = 55
NAL_UNIT_UNSPECIFIED_56 = 56
NAL_UNIT_UNSPECIFIED_57 = 57
NAL_UNIT_UNSPECIFIED_58 = 58
NAL_UNIT_UNSPECIFIED_59 = 59
NAL_UNIT_UNSPECIFIED_60 = 60
NAL_UNIT_UNSPECIFIED_61 = 61
NAL_UNIT_UNSPECIFIED_62 = 62
NAL_UNIT_UNSPECIFIED_63 = 63
NAL_UNIT_INVALID = 64
)
const (
MAX_VPS_COUNT = 16
MAX_SUB_LAYERS = 7
MAX_SPS_COUNT = 32
)
var (
ErrorHEVCIncorectUnitSize = errors.New("incorrect unit size")
ErrorHECVIncorectUnitType = errors.New("incorrect unit type")
)
var StartCodeBytes = []byte{0, 0, 1}
var AUDBytes = []byte{0, 0, 0, 1, 0x9, 0xf0, 0, 0, 0, 1} // AUD
const (
NALU_RAW = iota
NALU_AVCC
NALU_ANNEXB
)
func SplitNALUs(b []byte) (nalus [][]byte, typ int) {
if len(b) < 4 {
return [][]byte{b}, NALU_RAW
}
val3 := pio.U24BE(b)
val4 := pio.U32BE(b)
if val4 <= uint32(len(b)) {
_val4 := val4
_b := b[4:]
nalus := [][]byte{}
for {
nalus = append(nalus, _b[:_val4])
_b = _b[_val4:]
if len(_b) < 4 {
break
}
_val4 = pio.U32BE(_b)
_b = _b[4:]
if _val4 > uint32(len(_b)) {
break
}
}
if len(_b) == 0 {
return nalus, NALU_AVCC
}
}
if val3 == 1 || val4 == 1 {
_val3 := val3
_val4 := val4
start := 0
pos := 0
for {
if start != pos {
nalus = append(nalus, b[start:pos])
}
if _val3 == 1 {
pos += 3
} else if _val4 == 1 {
pos += 4
}
start = pos
if start == len(b) {
break
}
_val3 = 0
_val4 = 0
for pos < len(b) {
if pos+2 < len(b) && b[pos] == 0 {
_val3 = pio.U24BE(b[pos:])
if _val3 == 0 {
if pos+3 < len(b) {
_val4 = uint32(b[pos+3])
if _val4 == 1 {
break
}
}
} else if _val3 == 1 {
break
}
pos++
} else {
pos++
}
}
}
typ = NALU_ANNEXB
return
}
return [][]byte{b}, NALU_RAW
}
func ParseSPS(sps []byte) (ctx SPSInfo, err error) {
if len(sps) < 2 {
err = ErrorHEVCIncorectUnitSize
return
}
rbsp := nal2rbsp(sps[2:])
br := &bits.GolombBitReader{R: bytes.NewReader(rbsp)}
// sps_video_parameter_set_id
if _, err = br.ReadBits(4); err != nil {
return
}
// sps_max_sub_layers_minus1
spsMaxSubLayersMinus1, err := br.ReadBits(3)
if err != nil {
return
}
// sps_temporal_id_nesting_flag
if _, err = br.ReadBit(); err != nil {
return
}
// profile_tier_level( 1, sps_max_sub_layers_minus1 )
if err = parsePTL(br, &ctx, spsMaxSubLayersMinus1); err != nil {
return
}
// sps_seq_parameter_set_id
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// chroma_format_idc
var chroma_format_idc uint
if chroma_format_idc, err = br.ReadExponentialGolombCode(); err != nil {
return
}
if chroma_format_idc == 3 {
// separate_colour_plane_flag
if _, err = br.ReadBit(); err != nil {
return
}
}
// Table 6-1, Section 6.2
var subWidthC uint
var subHeightC uint
switch chroma_format_idc {
case 0:
subWidthC, subHeightC = 1, 1
case 1:
subWidthC, subHeightC = 2, 2
case 2:
subWidthC, subHeightC = 2, 1
case 3:
subWidthC, subHeightC = 1, 1
}
// pic_width_in_luma_samples
if ctx.PicWidthInLumaSamples, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// pic_height_in_luma_samples
if ctx.PicHeightInLumaSamples, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// conformance_window_flag
conformanceWindowFlag, err := br.ReadBit()
if err != nil {
return
}
var conf_win_left_offset uint
var conf_win_right_offset uint
var conf_win_top_offset uint
var conf_win_bottom_offset uint
if conformanceWindowFlag != 0 {
// conf_win_left_offset
conf_win_left_offset, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
ctx.CropLeft = subWidthC * conf_win_left_offset
// conf_win_right_offset
conf_win_right_offset, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
ctx.CropRight = subWidthC * conf_win_right_offset
// conf_win_top_offset
conf_win_top_offset, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
ctx.CropTop = subHeightC * conf_win_top_offset
// conf_win_bottom_offset
conf_win_bottom_offset, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
ctx.CropBottom = subHeightC * conf_win_bottom_offset
}
ctx.Width = ctx.PicWidthInLumaSamples - ctx.CropLeft - ctx.CropRight
ctx.Height = ctx.PicHeightInLumaSamples - ctx.CropTop - ctx.CropBottom
// bit_depth_luma_minus8
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// bit_depth_chroma_minus8
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// log2_max_pic_order_cnt_lsb_minus4
_, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
// sps_sub_layer_ordering_info_present_flag
spsSubLayerOrderingInfoPresentFlag, err := br.ReadBit()
if err != nil {
return
}
var i uint
if spsSubLayerOrderingInfoPresentFlag != 0 {
i = 0
} else {
i = spsMaxSubLayersMinus1
}
for ; i <= spsMaxSubLayersMinus1; i++ {
// sps_max_dec_pic_buffering_minus1[ i ]
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// sps_max_num_reorder_pics[ i ]
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// sps_max_latency_increase_plus1[ i ]
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
}
// log2_min_luma_coding_block_size_minus3
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// log2_diff_max_min_luma_coding_block_size
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// log2_min_luma_transform_block_size_minus2
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// log2_diff_max_min_luma_transform_block_size
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// max_transform_hierarchy_depth_inter
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// max_transform_hierarchy_depth_intra
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
return
}
func parsePTL(br *bits.GolombBitReader, ctx *SPSInfo, maxSubLayersMinus1 uint) error {
var err error
var ptl SPSInfo
if ptl.generalProfileSpace, err = br.ReadBits(2); err != nil {
return err
}
if ptl.generalTierFlag, err = br.ReadBit(); err != nil {
return err
}
if ptl.generalProfileIDC, err = br.ReadBits(5); err != nil {
return err
}
if ptl.generalProfileCompatibilityFlags, err = br.ReadBits32(32); err != nil {
return err
}
if ptl.generalConstraintIndicatorFlags, err = br.ReadBits64(48); err != nil {
return err
}
if ptl.generalLevelIDC, err = br.ReadBits(8); err != nil {
return err
}
updatePTL(ctx, &ptl)
if maxSubLayersMinus1 == 0 {
return nil
}
subLayerProfilePresentFlag := make([]uint, maxSubLayersMinus1)
subLayerLevelPresentFlag := make([]uint, maxSubLayersMinus1)
for i := uint(0); i < maxSubLayersMinus1; i++ {
if subLayerProfilePresentFlag[i], err = br.ReadBit(); err != nil {
return err
}
if subLayerLevelPresentFlag[i], err = br.ReadBit(); err != nil {
return err
}
}
if maxSubLayersMinus1 > 0 {
for i := maxSubLayersMinus1; i < 8; i++ {
if _, err = br.ReadBits(2); err != nil {
return err
}
}
}
for i := uint(0); i < maxSubLayersMinus1; i++ {
if subLayerProfilePresentFlag[i] != 0 {
if _, err = br.ReadBits32(32); err != nil {
return err
}
if _, err = br.ReadBits32(32); err != nil {
return err
}
if _, err = br.ReadBits32(24); err != nil {
return err
}
}
if subLayerLevelPresentFlag[i] != 0 {
if _, err = br.ReadBits(8); err != nil {
return err
}
}
}
return nil
}
func updatePTL(ctx, ptl *SPSInfo) {
ctx.generalProfileSpace = ptl.generalProfileSpace
if ptl.generalTierFlag > ctx.generalTierFlag {
ctx.generalLevelIDC = ptl.generalLevelIDC
ctx.generalTierFlag = ptl.generalTierFlag
} else {
if ptl.generalLevelIDC > ctx.generalLevelIDC {
ctx.generalLevelIDC = ptl.generalLevelIDC
}
}
if ptl.generalProfileIDC > ctx.generalProfileIDC {
ctx.generalProfileIDC = ptl.generalProfileIDC
}
ctx.generalProfileCompatibilityFlags &= ptl.generalProfileCompatibilityFlags
ctx.generalConstraintIndicatorFlags &= ptl.generalConstraintIndicatorFlags
}
func nal2rbsp(nal []byte) []byte {
return bytes.Replace(nal, []byte{0x0, 0x0, 0x3}, []byte{0x0, 0x0}, -1)
}
type CodecData struct {
Record []byte
RecordInfo HEVCDecoderConfRecord
SPSInfo SPSInfo
}
func (codec CodecData) Type() av.CodecType {
return av.HEVC
}
func (codec CodecData) HEVCDecoderConfRecordBytes() []byte {
return codec.Record
}
func (codec CodecData) SPS() []byte {
return codec.RecordInfo.SPS[0]
}
func (codec CodecData) PPS() []byte {
return codec.RecordInfo.PPS[0]
}
func (codec CodecData) VPS() []byte {
return codec.RecordInfo.VPS[0]
}
func (codec CodecData) Width() int {
return int(codec.SPSInfo.Width)
}
func (codec CodecData) Height() int {
return int(codec.SPSInfo.Height)
}
func NewCodecDataFromHEVCDecoderConfRecord(record []byte) (self CodecData, err error) {
self.Record = record
if _, err = (&self.RecordInfo).Unmarshal(record); err != nil {
return
}
if len(self.RecordInfo.SPS) == 0 {
err = fmt.Errorf("hevcparser: no SPS found in HEVCDecoderConfRecord")
return
}
if len(self.RecordInfo.PPS) == 0 {
err = fmt.Errorf("hevcparser: no PPS found in HEVCDecoderConfRecord")
return
}
if len(self.RecordInfo.VPS) == 0 {
err = fmt.Errorf("hevcparser: no VPS found in HEVCDecoderConfRecord")
return
}
if self.SPSInfo, err = ParseSPS(self.RecordInfo.SPS[0]); err != nil {
err = fmt.Errorf("hevcparser: parse SPS failed(%s)", err)
return
}
return
}
func NewCodecDataFromVPSAndSPSAndPPS(vps, sps, pps []byte) (self CodecData, err error) {
recordinfo := HEVCDecoderConfRecord{}
recordinfo.HEVCProfileIndication = sps[3]
recordinfo.ProfileCompatibility = sps[4]
recordinfo.HEVCLevelIndication = sps[5]
recordinfo.SPS = [][]byte{sps}
recordinfo.PPS = [][]byte{pps}
recordinfo.VPS = [][]byte{vps}
recordinfo.LengthSizeMinusOne = 3
if self.SPSInfo, err = ParseSPS(sps); err != nil {
return
}
buf := make([]byte, recordinfo.Len())
recordinfo.Marshal(buf, self.SPSInfo)
self.RecordInfo = recordinfo
self.Record = buf
return
}
type HEVCDecoderConfRecord struct {
HEVCProfileIndication uint8
ProfileCompatibility uint8
HEVCLevelIndication uint8
LengthSizeMinusOne uint8
VPS [][]byte
SPS [][]byte
PPS [][]byte
}
var ErrDecconfInvalid = fmt.Errorf("hevcparser: HEVCDecoderConfRecord invalid")
func (record *HEVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {
if len(b) < 30 {
err = ErrDecconfInvalid
return
}
record.HEVCProfileIndication = b[1]
record.ProfileCompatibility = b[2]
record.HEVCLevelIndication = b[3]
record.LengthSizeMinusOne = b[4] & 0x03
vpscount := int(b[25] & 0x1f)
n += 26
for i := 0; i < vpscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
vpslen := int(pio.U16BE(b[n:]))
n += 2
if len(b) < n+vpslen {
err = ErrDecconfInvalid
return
}
record.VPS = append(record.VPS, b[n:n+vpslen])
n += vpslen
}
if len(b) < n+1 {
err = ErrDecconfInvalid
return
}
n++
n++
spscount := int(b[n])
n++
for i := 0; i < spscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
spslen := int(pio.U16BE(b[n:]))
n += 2
if len(b) < n+spslen {
err = ErrDecconfInvalid
return
}
record.SPS = append(record.SPS, b[n:n+spslen])
n += spslen
}
n++
n++
ppscount := int(b[n])
n++
for i := 0; i < ppscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
ppslen := int(pio.U16BE(b[n:]))
n += 2
if len(b) < n+ppslen {
err = ErrDecconfInvalid
return
}
record.PPS = append(record.PPS, b[n:n+ppslen])
n += ppslen
}
return
}
func (record HEVCDecoderConfRecord) Len() (n int) {
n = 23
for _, sps := range record.SPS {
n += 5 + len(sps)
}
for _, pps := range record.PPS {
n += 5 + len(pps)
}
for _, vps := range record.VPS {
n += 5 + len(vps)
}
return
}
func (record HEVCDecoderConfRecord) Marshal(b []byte, si SPSInfo) (n int) {
b[0] = 1
b[1] = record.HEVCProfileIndication
b[2] = record.ProfileCompatibility
b[3] = record.HEVCLevelIndication
b[21] = 3
b[22] = 3
n += 23
b[n] = (record.VPS[0][0] >> 1) & 0x3f
n++
b[n] = byte(len(record.VPS) >> 8)
n++
b[n] = byte(len(record.VPS))
n++
for _, vps := range record.VPS {
pio.PutU16BE(b[n:], uint16(len(vps)))
n += 2
copy(b[n:], vps)
n += len(vps)
}
b[n] = (record.SPS[0][0] >> 1) & 0x3f
n++
b[n] = byte(len(record.SPS) >> 8)
n++
b[n] = byte(len(record.SPS))
n++
for _, sps := range record.SPS {
pio.PutU16BE(b[n:], uint16(len(sps)))
n += 2
copy(b[n:], sps)
n += len(sps)
}
b[n] = (record.PPS[0][0] >> 1) & 0x3f
n++
b[n] = byte(len(record.PPS) >> 8)
n++
b[n] = byte(len(record.PPS))
n++
for _, pps := range record.PPS {
pio.PutU16BE(b[n:], uint16(len(pps)))
n += 2
copy(b[n:], pps)
n += len(pps)
}
return
}

View File

@ -0,0 +1,31 @@
package vp9parser
import (
"github.com/datarhei/joy4/av"
)
type CodecData struct {
Record []byte
}
func (codec CodecData) Type() av.CodecType {
return av.VP9
}
func (codec CodecData) VPDecoderConfRecordBytes() []byte {
return codec.Record
}
func (codec CodecData) Width() int {
return 0
}
func (codec CodecData) Height() int {
return 0
}
func NewCodecDataFromVPDecoderConfRecord(record []byte) (self CodecData, err error) {
self.Record = record
return
}

View File

@ -3,15 +3,19 @@ package flv
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io"
"github.com/datarhei/joy4/av" "github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/av/avutil" "github.com/datarhei/joy4/av/avutil"
"github.com/datarhei/joy4/codec" "github.com/datarhei/joy4/codec"
"github.com/datarhei/joy4/codec/aacparser" "github.com/datarhei/joy4/codec/aacparser"
"github.com/datarhei/joy4/codec/av1parser"
"github.com/datarhei/joy4/codec/fake" "github.com/datarhei/joy4/codec/fake"
"github.com/datarhei/joy4/codec/h264parser" "github.com/datarhei/joy4/codec/h264parser"
"github.com/datarhei/joy4/codec/hevcparser"
"github.com/datarhei/joy4/codec/vp9parser"
"github.com/datarhei/joy4/format/flv/flvio" "github.com/datarhei/joy4/format/flv/flvio"
"github.com/datarhei/joy4/utils/bits/pio" "github.com/datarhei/joy4/utils/bits/pio"
"io"
) )
var MaxProbePacketCount = 20 var MaxProbePacketCount = 20
@ -27,16 +31,29 @@ func NewMetadataByStreams(streams []av.CodecData) (metadata flvio.AMFMap, err er
switch typ { switch typ {
case av.H264: case av.H264:
metadata["videocodecid"] = flvio.VIDEO_H264 metadata["videocodecid"] = flvio.VIDEO_H264
case av.HEVC:
metadata["videocodecid"] = flvio.FourCCToFloat(flvio.FOURCC_HEVC)
case av.VP9:
metadata["videocodecid"] = flvio.FourCCToFloat(flvio.FOURCC_VP9)
case av.AV1:
metadata["videocodecid"] = flvio.FourCCToFloat(flvio.FOURCC_AV1)
default: default:
err = fmt.Errorf("flv: metadata: unsupported video codecType=%v", stream.Type()) err = fmt.Errorf("flv: metadata: unsupported video codecType=%v", stream.Type())
return return
} }
metadata["width"] = stream.Width() width, height := stream.Width(), stream.Height()
metadata["height"] = stream.Height()
metadata["displayWidth"] = stream.Width() if width != 0 {
metadata["displayHeight"] = stream.Height() metadata["width"] = width
metadata["displayWidth"] = width
}
if height != 0 {
metadata["height"] = height
metadata["displayHeight"] = height
}
case typ.IsAudio(): case typ.IsAudio():
stream := _stream.(av.AudioCodecData) stream := _stream.(av.AudioCodecData)
@ -68,36 +85,98 @@ type Prober struct {
CachedPkts []av.Packet CachedPkts []av.Packet
} }
func (self *Prober) CacheTag(_tag flvio.Tag, timestamp int32) { func (prober *Prober) CacheTag(_tag flvio.Tag, timestamp int32) {
pkt, _ := self.TagToPacket(_tag, timestamp) pkt, _ := prober.TagToPacket(_tag, timestamp)
self.CachedPkts = append(self.CachedPkts, pkt) prober.CachedPkts = append(prober.CachedPkts, pkt)
} }
func (self *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) { func (prober *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) {
self.PushedCount++ prober.PushedCount++
if self.PushedCount > MaxProbePacketCount { if prober.PushedCount > MaxProbePacketCount {
err = fmt.Errorf("flv: max probe packet count reached") err = fmt.Errorf("flv: max probe packet count reached")
return return
} }
switch tag.Type { switch tag.Type {
case flvio.TAG_VIDEO: case flvio.TAG_VIDEO:
switch tag.AVCPacketType { if tag.IsExHeader {
case flvio.AVC_SEQHDR: if tag.FourCC == flvio.FOURCC_HEVC {
if !self.GotVideo { if tag.PacketType == flvio.PKTTYPE_SEQUENCE_START {
var stream h264parser.CodecData if !prober.GotVideo {
if stream, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(tag.Data); err != nil { var stream hevcparser.CodecData
err = fmt.Errorf("flv: h264 seqhdr invalid: %s", err.Error()) //fmt.Printf("got HEVC sequence start:\n%s\n", hex.Dump(tag.Data))
return if stream, err = hevcparser.NewCodecDataFromHEVCDecoderConfRecord(tag.Data); err != nil {
err = fmt.Errorf("flv: hevc seqhdr invalid: %s", err.Error())
return
}
prober.VideoStreamIdx = len(prober.Streams)
prober.Streams = append(prober.Streams, stream)
prober.GotVideo = true
}
} else if tag.PacketType == flvio.PKTTYPE_CODED_FRAMES || tag.PacketType == flvio.PKTTYPE_CODED_FRAMESX {
prober.CacheTag(tag, timestamp)
} }
self.VideoStreamIdx = len(self.Streams) } else if tag.FourCC == flvio.FOURCC_VP9 {
self.Streams = append(self.Streams, stream) if tag.PacketType == flvio.PKTTYPE_SEQUENCE_START {
self.GotVideo = true if !prober.GotVideo {
} var stream vp9parser.CodecData
//fmt.Printf("got VP9 sequence start:\n%s\n", hex.Dump(tag.Data))
if stream, err = vp9parser.NewCodecDataFromVPDecoderConfRecord(tag.Data); err != nil {
err = fmt.Errorf("flv: vp9 seqhdr invalid: %s", err.Error())
return
}
prober.VideoStreamIdx = len(prober.Streams)
prober.Streams = append(prober.Streams, stream)
prober.GotVideo = true
}
} else if tag.PacketType == flvio.PKTTYPE_CODED_FRAMES || tag.PacketType == flvio.PKTTYPE_CODED_FRAMESX {
prober.CacheTag(tag, timestamp)
}
} else if tag.FourCC == flvio.FOURCC_AV1 {
if tag.PacketType == flvio.PKTTYPE_SEQUENCE_START || tag.PacketType == flvio.PKTTYPE_MPEG2TS_SEQUENCE_START {
if !prober.GotVideo {
var stream av1parser.CodecData
case flvio.AVC_NALU: if tag.PacketType == flvio.PKTTYPE_SEQUENCE_START {
self.CacheTag(tag, timestamp) //fmt.Printf("got AV1 sequence start:\n%s\n", hex.Dump(tag.Data))
if stream, err = av1parser.NewCodecDataFromAV1DecoderConfRecord(tag.Data); err != nil {
err = fmt.Errorf("flv: av1 seqhdr invalid: %s", err.Error())
return
}
} else {
//fmt.Printf("got AV1 video descriptor:\n%s\n", hex.Dump(tag.Data))
if stream, err = av1parser.NewCodecDataFromAV1VideoDescriptor(tag.Data); err != nil {
err = fmt.Errorf("flv: av1 video descriptor invalid: %s", err.Error())
return
}
}
prober.VideoStreamIdx = len(prober.Streams)
prober.Streams = append(prober.Streams, stream)
prober.GotVideo = true
}
} else if tag.PacketType == flvio.PKTTYPE_CODED_FRAMES || tag.PacketType == flvio.PKTTYPE_CODED_FRAMESX {
prober.CacheTag(tag, timestamp)
}
}
} else {
switch tag.AVCPacketType {
case flvio.AVC_SEQHDR:
if !prober.GotVideo {
var stream h264parser.CodecData
//fmt.Printf("got H264 sequence start:\n%s\n", hex.Dump(tag.Data))
if stream, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(tag.Data); err != nil {
err = fmt.Errorf("flv: h264 seqhdr invalid: %s", err.Error())
return
}
prober.VideoStreamIdx = len(prober.Streams)
prober.Streams = append(prober.Streams, stream)
prober.GotVideo = true
}
case flvio.AVC_NALU:
prober.CacheTag(tag, timestamp)
}
} }
case flvio.TAG_AUDIO: case flvio.TAG_AUDIO:
@ -105,42 +184,42 @@ func (self *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) {
case flvio.SOUND_AAC: case flvio.SOUND_AAC:
switch tag.AACPacketType { switch tag.AACPacketType {
case flvio.AAC_SEQHDR: case flvio.AAC_SEQHDR:
if !self.GotAudio { if !prober.GotAudio {
var stream aacparser.CodecData var stream aacparser.CodecData
if stream, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(tag.Data); err != nil { if stream, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(tag.Data); err != nil {
err = fmt.Errorf("flv: aac seqhdr invalid") err = fmt.Errorf("flv: aac seqhdr invalid")
return return
} }
self.AudioStreamIdx = len(self.Streams) prober.AudioStreamIdx = len(prober.Streams)
self.Streams = append(self.Streams, stream) prober.Streams = append(prober.Streams, stream)
self.GotAudio = true prober.GotAudio = true
} }
case flvio.AAC_RAW: case flvio.AAC_RAW:
self.CacheTag(tag, timestamp) prober.CacheTag(tag, timestamp)
} }
case flvio.SOUND_SPEEX: case flvio.SOUND_SPEEX:
if !self.GotAudio { if !prober.GotAudio {
stream := codec.NewSpeexCodecData(16000, tag.ChannelLayout()) stream := codec.NewSpeexCodecData(16000, tag.ChannelLayout())
self.AudioStreamIdx = len(self.Streams) prober.AudioStreamIdx = len(prober.Streams)
self.Streams = append(self.Streams, stream) prober.Streams = append(prober.Streams, stream)
self.GotAudio = true prober.GotAudio = true
self.CacheTag(tag, timestamp) prober.CacheTag(tag, timestamp)
} }
case flvio.SOUND_NELLYMOSER: case flvio.SOUND_NELLYMOSER:
if !self.GotAudio { if !prober.GotAudio {
stream := fake.CodecData{ stream := fake.CodecData{
CodecType_: av.NELLYMOSER, CodecType_: av.NELLYMOSER,
SampleRate_: 16000, SampleRate_: 16000,
SampleFormat_: av.S16, SampleFormat_: av.S16,
ChannelLayout_: tag.ChannelLayout(), ChannelLayout_: tag.ChannelLayout(),
} }
self.AudioStreamIdx = len(self.Streams) prober.AudioStreamIdx = len(prober.Streams)
self.Streams = append(self.Streams, stream) prober.Streams = append(prober.Streams, stream)
self.GotAudio = true prober.GotAudio = true
self.CacheTag(tag, timestamp) prober.CacheTag(tag, timestamp)
} }
} }
@ -149,25 +228,25 @@ func (self *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) {
return return
} }
func (self *Prober) Probed() (ok bool) { func (prober *Prober) Probed() (ok bool) {
if self.HasAudio || self.HasVideo { if prober.HasAudio || prober.HasVideo {
if self.HasAudio == self.GotAudio && self.HasVideo == self.GotVideo { if prober.HasAudio == prober.GotAudio && prober.HasVideo == prober.GotVideo {
return true return true
} }
} else { } else {
if self.PushedCount == MaxProbePacketCount { if prober.PushedCount == MaxProbePacketCount {
return true return true
} }
} }
return return
} }
func (self *Prober) TagToPacket(tag flvio.Tag, timestamp int32) (pkt av.Packet, ok bool) { func (prober *Prober) TagToPacket(tag flvio.Tag, timestamp int32) (pkt av.Packet, ok bool) {
switch tag.Type { switch tag.Type {
case flvio.TAG_VIDEO: case flvio.TAG_VIDEO:
pkt.Idx = int8(self.VideoStreamIdx) pkt.Idx = int8(prober.VideoStreamIdx)
switch tag.AVCPacketType { switch tag.PacketType {
case flvio.AVC_NALU: case flvio.PKTTYPE_CODED_FRAMES, flvio.PKTTYPE_CODED_FRAMESX:
ok = true ok = true
pkt.Data = tag.Data pkt.Data = tag.Data
pkt.CompositionTime = flvio.TsToTime(tag.CompositionTime) pkt.CompositionTime = flvio.TsToTime(tag.CompositionTime)
@ -175,7 +254,7 @@ func (self *Prober) TagToPacket(tag flvio.Tag, timestamp int32) (pkt av.Packet,
} }
case flvio.TAG_AUDIO: case flvio.TAG_AUDIO:
pkt.Idx = int8(self.AudioStreamIdx) pkt.Idx = int8(prober.AudioStreamIdx)
switch tag.SoundFormat { switch tag.SoundFormat {
case flvio.SOUND_AAC: case flvio.SOUND_AAC:
switch tag.AACPacketType { switch tag.AACPacketType {
@ -198,13 +277,13 @@ func (self *Prober) TagToPacket(tag flvio.Tag, timestamp int32) (pkt av.Packet,
return return
} }
func (self *Prober) Empty() bool { func (prober *Prober) Empty() bool {
return len(self.CachedPkts) == 0 return len(prober.CachedPkts) == 0
} }
func (self *Prober) PopPacket() av.Packet { func (prober *Prober) PopPacket() av.Packet {
pkt := self.CachedPkts[0] pkt := prober.CachedPkts[0]
self.CachedPkts = self.CachedPkts[1:] prober.CachedPkts = prober.CachedPkts[1:]
return pkt return pkt
} }
@ -219,6 +298,55 @@ func CodecDataToTag(stream av.CodecData) (_tag flvio.Tag, ok bool, err error) {
Data: h264.AVCDecoderConfRecordBytes(), Data: h264.AVCDecoderConfRecordBytes(),
FrameType: flvio.FRAME_KEY, FrameType: flvio.FRAME_KEY,
} }
//fmt.Printf("set H264 sequence start:\n%v\n", hex.Dump(tag.Data))
ok = true
_tag = tag
case av.HEVC:
hevc := stream.(hevcparser.CodecData)
tag := flvio.Tag{
Type: flvio.TAG_VIDEO,
IsExHeader: true,
PacketType: flvio.PKTTYPE_SEQUENCE_START,
FourCC: flvio.FOURCC_HEVC,
Data: hevc.HEVCDecoderConfRecordBytes(),
FrameType: flvio.FRAME_KEY,
}
//fmt.Printf("set HEVC sequence start:\n%v\n", hex.Dump(tag.Data))
ok = true
_tag = tag
case av.VP9:
vp9 := stream.(vp9parser.CodecData)
tag := flvio.Tag{
Type: flvio.TAG_VIDEO,
IsExHeader: true,
PacketType: flvio.PKTTYPE_SEQUENCE_START,
FourCC: flvio.FOURCC_VP9,
Data: vp9.VPDecoderConfRecordBytes(),
FrameType: flvio.FRAME_KEY,
}
//fmt.Printf("set VP9 sequence start:\n%v\n", hex.Dump(tag.Data))
ok = true
_tag = tag
case av.AV1:
av1 := stream.(av1parser.CodecData)
tag := flvio.Tag{
Type: flvio.TAG_VIDEO,
IsExHeader: true,
PacketType: flvio.PKTTYPE_SEQUENCE_START,
FourCC: flvio.FOURCC_AV1,
Data: av1.AV1DecoderConfRecordBytes(),
FrameType: flvio.FRAME_KEY,
}
if av1.IsMpeg2TS {
tag.PacketType = flvio.PKTTYPE_MPEG2TS_SEQUENCE_START
tag.Data = av1.AV1VideoDescriptorBytes()
}
//fmt.Printf("set AV1 sequence start:\n%v\n", hex.Dump(tag.Data))
ok = true ok = true
_tag = tag _tag = tag
@ -272,6 +400,58 @@ func PacketToTag(pkt av.Packet, stream av.CodecData) (tag flvio.Tag, timestamp i
tag.FrameType = flvio.FRAME_INTER tag.FrameType = flvio.FRAME_INTER
} }
case av.HEVC:
tag = flvio.Tag{
Type: flvio.TAG_VIDEO,
IsExHeader: true,
PacketType: flvio.PKTTYPE_CODED_FRAMES,
CompositionTime: flvio.TimeToTs(pkt.CompositionTime),
FourCC: flvio.FOURCC_HEVC,
Data: pkt.Data,
}
if pkt.CompositionTime == 0 {
tag.PacketType = flvio.PKTTYPE_CODED_FRAMESX
}
if pkt.IsKeyFrame {
tag.FrameType = flvio.FRAME_KEY
} else {
tag.FrameType = flvio.FRAME_INTER
}
case av.VP9:
tag = flvio.Tag{
Type: flvio.TAG_VIDEO,
IsExHeader: true,
PacketType: flvio.PKTTYPE_CODED_FRAMES,
CompositionTime: flvio.TimeToTs(pkt.CompositionTime),
FourCC: flvio.FOURCC_VP9,
Data: pkt.Data,
}
if pkt.IsKeyFrame {
tag.FrameType = flvio.FRAME_KEY
} else {
tag.FrameType = flvio.FRAME_INTER
}
case av.AV1:
tag = flvio.Tag{
Type: flvio.TAG_VIDEO,
IsExHeader: true,
PacketType: flvio.PKTTYPE_CODED_FRAMES,
CompositionTime: flvio.TimeToTs(pkt.CompositionTime),
FourCC: flvio.FOURCC_AV1,
Data: pkt.Data,
}
if pkt.IsKeyFrame {
tag.FrameType = flvio.FRAME_KEY
} else {
tag.FrameType = flvio.FRAME_INTER
}
case av.AAC: case av.AAC:
tag = flvio.Tag{ tag = flvio.Tag{
Type: flvio.TAG_AUDIO, Type: flvio.TAG_AUDIO,
@ -335,9 +515,9 @@ func NewMuxer(w io.Writer) *Muxer {
return NewMuxerWriteFlusher(bufio.NewWriterSize(w, pio.RecommendBufioSize)) return NewMuxerWriteFlusher(bufio.NewWriterSize(w, pio.RecommendBufioSize))
} }
var CodecTypes = []av.CodecType{av.H264, av.AAC, av.SPEEX} var CodecTypes = []av.CodecType{av.H264, av.HEVC, av.VP9, av.AV1, av.AAC, av.SPEEX}
func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { func (muxer *Muxer) WriteHeader(streams []av.CodecData) (err error) {
var flags uint8 var flags uint8
for _, stream := range streams { for _, stream := range streams {
if stream.Type().IsVideo() { if stream.Type().IsVideo() {
@ -347,8 +527,8 @@ func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {
} }
} }
n := flvio.FillFileHeader(self.b, flags) n := flvio.FillFileHeader(muxer.b, flags)
if _, err = self.bufw.Write(self.b[:n]); err != nil { if _, err = muxer.bufw.Write(muxer.b[:n]); err != nil {
return return
} }
@ -359,28 +539,28 @@ func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {
return return
} }
if ok { if ok {
if err = flvio.WriteTag(self.bufw, tag, 0, self.b); err != nil { if err = flvio.WriteTag(muxer.bufw, tag, 0, muxer.b); err != nil {
return return
} }
} }
} }
self.streams = streams muxer.streams = streams
return return
} }
func (self *Muxer) WritePacket(pkt av.Packet) (err error) { func (muxer *Muxer) WritePacket(pkt av.Packet) (err error) {
stream := self.streams[pkt.Idx] stream := muxer.streams[pkt.Idx]
tag, timestamp := PacketToTag(pkt, stream) tag, timestamp := PacketToTag(pkt, stream)
if err = flvio.WriteTag(self.bufw, tag, timestamp, self.b); err != nil { if err = flvio.WriteTag(muxer.bufw, tag, timestamp, muxer.b); err != nil {
return return
} }
return return
} }
func (self *Muxer) WriteTrailer() (err error) { func (muxer *Muxer) WriteTrailer() (err error) {
if err = self.bufw.Flush(); err != nil { if err = muxer.bufw.Flush(); err != nil {
return return
} }
return return
@ -401,78 +581,76 @@ func NewDemuxer(r io.Reader) *Demuxer {
} }
} }
func (self *Demuxer) prepare() (err error) { func (demuxer *Demuxer) prepare() (err error) {
for self.stage < 2 { for demuxer.stage < 2 {
switch self.stage { switch demuxer.stage {
case 0: case 0:
if _, err = io.ReadFull(self.bufr, self.b[:flvio.FileHeaderLength]); err != nil { if _, err = io.ReadFull(demuxer.bufr, demuxer.b[:flvio.FileHeaderLength]); err != nil {
return return
} }
var flags uint8 var flags uint8
var skip int var skip int
if flags, skip, err = flvio.ParseFileHeader(self.b); err != nil { if flags, skip, err = flvio.ParseFileHeader(demuxer.b); err != nil {
return return
} }
if _, err = self.bufr.Discard(skip); err != nil { if _, err = demuxer.bufr.Discard(skip); err != nil {
return return
} }
if flags&flvio.FILE_HAS_AUDIO != 0 { if flags&flvio.FILE_HAS_AUDIO != 0 {
self.prober.HasAudio = true demuxer.prober.HasAudio = true
} }
if flags&flvio.FILE_HAS_VIDEO != 0 { if flags&flvio.FILE_HAS_VIDEO != 0 {
self.prober.HasVideo = true demuxer.prober.HasVideo = true
} }
self.stage++ demuxer.stage++
case 1: case 1:
for !self.prober.Probed() { for !demuxer.prober.Probed() {
var tag flvio.Tag var tag flvio.Tag
var timestamp int32 var timestamp int32
if tag, timestamp, err = flvio.ReadTag(self.bufr, self.b); err != nil { if tag, timestamp, err = flvio.ReadTag(demuxer.bufr, demuxer.b); err != nil {
return return
} }
if err = self.prober.PushTag(tag, timestamp); err != nil { if err = demuxer.prober.PushTag(tag, timestamp); err != nil {
return return
} }
} }
self.stage++ demuxer.stage++
} }
} }
return return
} }
func (self *Demuxer) Streams() (streams []av.CodecData, err error) { func (demuxer *Demuxer) Streams() (streams []av.CodecData, err error) {
if err = self.prepare(); err != nil { if err = demuxer.prepare(); err != nil {
return return
} }
streams = self.prober.Streams streams = demuxer.prober.Streams
return return
} }
func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { func (demuxer *Demuxer) ReadPacket() (pkt av.Packet, err error) {
if err = self.prepare(); err != nil { if err = demuxer.prepare(); err != nil {
return return
} }
if !self.prober.Empty() { if !demuxer.prober.Empty() {
pkt = self.prober.PopPacket() pkt = demuxer.prober.PopPacket()
return return
} }
for { for {
var tag flvio.Tag var tag flvio.Tag
var timestamp int32 var timestamp int32
if tag, timestamp, err = flvio.ReadTag(self.bufr, self.b); err != nil { if tag, timestamp, err = flvio.ReadTag(demuxer.bufr, demuxer.b); err != nil {
return return
} }
var ok bool var ok bool
if pkt, ok = self.prober.TagToPacket(tag, timestamp); ok { if pkt, ok = demuxer.prober.TagToPacket(tag, timestamp); ok {
return return
} }
} }
return
} }
func Handler(h *avutil.RegisterHandler) { func Handler(h *avutil.RegisterHandler) {

View File

@ -2,10 +2,11 @@ package flvio
import ( import (
"fmt" "fmt"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits/pio"
"io" "io"
"time" "time"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits/pio"
) )
func TsToTime(ts int32) time.Duration { func TsToTime(ts int32) time.Duration {
@ -60,6 +61,27 @@ const (
VIDEO_H264 = 7 VIDEO_H264 = 7
) )
const (
PKTTYPE_SEQUENCE_START = 0
PKTTYPE_CODED_FRAMES = 1
PKTTYPE_SEQUENCE_END = 2
PKTTYPE_CODED_FRAMESX = 3
PKTTYPE_METADATA = 4
PKTTYPE_MPEG2TS_SEQUENCE_START = 5
)
var (
FOURCC_AV1 = [4]byte{'a', 'v', '0', '1'}
FOURCC_VP9 = [4]byte{'v', 'p', '0', '9'}
FOURCC_HEVC = [4]byte{'h', 'v', 'c', '1'}
)
func FourCCToFloat(fourcc [4]byte) float64 {
i := int(fourcc[0])<<24 | int(fourcc[1])<<16 | int(fourcc[2])<<8 | int(fourcc[3])
return float64(i)
}
type Tag struct { type Tag struct {
/* /*
8 = Audio 8 = Audio
@ -126,14 +148,22 @@ type Tag struct {
AACPacketType uint8 AACPacketType uint8
/* /*
0: reserved
1: keyframe (for AVC, a seekable frame) 1: keyframe (for AVC, a seekable frame)
2: inter frame (for AVC, a non- seekable frame) 2: inter frame (for AVC, a non- seekable frame)
3: disposable inter frame (H.263 only) 3: disposable inter frame (H.263 only)
4: generated keyframe (reserved for server use only) 4: generated keyframe (reserved for server use only)
5: video info/command frame 5: video info/command frame
6: reserved
7: reserved
*/ */
FrameType uint8 FrameType uint8
/*
FrameType & 0b1000 != 0
*/
IsExHeader bool
/* /*
1: JPEG (currently unused) 1: JPEG (currently unused)
2: Sorenson H.263 2: Sorenson H.263
@ -145,6 +175,16 @@ type Tag struct {
*/ */
CodecID uint8 CodecID uint8
/*
0: PacketTypeSequenceStart
1: PacketTypeCodedFrames
2: PacketTypeSequenceEnd
3: PacketTypeCodedFramesX
4: PacketTypeMetadata
5: PacketTypeMPEG2TSSequenceStart
*/
PacketType uint8
/* /*
0: AVC sequence header 0: AVC sequence header
1: AVC NALU 1: AVC NALU
@ -154,18 +194,20 @@ type Tag struct {
CompositionTime int32 CompositionTime int32
FourCC [4]byte
Data []byte Data []byte
} }
func (self Tag) ChannelLayout() av.ChannelLayout { func (t Tag) ChannelLayout() av.ChannelLayout {
if self.SoundType == SOUND_MONO { if t.SoundType == SOUND_MONO {
return av.CH_MONO return av.CH_MONO
} else { } else {
return av.CH_STEREO return av.CH_STEREO
} }
} }
func (self *Tag) audioParseHeader(b []byte) (n int, err error) { func (t *Tag) audioParseHeader(b []byte) (n int, err error) {
if len(b) < n+1 { if len(b) < n+1 {
err = fmt.Errorf("audiodata: parse invalid") err = fmt.Errorf("audiodata: parse invalid")
return return
@ -173,97 +215,163 @@ func (self *Tag) audioParseHeader(b []byte) (n int, err error) {
flags := b[n] flags := b[n]
n++ n++
self.SoundFormat = flags >> 4 t.SoundFormat = flags >> 4
self.SoundRate = (flags >> 2) & 0x3 t.SoundRate = (flags >> 2) & 0x3
self.SoundSize = (flags >> 1) & 0x1 t.SoundSize = (flags >> 1) & 0x1
self.SoundType = flags & 0x1 t.SoundType = flags & 0x1
switch self.SoundFormat { switch t.SoundFormat {
case SOUND_AAC: case SOUND_AAC:
if len(b) < n+1 { if len(b) < n+1 {
err = fmt.Errorf("audiodata: parse invalid") err = fmt.Errorf("audiodata: parse invalid")
return return
} }
self.AACPacketType = b[n] t.AACPacketType = b[n]
n++ n++
} }
return return
} }
func (self Tag) audioFillHeader(b []byte) (n int) { func (t Tag) audioFillHeader(b []byte) (n int) {
var flags uint8 var flags uint8
flags |= self.SoundFormat << 4 flags |= t.SoundFormat << 4
flags |= self.SoundRate << 2 flags |= t.SoundRate << 2
flags |= self.SoundSize << 1 flags |= t.SoundSize << 1
flags |= self.SoundType flags |= t.SoundType
b[n] = flags b[n] = flags
n++ n++
switch self.SoundFormat { switch t.SoundFormat {
case SOUND_AAC: case SOUND_AAC:
b[n] = self.AACPacketType b[n] = t.AACPacketType
n++ n++
} }
return return
} }
func (self *Tag) videoParseHeader(b []byte) (n int, err error) { func (t *Tag) videoParseHeader(b []byte) (n int, err error) {
if len(b) < n+1 { if len(b) < n+1 {
err = fmt.Errorf("videodata: parse invalid") err = fmt.Errorf("videodata: parse invalid")
return return
} }
flags := b[n] flags := b[n]
self.FrameType = flags >> 4 t.FrameType = flags >> 4
self.CodecID = flags & 0xf t.CodecID = flags & 0b1111
//fmt.Printf("%#8b\n", flags)
n++ n++
if self.FrameType == FRAME_INTER || self.FrameType == FRAME_KEY { if (t.FrameType & 0b1000) != 0 {
t.IsExHeader = true
t.PacketType = t.CodecID
t.CodecID = 0
if t.PacketType != PKTTYPE_METADATA {
t.FrameType = t.FrameType & 0b0111
}
}
if !t.IsExHeader {
if t.FrameType == FRAME_INTER || t.FrameType == FRAME_KEY {
if len(b) < n+4 {
err = fmt.Errorf("videodata: parse invalid: neither interframe nor keyframe")
return
}
t.AVCPacketType = b[n]
switch t.AVCPacketType {
case AVC_SEQHDR:
t.PacketType = PKTTYPE_SEQUENCE_START
case AVC_NALU:
t.PacketType = PKTTYPE_CODED_FRAMES
case AVC_EOS:
t.PacketType = PKTTYPE_SEQUENCE_END
}
n++
t.CompositionTime = pio.I24BE(b[n:])
n += 3
}
} else {
if len(b) < n+4 { if len(b) < n+4 {
err = fmt.Errorf("videodata: parse invalid") err = fmt.Errorf("videodata: parse invalid: not enough bytes for the fourCC value")
return return
} }
self.AVCPacketType = b[n]
n++
self.CompositionTime = pio.I24BE(b[n:]) t.FourCC[0] = b[n]
t.FourCC[1] = b[n+1]
t.FourCC[2] = b[n+2]
t.FourCC[3] = b[n+3]
n += 4
t.CompositionTime = 0
if t.FourCC == FOURCC_HEVC {
if t.PacketType == PKTTYPE_CODED_FRAMES {
t.CompositionTime = pio.I24BE(b[n:])
n += 3
}
}
}
//fmt.Printf("parseVideoHeader: PacketType: %d\n", t.PacketType)
return
}
func (t Tag) videoFillHeader(b []byte) (n int) {
if t.IsExHeader {
flags := t.FrameType<<4 | t.PacketType | 0b10000000
b[n] = flags
n++
b[n] = t.FourCC[0]
b[n+1] = t.FourCC[1]
b[n+2] = t.FourCC[2]
b[n+3] = t.FourCC[3]
n += 4
if t.FourCC == FOURCC_HEVC {
if t.PacketType == PKTTYPE_CODED_FRAMES {
pio.PutI24BE(b[n:], t.CompositionTime)
n += 3
}
}
} else {
flags := t.FrameType<<4 | t.CodecID
b[n] = flags
n++
b[n] = t.AVCPacketType
n++
pio.PutI24BE(b[n:], t.CompositionTime)
n += 3 n += 3
} }
//fmt.Printf("videoFillHeader: PacketType: %d\n%s\n", t.PacketType, hex.Dump(b[:n]))
return return
} }
func (self Tag) videoFillHeader(b []byte) (n int) { func (t Tag) FillHeader(b []byte) (n int) {
flags := self.FrameType<<4 | self.CodecID switch t.Type {
b[n] = flags
n++
b[n] = self.AVCPacketType
n++
pio.PutI24BE(b[n:], self.CompositionTime)
n += 3
return
}
func (self Tag) FillHeader(b []byte) (n int) {
switch self.Type {
case TAG_AUDIO: case TAG_AUDIO:
return self.audioFillHeader(b) return t.audioFillHeader(b)
case TAG_VIDEO: case TAG_VIDEO:
return self.videoFillHeader(b) return t.videoFillHeader(b)
} }
return return
} }
func (self *Tag) ParseHeader(b []byte) (n int, err error) { func (t *Tag) ParseHeader(b []byte) (n int, err error) {
switch self.Type { switch t.Type {
case TAG_AUDIO: case TAG_AUDIO:
return self.audioParseHeader(b) return t.audioParseHeader(b)
case TAG_VIDEO: case TAG_VIDEO:
return self.videoParseHeader(b) return t.videoParseHeader(b)
} }
return return

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,30 @@ func (self *GolombBitReader) ReadBits(n int) (res uint, err error) {
return return
} }
func (self *GolombBitReader) ReadBits32(n uint) (r uint32, err error) {
var t uint
for i := uint(0); i < n; i++ {
t, err = self.ReadBit()
if err != nil {
return
}
r = (r << 1) | uint32(t)
}
return
}
func (self *GolombBitReader) ReadBits64(n uint) (r uint64, err error) {
var t uint
for i := uint(0); i < n; i++ {
t, err = self.ReadBit()
if err != nil {
return
}
r = (r << 1) | uint64(t)
}
return
}
func (self *GolombBitReader) ReadExponentialGolombCode() (res uint, err error) { func (self *GolombBitReader) ReadExponentialGolombCode() (res uint, err error) {
i := 0 i := 0
for { for {

View File

@ -10,9 +10,6 @@
</h6> </h6>
<p align="center"> <p align="center">
<a href="https://travis-ci.org/gabriel-vasile/mimetype">
<img alt="Build Status" src="https://travis-ci.org/gabriel-vasile/mimetype.svg?branch=master">
</a>
<a href="https://pkg.go.dev/github.com/gabriel-vasile/mimetype"> <a href="https://pkg.go.dev/github.com/gabriel-vasile/mimetype">
<img alt="Go Reference" src="https://pkg.go.dev/badge/github.com/gabriel-vasile/mimetype.svg"> <img alt="Go Reference" src="https://pkg.go.dev/badge/github.com/gabriel-vasile/mimetype.svg">
</a> </a>

View File

@ -150,32 +150,34 @@ func Marc(raw []byte, limit uint32) bool {
} }
// Glb matches a glTF model format file. // Glb matches a glTF model format file.
// GLB is the binary file format representation of 3D models save in // GLB is the binary file format representation of 3D models saved in
// the GL transmission Format (glTF). // the GL transmission Format (glTF).
// see more: https://docs.fileformat.com/3d/glb/ // GLB uses little endian and its header structure is as follows:
// https://www.iana.org/assignments/media-types/model/gltf-binary
// GLB file format is based on little endian and its header structure
// show below:
// //
// <-- 12-byte header --> // <-- 12-byte header -->
// | magic | version | length | // | magic | version | length |
// | (uint32) | (uint32) | (uint32) | // | (uint32) | (uint32) | (uint32) |
// | \x67\x6C\x54\x46 | \x01\x00\x00\x00 | ... | // | \x67\x6C\x54\x46 | \x01\x00\x00\x00 | ... |
// | g l T F | 1 | ... | // | g l T F | 1 | ... |
//
// Visit [glTF specification] and [IANA glTF entry] for more details.
//
// [glTF specification]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html
// [IANA glTF entry]: https://www.iana.org/assignments/media-types/model/gltf-binary
var Glb = prefix([]byte("\x67\x6C\x54\x46\x02\x00\x00\x00"), var Glb = prefix([]byte("\x67\x6C\x54\x46\x02\x00\x00\x00"),
[]byte("\x67\x6C\x54\x46\x01\x00\x00\x00")) []byte("\x67\x6C\x54\x46\x01\x00\x00\x00"))
// TzIf matches a Time Zone Information Format (TZif) file. // TzIf matches a Time Zone Information Format (TZif) file.
// See more: https://tools.ietf.org/id/draft-murchison-tzdist-tzif-00.html#rfc.section.3 // See more: https://tools.ietf.org/id/draft-murchison-tzdist-tzif-00.html#rfc.section.3
// Its header structure is shown below: // Its header structure is shown below:
// +---------------+---+ // +---------------+---+
// | magic (4) | <-+-- version (1) // | magic (4) | <-+-- version (1)
// +---------------+---+---------------------------------------+ // +---------------+---+---------------------------------------+
// | [unused - reserved for future use] (15) | // | [unused - reserved for future use] (15) |
// +---------------+---------------+---------------+-----------+ // +---------------+---------------+---------------+-----------+
// | isutccnt (4) | isstdcnt (4) | leapcnt (4) | // | isutccnt (4) | isstdcnt (4) | leapcnt (4) |
// +---------------+---------------+---------------+ // +---------------+---------------+---------------+
// | timecnt (4) | typecnt (4) | charcnt (4) | // | timecnt (4) | typecnt (4) | charcnt (4) |
func TzIf(raw []byte, limit uint32) bool { func TzIf(raw []byte, limit uint32) bool {
// File is at least 44 bytes (header size). // File is at least 44 bytes (header size).
if len(raw) < 44 { if len(raw) < 44 {

View File

@ -177,7 +177,9 @@ func newXMLSig(localName, xmlns string) xmlSig {
// and, optionally, followed by the arguments for the interpreter. // and, optionally, followed by the arguments for the interpreter.
// //
// Ex: // Ex:
// #! /usr/bin/env php //
// #! /usr/bin/env php
//
// /usr/bin/env is the interpreter, php is the first and only argument. // /usr/bin/env is the interpreter, php is the first and only argument.
func shebang(sigs ...[]byte) Detector { func shebang(sigs ...[]byte) Detector {
return func(raw []byte, limit uint32) bool { return func(raw []byte, limit uint32) bool {

View File

@ -3,6 +3,7 @@ package magic
import ( import (
"bytes" "bytes"
"encoding/csv" "encoding/csv"
"errors"
"io" "io"
) )
@ -19,12 +20,23 @@ func Tsv(raw []byte, limit uint32) bool {
func sv(in []byte, comma rune, limit uint32) bool { func sv(in []byte, comma rune, limit uint32) bool {
r := csv.NewReader(dropLastLine(in, limit)) r := csv.NewReader(dropLastLine(in, limit))
r.Comma = comma r.Comma = comma
r.TrimLeadingSpace = true r.ReuseRecord = true
r.LazyQuotes = true r.LazyQuotes = true
r.Comment = '#' r.Comment = '#'
lines, err := r.ReadAll() lines := 0
return err == nil && r.FieldsPerRecord > 1 && len(lines) > 1 for {
_, err := r.Read()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return false
}
lines++
}
return r.FieldsPerRecord > 1 && lines > 1
} }
// dropLastLine drops the last incomplete line from b. // dropLastLine drops the last incomplete line from b.

View File

@ -39,7 +39,8 @@ func Detect(in []byte) *MIME {
// //
// DetectReader assumes the reader offset is at the start. If the input is an // DetectReader assumes the reader offset is at the start. If the input is an
// io.ReadSeeker you previously read from, it should be rewinded before detection: // io.ReadSeeker you previously read from, it should be rewinded before detection:
// reader.Seek(0, io.SeekStart) //
// reader.Seek(0, io.SeekStart)
func DetectReader(r io.Reader) (*MIME, error) { func DetectReader(r io.Reader) (*MIME, error) {
var in []byte var in []byte
var err error var err error

View File

@ -1,4 +1,4 @@
## 172 Supported MIME types ## 173 Supported MIME types
This file is automatically generated when running tests. Do not edit manually. This file is automatically generated when running tests. Do not edit manually.
Extension | MIME type | Aliases Extension | MIME type | Aliases

61
vendor/github.com/go-openapi/jsonpointer/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,61 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 45
maligned:
suggest-new: true
dupl:
threshold: 200
goconst:
min-len: 2
min-occurrences: 3
linters:
enable-all: true
disable:
- maligned
- unparam
- lll
- gochecknoinits
- gochecknoglobals
- funlen
- godox
- gocognit
- whitespace
- wsl
- wrapcheck
- testpackage
- nlreturn
- gomnd
- exhaustivestruct
- goerr113
- errorlint
- nestif
- godot
- gofumpt
- paralleltest
- tparallel
- thelper
- ifshort
- exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase

View File

@ -1,6 +1,10 @@
# gojsonpointer [![Build Status](https://travis-ci.org/go-openapi/jsonpointer.svg?branch=master)](https://travis-ci.org/go-openapi/jsonpointer) [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) # gojsonpointer [![Build Status](https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonpointer/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer)
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer.svg)](https://pkg.go.dev/github.com/go-openapi/jsonpointer)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonpointer)](https://goreportcard.com/report/github.com/go-openapi/jsonpointer)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonpointer?status.svg)](http://godoc.org/github.com/go-openapi/jsonpointer)
An implementation of JSON Pointer - Go language An implementation of JSON Pointer - Go language
## Status ## Status

View File

@ -110,19 +110,39 @@ func SetForToken(document any, decodedToken string, value any) (any, error) {
return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider) return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
} }
func isNil(input any) bool {
if input == nil {
return true
}
kind := reflect.TypeOf(input).Kind()
switch kind { //nolint:exhaustive
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
return reflect.ValueOf(input).IsNil()
default:
return false
}
}
func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) { func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) {
rValue := reflect.Indirect(reflect.ValueOf(node)) rValue := reflect.Indirect(reflect.ValueOf(node))
kind := rValue.Kind() kind := rValue.Kind()
if isNil(node) {
return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken)
}
if rValue.Type().Implements(jsonPointableType) { switch typed := node.(type) {
r, err := node.(JSONPointable).JSONLookup(decodedToken) case JSONPointable:
r, err := typed.JSONLookup(decodedToken)
if err != nil { if err != nil {
return nil, kind, err return nil, kind, err
} }
return r, kind, nil return r, kind, nil
case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect
return getSingleImpl(*typed, decodedToken, nameProvider)
} }
switch kind { switch kind { //nolint:exhaustive
case reflect.Struct: case reflect.Struct:
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok { if !ok {
@ -170,7 +190,7 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider *swag.NameP
return node.(JSONSetable).JSONSet(decodedToken, data) return node.(JSONSetable).JSONSet(decodedToken, data)
} }
switch rValue.Kind() { switch rValue.Kind() { //nolint:exhaustive
case reflect.Struct: case reflect.Struct:
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok { if !ok {
@ -231,8 +251,7 @@ func (p *Pointer) get(node any, nameProvider *swag.NameProvider) (any, reflect.K
if err != nil { if err != nil {
return nil, knd, err return nil, knd, err
} }
node, kind = r, knd node = r
} }
rValue := reflect.ValueOf(node) rValue := reflect.ValueOf(node)
@ -284,7 +303,7 @@ func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error {
continue continue
} }
switch kind { switch kind { //nolint:exhaustive
case reflect.Struct: case reflect.Struct:
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok { if !ok {
@ -405,11 +424,11 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) {
case json.Delim: case json.Delim:
switch tk { switch tk {
case '{': case '{':
if err := drainSingle(dec); err != nil { if err = drainSingle(dec); err != nil {
return 0, err return 0, err
} }
case '[': case '[':
if err := drainSingle(dec); err != nil { if err = drainSingle(dec); err != nil {
return 0, err return 0, err
} }
} }
@ -435,20 +454,21 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
switch tk := tk.(type) {
case json.Delim: if delim, isDelim := tk.(json.Delim); isDelim {
switch tk { switch delim {
case '{': case '{':
if err := drainSingle(dec); err != nil { if err = drainSingle(dec); err != nil {
return 0, err return 0, err
} }
case '[': case '[':
if err := drainSingle(dec); err != nil { if err = drainSingle(dec); err != nil {
return 0, err return 0, err
} }
} }
} }
} }
if !dec.More() { if !dec.More() {
return 0, fmt.Errorf("token reference %q not found", decodedToken) return 0, fmt.Errorf("token reference %q not found", decodedToken)
} }
@ -456,27 +476,27 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) {
} }
// drainSingle drains a single level of object or array. // drainSingle drains a single level of object or array.
// The decoder has to guarantee the begining delim (i.e. '{' or '[') has been consumed. // The decoder has to guarantee the beginning delim (i.e. '{' or '[') has been consumed.
func drainSingle(dec *json.Decoder) error { func drainSingle(dec *json.Decoder) error {
for dec.More() { for dec.More() {
tk, err := dec.Token() tk, err := dec.Token()
if err != nil { if err != nil {
return err return err
} }
switch tk := tk.(type) { if delim, isDelim := tk.(json.Delim); isDelim {
case json.Delim: switch delim {
switch tk {
case '{': case '{':
if err := drainSingle(dec); err != nil { if err = drainSingle(dec); err != nil {
return err return err
} }
case '[': case '[':
if err := drainSingle(dec); err != nil { if err = drainSingle(dec); err != nil {
return err return err
} }
} }
} }
} }
// Consumes the ending delim // Consumes the ending delim
if _, err := dec.Token(); err != nil { if _, err := dec.Token(); err != nil {
return err return err
@ -498,14 +518,14 @@ const (
// Unescape unescapes a json pointer reference token string to the original representation // Unescape unescapes a json pointer reference token string to the original representation
func Unescape(token string) string { func Unescape(token string) string {
step1 := strings.Replace(token, encRefTok1, decRefTok1, -1) step1 := strings.ReplaceAll(token, encRefTok1, decRefTok1)
step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1) step2 := strings.ReplaceAll(step1, encRefTok0, decRefTok0)
return step2 return step2
} }
// Escape escapes a pointer reference token string // Escape escapes a pointer reference token string
func Escape(token string) string { func Escape(token string) string {
step1 := strings.Replace(token, decRefTok0, encRefTok0, -1) step1 := strings.ReplaceAll(token, decRefTok0, encRefTok0)
step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1) step2 := strings.ReplaceAll(step1, decRefTok1, encRefTok1)
return step2 return step2
} }

View File

@ -1,50 +1,61 @@
linters-settings: linters-settings:
govet: govet:
check-shadowing: true check-shadowing: true
golint:
min-confidence: 0
gocyclo: gocyclo:
min-complexity: 30 min-complexity: 45
maligned: maligned:
suggest-new: true suggest-new: true
dupl: dupl:
threshold: 100 threshold: 200
goconst: goconst:
min-len: 2 min-len: 2
min-occurrences: 4 min-occurrences: 3
paralleltest:
ignore-missing: true
linters: linters:
enable-all: true enable-all: true
disable: disable:
- maligned - maligned
- unparam
- lll - lll
- gochecknoinits
- gochecknoglobals - gochecknoglobals
- funlen
- godox - godox
- gocognit - gocognit
- whitespace - whitespace
- wsl - wsl
- funlen
- gochecknoglobals
- gochecknoinits
- scopelint
- wrapcheck - wrapcheck
- exhaustivestruct
- exhaustive
- nlreturn
- testpackage - testpackage
- gci - nlreturn
- gofumpt
- goerr113
- gomnd - gomnd
- tparallel - exhaustivestruct
- goerr113
- errorlint
- nestif - nestif
- godot - godot
- errorlint - gofumpt
- varcheck - paralleltest
- interfacer - tparallel
- deadcode - thelper
- golint
- ifshort - ifshort
- structcheck
- nosnakecase
- varnamelen
- exhaustruct - exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase

View File

@ -1,15 +1,19 @@
# gojsonreference [![Build Status](https://travis-ci.org/go-openapi/jsonreference.svg?branch=master)](https://travis-ci.org/go-openapi/jsonreference) [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) # gojsonreference [![Build Status](https://github.com/go-openapi/jsonreference/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonreference/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference)
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonreference.svg)](https://pkg.go.dev/github.com/go-openapi/jsonreference)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonreference)](https://goreportcard.com/report/github.com/go-openapi/jsonreference)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonreference?status.svg)](http://godoc.org/github.com/go-openapi/jsonreference)
An implementation of JSON Reference - Go language An implementation of JSON Reference - Go language
## Status ## Status
Feature complete. Stable API Feature complete. Stable API
## Dependencies ## Dependencies
https://github.com/go-openapi/jsonpointer * https://github.com/go-openapi/jsonpointer
## References ## References
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 * http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
* http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03

View File

@ -1,2 +1 @@
secrets.yml *.out
coverage.out

View File

@ -11,7 +11,7 @@ linters-settings:
threshold: 200 threshold: 200
goconst: goconst:
min-len: 2 min-len: 2
min-occurrences: 2 min-occurrences: 3
linters: linters:
enable-all: true enable-all: true
@ -40,3 +40,22 @@ linters:
- tparallel - tparallel
- thelper - thelper
- ifshort - ifshort
- exhaustruct
- varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase

View File

@ -1,8 +1,5 @@
# OAI object model # OpenAPI v2 object model [![Build Status](https://github.com/go-openapi/spec/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/spec/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec)
[![Build Status](https://travis-ci.org/go-openapi/spec.svg?branch=master)](https://travis-ci.org/go-openapi/spec)
<!-- [![Build status](https://ci.appveyor.com/api/projects/status/x377t5o9ennm847o/branch/master?svg=true)](https://ci.appveyor.com/project/casualjim/go-openapi/spec/branch/master) -->
[![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec)
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/spec.svg)](https://pkg.go.dev/github.com/go-openapi/spec) [![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/spec.svg)](https://pkg.go.dev/github.com/go-openapi/spec)

View File

@ -1,32 +0,0 @@
version: "0.1.{build}"
clone_folder: C:\go-openapi\spec
shallow_clone: true # for startup speed
pull_requests:
do_not_increment_build_number: true
#skip_tags: true
#skip_branch_with_pr: true
# appveyor.yml
build: off
environment:
GOPATH: c:\gopath
stack: go 1.15
test_script:
- go test -v -timeout 20m ./...
deploy: off
notifications:
- provider: Slack
incoming_webhook: https://hooks.slack.com/services/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
auth_token:
secure: Sf7kZf7ZGbnwWUMpffHwMu5A0cHkLK2MYY32LNTPj4+/3qC3Ghl7+9v4TSLOqOlCwdRNjOGblAq7s+GDJed6/xgRQl1JtCi1klzZNrYX4q01pgTPvvGcwbBkIYgeMaPeIRcK9OZnud7sRXdttozgTOpytps2U6Js32ip7uj5mHSg2ub0FwoSJwlS6dbezZ8+eDhoha0F/guY99BEwx8Bd+zROrT2TFGsSGOFGN6wFc7moCqTHO/YkWib13a2QNXqOxCCVBy/lt76Wp+JkeFppjHlzs/2lP3EAk13RIUAaesdEUHvIHrzCyNJEd3/+KO2DzsWOYfpktd+KBCvgaYOsoo7ubdT3IROeAegZdCgo/6xgCEsmFc9ZcqCfN5yNx2A+BZ2Vwmpws+bQ1E1+B5HDzzaiLcYfG4X2O210QVGVDLWsv1jqD+uPYeHY2WRfh5ZsIUFvaqgUEnwHwrK44/8REAhQavt1QAj5uJpsRd7CkRVPWRNK+yIky+wgbVUFEchRNmS55E7QWf+W4+4QZkQi7vUTMc9nbTUu2Es9NfvfudOpM2wZbn98fjpb/qq/nRv6Bk+ca+7XD5/IgNLMbWp2ouDdzbiHLCOfDUiHiDJhLfFZx9Bwo7ZwfzeOlbrQX66bx7xRKYmOe4DLrXhNcpbsMa8qbfxlZRCmYbubB/Y8h4=
channel: bots
on_build_success: false
on_build_failure: true
on_build_status_changed: true

File diff suppressed because one or more lines are too long

17
vendor/github.com/go-openapi/spec/embed.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
package spec
import (
"embed"
"path"
)
//go:embed schemas/*.json schemas/*/*.json
var assets embed.FS
func jsonschemaDraft04JSONBytes() ([]byte, error) {
return assets.ReadFile(path.Join("schemas", "jsonschema-draft-04.json"))
}
func v2SchemaJSONBytes() ([]byte, error) {
return assets.ReadFile(path.Join("schemas", "v2", "schema.json"))
}

View File

@ -27,7 +27,6 @@ import (
// all relative $ref's will be resolved from there. // all relative $ref's will be resolved from there.
// //
// PathLoader injects a document loading method. By default, this resolves to the function provided by the SpecLoader package variable. // PathLoader injects a document loading method. By default, this resolves to the function provided by the SpecLoader package variable.
//
type ExpandOptions struct { type ExpandOptions struct {
RelativeBase string // the path to the root document to expand. This is a file, not a directory RelativeBase string // the path to the root document to expand. This is a file, not a directory
SkipSchemas bool // do not expand schemas, just paths, parameters and responses SkipSchemas bool // do not expand schemas, just paths, parameters and responses
@ -103,15 +102,21 @@ const rootBase = ".root"
// baseForRoot loads in the cache the root document and produces a fake ".root" base path entry // baseForRoot loads in the cache the root document and produces a fake ".root" base path entry
// for further $ref resolution // for further $ref resolution
//
// Setting the cache is optional and this parameter may safely be left to nil.
func baseForRoot(root interface{}, cache ResolutionCache) string { func baseForRoot(root interface{}, cache ResolutionCache) string {
if root == nil {
return ""
}
// cache the root document to resolve $ref's // cache the root document to resolve $ref's
normalizedBase := normalizeBase(rootBase) normalizedBase := normalizeBase(rootBase)
if root == nil {
// ensure that we never leave a nil root: always cache the root base pseudo-document
cachedRoot, found := cache.Get(normalizedBase)
if found && cachedRoot != nil {
// the cache is already preloaded with a root
return normalizedBase
}
root = map[string]interface{}{}
}
cache.Set(normalizedBase, root) cache.Set(normalizedBase, root)
return normalizedBase return normalizedBase
@ -208,7 +213,19 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
} }
if target.Ref.String() != "" { if target.Ref.String() != "" {
return expandSchemaRef(target, parentRefs, resolver, basePath) if !resolver.options.SkipSchemas {
return expandSchemaRef(target, parentRefs, resolver, basePath)
}
// when "expand" with SkipSchema, we just rebase the existing $ref without replacing
// the full schema.
rebasedRef, err := NewRef(normalizeURI(target.Ref.String(), basePath))
if err != nil {
return nil, err
}
target.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
return &target, nil
} }
for k := range target.Definitions { for k := range target.Definitions {
@ -520,21 +537,25 @@ func getRefAndSchema(input interface{}) (*Ref, *Schema, error) {
} }
func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error { func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error {
ref, _, err := getRefAndSchema(input) ref, sch, err := getRefAndSchema(input)
if err != nil { if err != nil {
return err return err
} }
if ref == nil { if ref == nil && sch == nil { // nothing to do
return nil return nil
} }
parentRefs := make([]string, 0, 10) parentRefs := make([]string, 0, 10)
if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) { if ref != nil {
return err // dereference this $ref
if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
return err
}
ref, sch, _ = getRefAndSchema(input)
} }
ref, sch, _ := getRefAndSchema(input)
if ref.String() != "" { if ref.String() != "" {
transitiveResolver := resolver.transitiveResolver(basePath, *ref) transitiveResolver := resolver.transitiveResolver(basePath, *ref)
basePath = resolver.updateBasePath(transitiveResolver, basePath) basePath = resolver.updateBasePath(transitiveResolver, basePath)
@ -546,6 +567,7 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa
if ref != nil { if ref != nil {
*ref = Ref{} *ref = Ref{}
} }
return nil return nil
} }
@ -555,38 +577,29 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa
return ern return ern
} }
switch { if resolver.isCircular(&rebasedRef, basePath, parentRefs...) {
case resolver.isCircular(&rebasedRef, basePath, parentRefs...):
// this is a circular $ref: stop expansion // this is a circular $ref: stop expansion
if !resolver.options.AbsoluteCircularRef { if !resolver.options.AbsoluteCircularRef {
sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID) sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
} else { } else {
sch.Ref = rebasedRef sch.Ref = rebasedRef
} }
case !resolver.options.SkipSchemas:
// schema expanded to a $ref in another root
sch.Ref = rebasedRef
debugLog("rebased to: %s", sch.Ref.String())
default:
// skip schema expansion but rebase $ref to schema
sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
} }
} }
// $ref expansion or rebasing is performed by expandSchema below
if ref != nil { if ref != nil {
*ref = Ref{} *ref = Ref{}
} }
// expand schema // expand schema
if !resolver.options.SkipSchemas { // yes, we do it even if options.SkipSchema is true: we have to go down that rabbit hole and rebase nested $ref)
s, err := expandSchema(*sch, parentRefs, resolver, basePath) s, err := expandSchema(*sch, parentRefs, resolver, basePath)
if resolver.shouldStopOnError(err) { if resolver.shouldStopOnError(err) {
return err return err
} }
if s == nil {
// guard for when continuing on error if s != nil { // guard for when continuing on error
return nil
}
*sch = *s *sch = *s
} }

View File

@ -40,5 +40,5 @@ func repairURI(in string) (*url.URL, string) {
return u, "" return u, ""
} }
func fixWindowsURI(u *url.URL, in string) { func fixWindowsURI(_ *url.URL, _ string) {
} }

View File

@ -217,9 +217,12 @@ func (o *Operation) AddParam(param *Parameter) *Operation {
for i, p := range o.Parameters { for i, p := range o.Parameters {
if p.Name == param.Name && p.In == param.In { if p.Name == param.Name && p.In == param.In {
params := append(o.Parameters[:i], *param) params := make([]Parameter, 0, len(o.Parameters)+1)
params = append(params, o.Parameters[:i]...)
params = append(params, *param)
params = append(params, o.Parameters[i+1:]...) params = append(params, o.Parameters[i+1:]...)
o.Parameters = params o.Parameters = params
return o return o
} }
} }

View File

@ -84,27 +84,27 @@ type ParamProps struct {
// Parameter a unique parameter is defined by a combination of a [name](#parameterName) and [location](#parameterIn). // Parameter a unique parameter is defined by a combination of a [name](#parameterName) and [location](#parameterIn).
// //
// There are five possible parameter types. // There are five possible parameter types.
// * Path - Used together with [Path Templating](#pathTemplating), where the parameter value is actually part // - Path - Used together with [Path Templating](#pathTemplating), where the parameter value is actually part
// of the operation's URL. This does not include the host or base path of the API. For example, in `/items/{itemId}`, // of the operation's URL. This does not include the host or base path of the API. For example, in `/items/{itemId}`,
// the path parameter is `itemId`. // the path parameter is `itemId`.
// * Query - Parameters that are appended to the URL. For example, in `/items?id=###`, the query parameter is `id`. // - Query - Parameters that are appended to the URL. For example, in `/items?id=###`, the query parameter is `id`.
// * Header - Custom headers that are expected as part of the request. // - Header - Custom headers that are expected as part of the request.
// * Body - The payload that's appended to the HTTP request. Since there can only be one payload, there can only be // - Body - The payload that's appended to the HTTP request. Since there can only be one payload, there can only be
// _one_ body parameter. The name of the body parameter has no effect on the parameter itself and is used for // _one_ body parameter. The name of the body parameter has no effect on the parameter itself and is used for
// documentation purposes only. Since Form parameters are also in the payload, body and form parameters cannot exist // documentation purposes only. Since Form parameters are also in the payload, body and form parameters cannot exist
// together for the same operation. // together for the same operation.
// * Form - Used to describe the payload of an HTTP request when either `application/x-www-form-urlencoded` or // - Form - Used to describe the payload of an HTTP request when either `application/x-www-form-urlencoded` or
// `multipart/form-data` are used as the content type of the request (in Swagger's definition, // `multipart/form-data` are used as the content type of the request (in Swagger's definition,
// the [`consumes`](#operationConsumes) property of an operation). This is the only parameter type that can be used // the [`consumes`](#operationConsumes) property of an operation). This is the only parameter type that can be used
// to send files, thus supporting the `file` type. Since form parameters are sent in the payload, they cannot be // to send files, thus supporting the `file` type. Since form parameters are sent in the payload, they cannot be
// declared together with a body parameter for the same operation. Form parameters have a different format based on // declared together with a body parameter for the same operation. Form parameters have a different format based on
// the content-type used (for further details, consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4). // the content-type used (for further details, consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4).
// * `application/x-www-form-urlencoded` - Similar to the format of Query parameters but as a payload. // - `application/x-www-form-urlencoded` - Similar to the format of Query parameters but as a payload.
// For example, `foo=1&bar=swagger` - both `foo` and `bar` are form parameters. This is normally used for simple // For example, `foo=1&bar=swagger` - both `foo` and `bar` are form parameters. This is normally used for simple
// parameters that are being transferred. // parameters that are being transferred.
// * `multipart/form-data` - each parameter takes a section in the payload with an internal header. // - `multipart/form-data` - each parameter takes a section in the payload with an internal header.
// For example, for the header `Content-Disposition: form-data; name="submit-name"` the name of the parameter is // For example, for the header `Content-Disposition: form-data; name="submit-name"` the name of the parameter is
// `submit-name`. This type of form parameters is more commonly used for file transfers. // `submit-name`. This type of form parameters is more commonly used for file transfers.
// //
// For more information: http://goo.gl/8us55a#parameterObject // For more information: http://goo.gl/8us55a#parameterObject
type Parameter struct { type Parameter struct {

View File

@ -168,14 +168,7 @@ func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error)
normalized := normalizeBase(pth) normalized := normalizeBase(pth)
debugLog("loading doc from: %s", normalized) debugLog("loading doc from: %s", normalized)
unescaped, err := url.PathUnescape(normalized) data, fromCache := r.cache.Get(normalized)
if err != nil {
return nil, url.URL{}, false, err
}
u := url.URL{Path: unescaped}
data, fromCache := r.cache.Get(u.RequestURI())
if fromCache { if fromCache {
return data, toFetch, fromCache, nil return data, toFetch, fromCache, nil
} }

View File

@ -0,0 +1,149 @@
{
"id": "http://json-schema.org/draft-04/schema#",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Core schema meta-schema",
"definitions": {
"schemaArray": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#" }
},
"positiveInteger": {
"type": "integer",
"minimum": 0
},
"positiveIntegerDefault0": {
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
},
"simpleTypes": {
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
},
"stringArray": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
},
"type": "object",
"properties": {
"id": {
"type": "string"
},
"$schema": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"default": {},
"multipleOf": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "boolean",
"default": false
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "boolean",
"default": false
},
"maxLength": { "$ref": "#/definitions/positiveInteger" },
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
"pattern": {
"type": "string",
"format": "regex"
},
"additionalItems": {
"anyOf": [
{ "type": "boolean" },
{ "$ref": "#" }
],
"default": {}
},
"items": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": {}
},
"maxItems": { "$ref": "#/definitions/positiveInteger" },
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
"uniqueItems": {
"type": "boolean",
"default": false
},
"maxProperties": { "$ref": "#/definitions/positiveInteger" },
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
"required": { "$ref": "#/definitions/stringArray" },
"additionalProperties": {
"anyOf": [
{ "type": "boolean" },
{ "$ref": "#" }
],
"default": {}
},
"definitions": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"properties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"dependencies": {
"type": "object",
"additionalProperties": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/stringArray" }
]
}
},
"enum": {
"type": "array",
"minItems": 1,
"uniqueItems": true
},
"type": {
"anyOf": [
{ "$ref": "#/definitions/simpleTypes" },
{
"type": "array",
"items": { "$ref": "#/definitions/simpleTypes" },
"minItems": 1,
"uniqueItems": true
}
]
},
"format": { "type": "string" },
"allOf": { "$ref": "#/definitions/schemaArray" },
"anyOf": { "$ref": "#/definitions/schemaArray" },
"oneOf": { "$ref": "#/definitions/schemaArray" },
"not": { "$ref": "#" }
},
"dependencies": {
"exclusiveMaximum": [ "maximum" ],
"exclusiveMinimum": [ "minimum" ]
},
"default": {}
}

1607
vendor/github.com/go-openapi/spec/schemas/v2/schema.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@ import (
const ( const (
// SwaggerSchemaURL the url for the swagger 2.0 schema to validate specs // SwaggerSchemaURL the url for the swagger 2.0 schema to validate specs
SwaggerSchemaURL = "http://swagger.io/v2/schema.json#" SwaggerSchemaURL = "http://swagger.io/v2/schema.json#"
// JSONSchemaURL the url for the json schema schema // JSONSchemaURL the url for the json schema
JSONSchemaURL = "http://json-schema.org/draft-04/schema#" JSONSchemaURL = "http://json-schema.org/draft-04/schema#"
) )
@ -41,7 +41,7 @@ func MustLoadJSONSchemaDraft04() *Schema {
// JSONSchemaDraft04 loads the json schema document for json shema draft04 // JSONSchemaDraft04 loads the json schema document for json shema draft04
func JSONSchemaDraft04() (*Schema, error) { func JSONSchemaDraft04() (*Schema, error) {
b, err := Asset("jsonschema-draft-04.json") b, err := jsonschemaDraft04JSONBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -65,7 +65,7 @@ func MustLoadSwagger20Schema() *Schema {
// Swagger20Schema loads the swagger 2.0 schema from the embedded assets // Swagger20Schema loads the swagger 2.0 schema from the embedded assets
func Swagger20Schema() (*Schema, error) { func Swagger20Schema() (*Schema, error) {
b, err := Asset("v2/schema.json") b, err := v2SchemaJSONBytes()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -253,7 +253,7 @@ func (s SchemaOrBool) MarshalJSON() ([]byte, error) {
// UnmarshalJSON converts this bool or schema object from a JSON structure // UnmarshalJSON converts this bool or schema object from a JSON structure
func (s *SchemaOrBool) UnmarshalJSON(data []byte) error { func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
var nw SchemaOrBool var nw SchemaOrBool
if len(data) >= 4 { if len(data) > 0 {
if data[0] == '{' { if data[0] == '{' {
var sch Schema var sch Schema
if err := json.Unmarshal(data, &sch); err != nil { if err := json.Unmarshal(data, &sch); err != nil {
@ -261,7 +261,7 @@ func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
} }
nw.Schema = &sch nw.Schema = &sch
} }
nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e') nw.Allows = !bytes.Equal(data, []byte("false"))
} }
*s = nw *s = nw
return nil return nil

View File

@ -1,8 +0,0 @@
//go:build !go1.19
// +build !go1.19
package spec
import "net/url"
var parseURL = url.Parse

View File

@ -1,6 +1,3 @@
//go:build go1.19
// +build go1.19
package spec package spec
import "net/url" import "net/url"

View File

@ -2,3 +2,4 @@ secrets.yml
vendor vendor
Godeps Godeps
.idea .idea
*.out

View File

@ -4,14 +4,14 @@ linters-settings:
golint: golint:
min-confidence: 0 min-confidence: 0
gocyclo: gocyclo:
min-complexity: 25 min-complexity: 45
maligned: maligned:
suggest-new: true suggest-new: true
dupl: dupl:
threshold: 100 threshold: 200
goconst: goconst:
min-len: 3 min-len: 3
min-occurrences: 2 min-occurrences: 3
linters: linters:
enable-all: true enable-all: true
@ -20,35 +20,41 @@ linters:
- lll - lll
- gochecknoinits - gochecknoinits
- gochecknoglobals - gochecknoglobals
- nlreturn - funlen
- testpackage - godox
- gocognit
- whitespace
- wsl
- wrapcheck - wrapcheck
- testpackage
- nlreturn
- gomnd - gomnd
- exhaustive
- exhaustivestruct - exhaustivestruct
- goerr113 - goerr113
- wsl - errorlint
- whitespace
- gofumpt
- godot
- nestif - nestif
- godox - godot
- funlen - gofumpt
- gci
- gocognit
- paralleltest - paralleltest
- tparallel
- thelper - thelper
- ifshort - ifshort
- gomoddirectives
- cyclop
- forcetypeassert
- ireturn
- tagliatelle
- varnamelen
- goimports
- tenv
- golint
- exhaustruct - exhaustruct
- nilnil - varnamelen
- gci
- depguard
- errchkjson
- inamedparam
- nonamedreturns - nonamedreturns
- musttag
- ireturn
- forcetypeassert
- cyclop
# deprecated linters
- deadcode
- interfacer
- scopelint
- varcheck
- structcheck
- golint
- nosnakecase - nosnakecase

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