diff --git a/plugins/cmd/ndpgen/.gitignore b/plugins/cmd/ndpgen/.gitignore new file mode 100644 index 000000000..315ccc05f --- /dev/null +++ b/plugins/cmd/ndpgen/.gitignore @@ -0,0 +1 @@ +ndpgen \ No newline at end of file diff --git a/plugins/cmd/ndpgen/go.mod b/plugins/cmd/ndpgen/go.mod index ff5fb77ee..23a40564f 100644 --- a/plugins/cmd/ndpgen/go.mod +++ b/plugins/cmd/ndpgen/go.mod @@ -3,21 +3,26 @@ module github.com/navidrome/navidrome/plugins/cmd/ndpgen go 1.25 require ( - github.com/onsi/ginkgo/v2 v2.22.2 - github.com/onsi/gomega v1.36.2 + github.com/extism/go-pdk v1.1.3 + github.com/onsi/ginkgo/v2 v2.27.3 + github.com/onsi/gomega v1.38.3 github.com/xeipuuv/gojsonschema v1.2.0 + golang.org/x/tools v0.40.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/go-logr/logr v1.4.2 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.28.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect ) diff --git a/plugins/cmd/ndpgen/go.sum b/plugins/cmd/ndpgen/go.sum index 5a011b24d..d95fa78b8 100644 --- a/plugins/cmd/ndpgen/go.sum +++ b/plugins/cmd/ndpgen/go.sum @@ -1,42 +1,80 @@ +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= +github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -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/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= -github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= +github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8= +github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM= +github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= 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/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/cmd/ndpgen/internal/generator.go b/plugins/cmd/ndpgen/internal/generator.go index 8d264c5e5..26df6fc17 100644 --- a/plugins/cmd/ndpgen/internal/generator.go +++ b/plugins/cmd/ndpgen/internal/generator.go @@ -640,3 +640,220 @@ func GenerateCapabilityRustLib(capabilities []Capability) ([]byte, error) { return buf.Bytes(), nil } + +// pdkFuncMap returns the template functions for PDK code generation. +func pdkFuncMap() template.FuncMap { + return template.FuncMap{ + "firstSentence": firstSentence, + "paramList": pdkParamList, + "returnList": pdkReturnList, + "argList": pdkArgList, + "argListWithReceiver": pdkArgListWithReceiver, + "mockReturns": pdkMockReturns, + "constValue": pdkConstValue, + "stubTypeUnderlying": stubTypeUnderlying, + "methodReceiver": pdkMethodReceiver, + } +} + +// stubTypeUnderlying returns the appropriate stub type for non-WASM builds. +// For types that reference internal packages (like memory.Memory), returns "struct{}". +func stubTypeUnderlying(t PDKType) string { + underlying := t.Underlying + // If the underlying type references a package (contains a dot), use a stub struct + if strings.Contains(underlying, ".") { + return "struct{}" + } + // For simple types like int, int32, return as-is + return underlying +} + +// firstSentence returns the first sentence of a doc string, normalized to a single line. +func firstSentence(doc string) string { + if doc == "" { + return "" + } + // Normalize whitespace (replace newlines with spaces, collapse multiple spaces) + doc = strings.Join(strings.Fields(doc), " ") + + // Find first period followed by space or end + for i, r := range doc { + if r == '.' && (i+1 >= len(doc) || doc[i+1] == ' ') { + return doc[:i+1] + } + } + return doc +} + +// pdkParamList generates a parameter list string for function signature. +func pdkParamList(params []PDKParam) string { + var parts []string + for _, p := range params { + if p.Name != "" { + parts = append(parts, p.Name+" "+p.Type) + } else { + parts = append(parts, p.Type) + } + } + return strings.Join(parts, ", ") +} + +// pdkReturnList generates a return list string for function signature. +func pdkReturnList(returns []PDKReturn) string { + if len(returns) == 0 { + return "" + } + if len(returns) == 1 && returns[0].Name == "" { + return " " + returns[0].Type + } + var parts []string + for _, r := range returns { + if r.Name != "" { + parts = append(parts, r.Name+" "+r.Type) + } else { + parts = append(parts, r.Type) + } + } + return " (" + strings.Join(parts, ", ") + ")" +} + +// pdkArgList generates an argument list string for function call. +func pdkArgList(params []PDKParam) string { + var parts []string + for _, p := range params { + if p.Name != "" { + parts = append(parts, p.Name) + } else { + parts = append(parts, "_") + } + } + return strings.Join(parts, ", ") +} + +// pdkArgListWithReceiver generates an argument list that includes the receiver variable +// as the first argument to PDKMock.Called(). This allows tests to verify which instance +// a method was called on. +func pdkArgListWithReceiver(params []PDKParam, typeName string) string { + // Use lowercase first letter of type name as receiver variable + receiverVar := strings.ToLower(typeName[:1]) + parts := []string{receiverVar} + for _, p := range params { + if p.Name != "" { + parts = append(parts, p.Name) + } else { + parts = append(parts, "_") + } + } + return strings.Join(parts, ", ") +} + +// pdkMethodReceiver generates the receiver declaration for a method. +// Example: "r *HTTPRequest" or "m Memory" +func pdkMethodReceiver(receiver, typeName string) string { + receiverVar := strings.ToLower(typeName[:1]) + if strings.HasPrefix(receiver, "*") { + return receiverVar + " *" + typeName + } + return receiverVar + " " + typeName +} + +// pdkMockReturns generates the mock return accessors for a function. +func pdkMockReturns(returns []PDKReturn) string { + var parts []string + for i, r := range returns { + parts = append(parts, mockAccessorForType(r.Type, i)) + } + return strings.Join(parts, ", ") +} + +// mockAccessorForType returns the testify mock accessor for a type. +func mockAccessorForType(typ string, idx int) string { + switch typ { + case "string": + return fmt.Sprintf("args.String(%d)", idx) + case "bool": + return fmt.Sprintf("args.Bool(%d)", idx) + case "int": + return fmt.Sprintf("args.Int(%d)", idx) + case "error": + return fmt.Sprintf("args.Error(%d)", idx) + case "[]byte": + return fmt.Sprintf("args.Get(%d).([]byte)", idx) + case "uint64": + return fmt.Sprintf("args.Get(%d).(uint64)", idx) + case "uint32": + return fmt.Sprintf("args.Get(%d).(uint32)", idx) + case "uint16": + return fmt.Sprintf("args.Get(%d).(uint16)", idx) + default: + return fmt.Sprintf("args.Get(%d).(%s)", idx, typ) + } +} + +// pdkConstValue returns the value expression for a constant. +func pdkConstValue(c PDKConst) string { + if c.Value == "" || c.Value == "iota" { + return "iota" + } + return c.Value +} + +// GeneratePDKGo generates the WASM implementation of the PDK wrapper package. +func GeneratePDKGo(symbols *PDKSymbols) ([]byte, error) { + tmplContent, err := templatesFS.ReadFile("templates/pdk.go.tmpl") + if err != nil { + return nil, fmt.Errorf("reading pdk template: %w", err) + } + + tmpl, err := template.New("pdk").Funcs(pdkFuncMap()).Parse(string(tmplContent)) + if err != nil { + return nil, fmt.Errorf("parsing template: %w", err) + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, symbols); err != nil { + return nil, fmt.Errorf("executing template: %w", err) + } + + return buf.Bytes(), nil +} + +// GeneratePDKGoStub generates the native stub implementation of the PDK wrapper package. +func GeneratePDKGoStub(symbols *PDKSymbols) ([]byte, error) { + tmplContent, err := templatesFS.ReadFile("templates/pdk_stub.go.tmpl") + if err != nil { + return nil, fmt.Errorf("reading pdk stub template: %w", err) + } + + tmpl, err := template.New("pdk_stub").Funcs(pdkFuncMap()).Parse(string(tmplContent)) + if err != nil { + return nil, fmt.Errorf("parsing template: %w", err) + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, symbols); err != nil { + return nil, fmt.Errorf("executing template: %w", err) + } + + return buf.Bytes(), nil +} + +// GeneratePDKTypesStub generates the native type definitions for the PDK wrapper package. +func GeneratePDKTypesStub(symbols *PDKSymbols) ([]byte, error) { + tmplContent, err := templatesFS.ReadFile("templates/types_stub.go.tmpl") + if err != nil { + return nil, fmt.Errorf("reading types stub template: %w", err) + } + + tmpl, err := template.New("types_stub").Funcs(pdkFuncMap()).Parse(string(tmplContent)) + if err != nil { + return nil, fmt.Errorf("parsing template: %w", err) + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, symbols); err != nil { + return nil, fmt.Errorf("executing template: %w", err) + } + + return buf.Bytes(), nil +} diff --git a/plugins/cmd/ndpgen/internal/pdk_parser.go b/plugins/cmd/ndpgen/internal/pdk_parser.go new file mode 100644 index 000000000..4756334dd --- /dev/null +++ b/plugins/cmd/ndpgen/internal/pdk_parser.go @@ -0,0 +1,441 @@ +package internal + +import ( + "fmt" + "go/ast" + "go/token" + "sort" + "strings" + + "golang.org/x/tools/go/packages" +) + +// PDKSymbols contains all exported symbols parsed from extism/go-pdk. +type PDKSymbols struct { + Types []PDKType + Consts []PDKConst + Functions []PDKFunc +} + +// PDKType represents an exported type from extism/go-pdk. +type PDKType struct { + Name string + Underlying string // The underlying type (e.g., "int" for LogLevel) + IsAlias bool // True if it's a type alias (type X = Y) + Doc string // Documentation comment + Methods []PDKFunc // Methods on this type + Fields []PDKField // Struct fields (if it's a struct type) +} + +// PDKField represents a struct field. +type PDKField struct { + Name string + Type string + Tag string // Struct tag (e.g., `json:"name"`) +} + +// PDKConst represents an exported constant from extism/go-pdk. +type PDKConst struct { + Name string + Type string // The type name (may be empty for untyped consts) + Value string // The value expression + Doc string +} + +// PDKFunc represents an exported function from extism/go-pdk. +type PDKFunc struct { + Name string + Doc string + Receiver string // Empty for package-level functions + Params []PDKParam + Returns []PDKReturn + IsVariadic bool +} + +// PDKParam represents a function parameter. +type PDKParam struct { + Name string + Type string +} + +// PDKReturn represents a function return value. +type PDKReturn struct { + Name string // May be empty for unnamed returns + Type string +} + +// ParseExtismPDK parses the extism/go-pdk package and extracts all exported symbols. +func ParseExtismPDK() (*PDKSymbols, error) { + // Load both packages with syntax trees in one call + cfg := &packages.Config{ + Mode: packages.NeedName | packages.NeedSyntax | packages.NeedFiles, + } + pkgs, err := packages.Load(cfg, + "github.com/extism/go-pdk", + "github.com/extism/go-pdk/internal/memory", + ) + if err != nil { + return nil, fmt.Errorf("loading extism/go-pdk: %w", err) + } + + // Find both packages + var pdkPkg, memoryPkg *packages.Package + for _, pkg := range pkgs { + if len(pkg.Errors) > 0 { + return nil, fmt.Errorf("loading %s: %v", pkg.PkgPath, pkg.Errors[0]) + } + switch pkg.Name { + case "pdk": + pdkPkg = pkg + case "memory": + memoryPkg = pkg + } + } + if pdkPkg == nil { + return nil, fmt.Errorf("package github.com/extism/go-pdk not found") + } + if memoryPkg == nil { + return nil, fmt.Errorf("package github.com/extism/go-pdk/internal/memory not found") + } + + symbols := &PDKSymbols{} + seenTypes := make(map[string]bool) + + // Extract Memory type from internal/memory package first + extractMemorySymbols(memoryPkg.Syntax, symbols, seenTypes) + + // First pass: collect types from pdk package (skip if already found in internal packages) + for _, file := range pdkPkg.Syntax { + for _, decl := range file.Decls { + if genDecl, ok := decl.(*ast.GenDecl); ok { + for _, spec := range genDecl.Specs { + if typeSpec, ok := spec.(*ast.TypeSpec); ok { + if !typeSpec.Name.IsExported() { + continue + } + // Skip if we already have this type (from internal packages) + if seenTypes[typeSpec.Name.Name] { + continue + } + seenTypes[typeSpec.Name.Name] = true + pdkType := extractType(typeSpec, genDecl.Doc) + symbols.Types = append(symbols.Types, pdkType) + } + } + } + } + } + + // Build typeMap from the final slice (after all types are added) + typeMap := make(map[string]*PDKType) + for i := range symbols.Types { + typeMap[symbols.Types[i].Name] = &symbols.Types[i] + } + + // Second pass: collect functions and methods from pdk package + for _, file := range pdkPkg.Syntax { + for _, decl := range file.Decls { + switch d := decl.(type) { + case *ast.GenDecl: + if d.Tok == token.CONST { + consts := extractConsts(d) + symbols.Consts = append(symbols.Consts, consts...) + } + case *ast.FuncDecl: + if !d.Name.IsExported() { + continue + } + fn := extractFunc(d) + if fn.Receiver != "" { + // It's a method, associate with type + typeName := fn.Receiver + if strings.HasPrefix(typeName, "*") { + typeName = typeName[1:] + } + if t, ok := typeMap[typeName]; ok { + t.Methods = append(t.Methods, fn) + } + } else { + symbols.Functions = append(symbols.Functions, fn) + } + } + } + } + + // Sort for consistent output + sort.Slice(symbols.Types, func(i, j int) bool { + return symbols.Types[i].Name < symbols.Types[j].Name + }) + sort.Slice(symbols.Consts, func(i, j int) bool { + return symbols.Consts[i].Name < symbols.Consts[j].Name + }) + sort.Slice(symbols.Functions, func(i, j int) bool { + return symbols.Functions[i].Name < symbols.Functions[j].Name + }) + + return symbols, nil +} + +func extractType(spec *ast.TypeSpec, doc *ast.CommentGroup) PDKType { + t := PDKType{ + Name: spec.Name.Name, + Doc: extractDoc(doc), + } + + // Check if it's an alias (type X = Y) + t.IsAlias = spec.Assign.IsValid() + + // Extract underlying type + t.Underlying = typeString(spec.Type) + + // Extract struct fields if it's a struct type + if structType, ok := spec.Type.(*ast.StructType); ok { + t.Fields = extractStructFields(structType) + } + + return t +} + +func extractStructFields(st *ast.StructType) []PDKField { + var fields []PDKField + if st.Fields == nil { + return fields + } + + for _, field := range st.Fields.List { + fieldType := typeString(field.Type) + tag := "" + if field.Tag != nil { + tag = field.Tag.Value + } + + if len(field.Names) == 0 { + // Embedded field + fields = append(fields, PDKField{ + Name: fieldType, // Use type name as field name for embedded + Type: fieldType, + Tag: tag, + }) + } else { + for _, name := range field.Names { + // Skip unexported fields + if !name.IsExported() { + continue + } + fields = append(fields, PDKField{ + Name: name.Name, + Type: fieldType, + Tag: tag, + }) + } + } + } + return fields +} + +func extractConsts(decl *ast.GenDecl) []PDKConst { + var consts []PDKConst + var currentType string // For iota-style const blocks + + for i, spec := range decl.Specs { + valSpec, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + + // Update type if specified + if valSpec.Type != nil { + currentType = typeString(valSpec.Type) + } + + for j, name := range valSpec.Names { + if !name.IsExported() { + continue + } + + c := PDKConst{ + Name: name.Name, + Type: currentType, + } + + // Extract value + if j < len(valSpec.Values) { + c.Value = exprString(valSpec.Values[j]) + } else if i == 0 && j == 0 { + // First const with no value - likely iota + c.Value = "iota" + } + + // Extract doc + if valSpec.Doc != nil { + c.Doc = extractDoc(valSpec.Doc) + } else if i == 0 && decl.Doc != nil { + c.Doc = extractDoc(decl.Doc) + } + + consts = append(consts, c) + } + } + + return consts +} + +func extractFunc(decl *ast.FuncDecl) PDKFunc { + fn := PDKFunc{ + Name: decl.Name.Name, + Doc: extractDoc(decl.Doc), + } + + // Extract receiver + if decl.Recv != nil && len(decl.Recv.List) > 0 { + fn.Receiver = typeString(decl.Recv.List[0].Type) + } + + // Extract parameters + if decl.Type.Params != nil { + for _, field := range decl.Type.Params.List { + paramType := typeString(field.Type) + + // Check for variadic + if _, ok := field.Type.(*ast.Ellipsis); ok { + fn.IsVariadic = true + } + + if len(field.Names) == 0 { + // Unnamed parameter + fn.Params = append(fn.Params, PDKParam{Type: paramType}) + } else { + for _, name := range field.Names { + fn.Params = append(fn.Params, PDKParam{ + Name: name.Name, + Type: paramType, + }) + } + } + } + } + + // Extract returns + if decl.Type.Results != nil { + for _, field := range decl.Type.Results.List { + retType := typeString(field.Type) + + if len(field.Names) == 0 { + // Unnamed return + fn.Returns = append(fn.Returns, PDKReturn{Type: retType}) + } else { + for _, name := range field.Names { + fn.Returns = append(fn.Returns, PDKReturn{ + Name: name.Name, + Type: retType, + }) + } + } + } + } + + return fn +} + +func extractDoc(doc *ast.CommentGroup) string { + if doc == nil { + return "" + } + return strings.TrimSpace(doc.Text()) +} + +func typeString(expr ast.Expr) string { + switch t := expr.(type) { + case *ast.Ident: + return t.Name + case *ast.StarExpr: + return "*" + typeString(t.X) + case *ast.SelectorExpr: + return typeString(t.X) + "." + t.Sel.Name + case *ast.ArrayType: + if t.Len == nil { + return "[]" + typeString(t.Elt) + } + return fmt.Sprintf("[%s]%s", exprString(t.Len), typeString(t.Elt)) + case *ast.MapType: + return fmt.Sprintf("map[%s]%s", typeString(t.Key), typeString(t.Value)) + case *ast.InterfaceType: + return "any" // Simplified + case *ast.Ellipsis: + return "..." + typeString(t.Elt) + case *ast.StructType: + return "struct{}" // Simplified for anonymous structs + case *ast.FuncType: + return "func()" // Simplified + default: + return fmt.Sprintf("%T", expr) + } +} + +func exprString(expr ast.Expr) string { + switch e := expr.(type) { + case *ast.Ident: + return e.Name + case *ast.BasicLit: + return e.Value + case *ast.BinaryExpr: + return exprString(e.X) + " " + e.Op.String() + " " + exprString(e.Y) + case *ast.UnaryExpr: + return e.Op.String() + exprString(e.X) + case *ast.CallExpr: + return typeString(e.Fun) + "(...)" + default: + return fmt.Sprintf("%T", expr) + } +} + +// extractMemorySymbols extracts the Memory type and its methods from already-parsed syntax trees. +// This is needed because Memory is defined in internal/memory but re-exported by the pdk package. +func extractMemorySymbols(files []*ast.File, symbols *PDKSymbols, seenTypes map[string]bool) { + // Collect the Memory type + for _, file := range files { + for _, decl := range file.Decls { + if genDecl, ok := decl.(*ast.GenDecl); ok { + for _, spec := range genDecl.Specs { + if typeSpec, ok := spec.(*ast.TypeSpec); ok { + // Only interested in Memory type + if typeSpec.Name.Name == "Memory" { + pdkType := extractType(typeSpec, genDecl.Doc) + symbols.Types = append(symbols.Types, pdkType) + seenTypes["Memory"] = true + } + } + } + } + } + } + + // Build local type map for method association + localTypeMap := make(map[string]*PDKType) + for i := range symbols.Types { + localTypeMap[symbols.Types[i].Name] = &symbols.Types[i] + } + + // Collect methods for Memory + for _, file := range files { + for _, decl := range file.Decls { + if funcDecl, ok := decl.(*ast.FuncDecl); ok { + if !funcDecl.Name.IsExported() { + continue + } + fn := extractFunc(funcDecl) + if fn.Receiver != "" { + typeName := fn.Receiver + if strings.HasPrefix(typeName, "*") { + typeName = typeName[1:] + } + if typeName == "Memory" { + if t, ok := localTypeMap["Memory"]; ok { + t.Methods = append(t.Methods, fn) + } + } + } + } + } + } +} diff --git a/plugins/cmd/ndpgen/internal/templates/capability.go.tmpl b/plugins/cmd/ndpgen/internal/templates/capability.go.tmpl index c90c55791..ebcd80739 100644 --- a/plugins/cmd/ndpgen/internal/templates/capability.go.tmpl +++ b/plugins/cmd/ndpgen/internal/templates/capability.go.tmpl @@ -8,7 +8,7 @@ package {{.Package}} import ( - pdk "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) {{- /* Generate type alias definitions */ -}} diff --git a/plugins/cmd/ndpgen/internal/templates/client.go.tmpl b/plugins/cmd/ndpgen/internal/templates/client.go.tmpl index 195100f0f..a99796f66 100644 --- a/plugins/cmd/ndpgen/internal/templates/client.go.tmpl +++ b/plugins/cmd/ndpgen/internal/templates/client.go.tmpl @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) {{- /* Generate struct definitions */ -}} diff --git a/plugins/cmd/ndpgen/internal/templates/go.mod.tmpl b/plugins/cmd/ndpgen/internal/templates/go.mod.tmpl index 97f807123..3916cd749 100644 --- a/plugins/cmd/ndpgen/internal/templates/go.mod.tmpl +++ b/plugins/cmd/ndpgen/internal/templates/go.mod.tmpl @@ -2,4 +2,7 @@ module github.com/navidrome/navidrome/plugins/pdk/go go 1.25 -require github.com/extism/go-pdk v1.1.3 +require ( + github.com/extism/go-pdk v1.1.3 + github.com/stretchr/testify v1.11.1 +) diff --git a/plugins/cmd/ndpgen/internal/templates/pdk.go.tmpl b/plugins/cmd/ndpgen/internal/templates/pdk.go.tmpl new file mode 100644 index 000000000..ebaf88df0 --- /dev/null +++ b/plugins/cmd/ndpgen/internal/templates/pdk.go.tmpl @@ -0,0 +1,50 @@ +// Code generated by ndpgen. DO NOT EDIT. +// +// This file contains wrapper functions for the extism/go-pdk package. +// For WASM builds, it provides type aliases and function wrappers that delegate +// to the real extism/go-pdk package with zero overhead. +// +//go:build wasip1 + +package pdk + +import ( + extism "github.com/extism/go-pdk" +) + +// Type aliases - zero overhead, full compatibility +{{- range .Types}} +type {{.Name}} = extism.{{.Name}} +{{- end}} + +// Constants +{{- $prevType := ""}} +{{- range .Consts}} +{{- if ne .Type $prevType}} +{{- if ne $prevType ""}} +) +{{- end}} + +const ( +{{- end}} + {{.Name}} = extism.{{.Name}} +{{- $prevType = .Type}} +{{- end}} +{{- if ne $prevType ""}} +) +{{- end}} + +// Functions +{{- range .Functions}} + +{{- if .Doc}} +// {{.Name}} {{firstSentence .Doc}} +{{- end}} +func {{.Name}}({{paramList .Params}}){{returnList .Returns}} { +{{- if .Returns}} + return extism.{{.Name}}({{argList .Params}}) +{{- else}} + extism.{{.Name}}({{argList .Params}}) +{{- end}} +} +{{- end}} diff --git a/plugins/cmd/ndpgen/internal/templates/pdk_stub.go.tmpl b/plugins/cmd/ndpgen/internal/templates/pdk_stub.go.tmpl new file mode 100644 index 000000000..48d27be4b --- /dev/null +++ b/plugins/cmd/ndpgen/internal/templates/pdk_stub.go.tmpl @@ -0,0 +1,42 @@ +// Code generated by ndpgen. DO NOT EDIT. +// +// This file contains mock implementations for non-WASM builds. +// These mocks allow IDE support, compilation, and unit testing on non-WASM platforms. +// Plugin authors can use the exported PDKMock instance to set expectations in tests. +// +//go:build !wasip1 + +package pdk + +import "github.com/stretchr/testify/mock" + +// MockPDK is the mock implementation for testing PDK functions. +type MockPDK struct { + mock.Mock +} + +// PDKMock is the auto-instantiated mock instance for testing. +// Use this to set expectations: pdk.PDKMock.On("GetConfig", "key").Return("value", true) +var PDKMock = &MockPDK{} + +// ResetMock resets the mock to its initial state. +// Call this in test setup/teardown to ensure clean state between tests. +func ResetMock() { + PDKMock = &MockPDK{} +} + +// Functions +{{- range .Functions}} + +{{- if .Doc}} +// {{.Name}} {{firstSentence .Doc}} +{{- end}} +func {{.Name}}({{paramList .Params}}){{returnList .Returns}} { +{{- if .Returns}} + args := PDKMock.Called({{argList .Params}}) + return {{mockReturns .Returns}} +{{- else}} + PDKMock.Called({{argList .Params}}) +{{- end}} +} +{{- end}} diff --git a/plugins/cmd/ndpgen/internal/templates/types_stub.go.tmpl b/plugins/cmd/ndpgen/internal/templates/types_stub.go.tmpl new file mode 100644 index 000000000..06cbb4f1f --- /dev/null +++ b/plugins/cmd/ndpgen/internal/templates/types_stub.go.tmpl @@ -0,0 +1,192 @@ +// Code generated by ndpgen. DO NOT EDIT. +// +// This file contains type definitions for non-WASM builds. +// These types match the extism/go-pdk signatures to allow compilation and testing +// on native platforms without importing the WASM-only extism package. +// +//go:build !wasip1 + +package pdk + +// LogLevel represents a logging level. +type LogLevel int + +// Log level constants +const ( + LogTrace LogLevel = iota + LogDebug + LogInfo + LogWarn + LogError +) + +// HTTPMethod represents an HTTP method. +type HTTPMethod int32 + +// HTTP method constants +const ( + MethodGet HTTPMethod = iota + MethodHead + MethodPost + MethodPut + MethodPatch + MethodDelete + MethodConnect + MethodOptions + MethodTrace +) + +// String returns the string representation of the HTTP method. +func (m HTTPMethod) String() string { + switch m { + case MethodGet: + return "GET" + case MethodHead: + return "HEAD" + case MethodPost: + return "POST" + case MethodPut: + return "PUT" + case MethodPatch: + return "PATCH" + case MethodDelete: + return "DELETE" + case MethodConnect: + return "CONNECT" + case MethodOptions: + return "OPTIONS" + case MethodTrace: + return "TRACE" + default: + return "UNKNOWN" + } +} + +// Memory represents memory allocated by (and shared with) the host. +// This is a stub implementation for non-WASM platforms. +type Memory struct { + offset uint64 + length uint64 + data []byte +} + +// Offset returns the offset of the memory block. +func (m Memory) Offset() uint64 { + return m.offset +} + +// Length returns the length of the memory block. +func (m Memory) Length() uint64 { + return m.length +} + +// ReadBytes reads all bytes from the memory block. +func (m Memory) ReadBytes() []byte { + return m.data +} + +// Load reads the memory block into the provided buffer. +func (m *Memory) Load(buffer []byte) { + copy(buffer, m.data) +} + +// Store writes data to the memory block. +func (m *Memory) Store(data []byte) { + m.data = make([]byte, len(data)) + copy(m.data, data) + m.length = uint64(len(data)) +} + +// Free frees the memory block. +func (m *Memory) Free() { + m.data = nil + m.length = 0 +} + +// NewStubMemory creates a new stub Memory for testing. +// This is a helper function not present in the real PDK. +func NewStubMemory(offset, length uint64, data []byte) Memory { + return Memory{ + offset: offset, + length: length, + data: data, + } +} + +// HTTPRequest represents an HTTP request sent by the host. +// This is a stub implementation for non-WASM platforms. +type HTTPRequest struct { + method HTTPMethod + url string + headers map[string]string + body []byte +} + +// SetHeader sets an HTTP header key to value. +func (r *HTTPRequest) SetHeader(key string, value string) *HTTPRequest { + if r.headers == nil { + r.headers = make(map[string]string) + } + r.headers[key] = value + return r +} + +// SetBody sets the HTTP request body. +func (r *HTTPRequest) SetBody(body []byte) *HTTPRequest { + r.body = body + return r +} + +// Send sends the HTTP request and returns the response. +// In the stub implementation, this delegates to the mock. +func (r *HTTPRequest) Send() HTTPResponse { + args := PDKMock.Called(r) + return args.Get(0).(HTTPResponse) +} + +// HTTPRequestMeta represents the metadata associated with an HTTP request. +type HTTPRequestMeta struct { + URL string `json:"url"` + Method string `json:"method"` + Headers map[string]string `json:"headers"` +} + +// HTTPResponse represents an HTTP response returned from the host. +// This is a stub implementation for non-WASM platforms. +type HTTPResponse struct { + status uint16 + headers map[string]string + body []byte + memory Memory +} + +// Status returns the status code from the response. +func (r HTTPResponse) Status() uint16 { + return r.status +} + +// Headers returns the HTTP response headers. +func (r *HTTPResponse) Headers() map[string]string { + return r.headers +} + +// Body returns the body byte slice from the response. +func (r HTTPResponse) Body() []byte { + return r.body +} + +// Memory returns the memory associated with the response. +func (r HTTPResponse) Memory() Memory { + return r.memory +} + +// NewStubHTTPResponse creates a new stub HTTPResponse for testing. +// This is a helper function not present in the real PDK. +func NewStubHTTPResponse(status uint16, headers map[string]string, body []byte) HTTPResponse { + return HTTPResponse{ + status: status, + headers: headers, + body: body, + memory: NewStubMemory(0, uint64(len(body)), body), + } +} diff --git a/plugins/cmd/ndpgen/main.go b/plugins/cmd/ndpgen/main.go index a1fbf6a19..b34ee4296 100644 --- a/plugins/cmd/ndpgen/main.go +++ b/plugins/cmd/ndpgen/main.go @@ -62,6 +62,7 @@ type config struct { hostWrappers bool // Generate host wrappers (used by Navidrome server) capabilityOnly bool schemasOnly bool // Generate XTP schemas from capabilities (output goes to inputDir) + pdkOnly bool // Generate PDK abstraction layer wrapper generateGoClient bool generatePyClient bool generateRsClient bool @@ -84,6 +85,14 @@ func main() { return } + if cfg.pdkOnly { + if err := runPDKGeneration(cfg); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + return + } + if cfg.capabilityOnly { if err := runCapabilityGeneration(cfg); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) @@ -148,6 +157,128 @@ func runSchemaGeneration(cfg *config) error { return generateSchemas(cfg, capabilities) } +// runPDKGeneration handles PDK abstraction layer code generation. +// This generates the pdk wrapper package that wraps extism/go-pdk +// with mockable implementations for unit testing on native platforms. +func runPDKGeneration(cfg *config) error { + // Output directory is $output/go/pdk/ + outputDir := filepath.Join(cfg.outputDir, "go", "pdk") + return generatePDKPackageWithParsing(outputDir, cfg.dryRun, cfg.verbose) +} + +// generatePDKPackageWithParsing generates the PDK abstraction layer using AST parsing. +// It extracts all exported symbols from extism/go-pdk and generates wrappers for them. +func generatePDKPackageWithParsing(outputDir string, dryRun, verbose bool) error { + if verbose { + fmt.Println("Parsing extism/go-pdk to extract exported symbols...") + } + + // Parse extism/go-pdk to get all exported symbols + symbols, err := internal.ParseExtismPDK() + if err != nil { + return fmt.Errorf("parsing extism/go-pdk: %w", err) + } + + if verbose { + fmt.Printf("Found %d types, %d constants, %d functions\n", + len(symbols.Types), len(symbols.Consts), len(symbols.Functions)) + for _, t := range symbols.Types { + fmt.Printf(" Type %s: %d methods, %d fields\n", t.Name, len(t.Methods), len(t.Fields)) + for _, m := range t.Methods { + fmt.Printf(" Method: %s (receiver: %s)\n", m.Name, m.Receiver) + } + } + fmt.Printf("Generating PDK abstraction layer to: %s\n", outputDir) + } + + // Generate the WASM implementation (pdk.go) + pdkCode, err := internal.GeneratePDKGo(symbols) + if err != nil { + return fmt.Errorf("generating pdk.go: %w", err) + } + + formatted, err := format.Source(pdkCode) + if err != nil { + return fmt.Errorf("formatting pdk.go: %w\nRaw code:\n%s", err, pdkCode) + } + + pdkFile := filepath.Join(outputDir, "pdk.go") + + if dryRun { + fmt.Printf("=== %s ===\n%s\n", pdkFile, formatted) + } else { + if err := os.MkdirAll(outputDir, 0755); err != nil { + return fmt.Errorf("creating output directory: %w", err) + } + + if err := os.WriteFile(pdkFile, formatted, 0600); err != nil { + return fmt.Errorf("writing pdk.go: %w", err) + } + + if verbose { + fmt.Printf("Generated: %s\n", pdkFile) + } + } + + // Generate the types stub (types_stub.go) + typesStubCode, err := internal.GeneratePDKTypesStub(symbols) + if err != nil { + return fmt.Errorf("generating types_stub.go: %w", err) + } + + formattedTypesStub, err := format.Source(typesStubCode) + if err != nil { + return fmt.Errorf("formatting types_stub.go: %w\nRaw code:\n%s", err, typesStubCode) + } + + typesStubFile := filepath.Join(outputDir, "types_stub.go") + + if dryRun { + fmt.Printf("=== %s ===\n%s\n", typesStubFile, formattedTypesStub) + } else { + if err := os.WriteFile(typesStubFile, formattedTypesStub, 0600); err != nil { + return fmt.Errorf("writing types_stub.go: %w", err) + } + + if verbose { + fmt.Printf("Generated: %s\n", typesStubFile) + } + } + + // Generate the stub implementation (pdk_stub.go) + stubCode, err := internal.GeneratePDKGoStub(symbols) + if err != nil { + return fmt.Errorf("generating pdk_stub.go: %w", err) + } + + formattedStub, err := format.Source(stubCode) + if err != nil { + return fmt.Errorf("formatting pdk_stub.go: %w\nRaw code:\n%s", err, stubCode) + } + + stubFile := filepath.Join(outputDir, "pdk_stub.go") + + if dryRun { + fmt.Printf("=== %s ===\n%s\n", stubFile, formattedStub) + } else { + if err := os.WriteFile(stubFile, formattedStub, 0600); err != nil { + return fmt.Errorf("writing pdk_stub.go: %w", err) + } + + if verbose { + fmt.Printf("Generated: %s\n", stubFile) + } + } + + return nil +} + +// generatePDKPackage generates the PDK abstraction layer to a specific directory. +// This is called by generateAllCode to include the PDK package alongside host client code. +func generatePDKPackage(outputDir string, dryRun, verbose bool) error { + return generatePDKPackageWithParsing(outputDir, dryRun, verbose) +} + // runHostWrapperGeneration handles host wrapper code generation. // This generates the *_gen.go files in the input directory that are used // by Navidrome server to expose host functions to plugins. @@ -183,6 +314,7 @@ func parseConfig() (*config, error) { hostWrappers = flag.Bool("host-wrappers", false, "Generate host wrappers (used by Navidrome server, output to input directory)") capabilityOnly = flag.Bool("capability-only", false, "Generate only capability export wrappers") schemasOnly = flag.Bool("schemas", false, "Generate XTP YAML schemas from capabilities (output to input directory)") + pdkOnly = flag.Bool("extism-pdk", false, "Generate PDK abstraction layer by parsing extism/go-pdk") goClient = flag.Bool("go", false, "Generate Go client wrappers") pyClient = flag.Bool("python", false, "Generate Python client wrappers") rsClient = flag.Bool("rust", false, "Generate Rust client wrappers") @@ -205,6 +337,9 @@ func parseConfig() (*config, error) { if *schemasOnly { modeCount++ } + if *pdkOnly { + modeCount++ + } // Default to host-only if no mode is specified if modeCount == 0 { @@ -213,7 +348,7 @@ func parseConfig() (*config, error) { // Cannot specify multiple modes if modeCount > 1 { - return nil, fmt.Errorf("cannot specify multiple modes (-host-only, -host-wrappers, -capability-only, -schemas)") + return nil, fmt.Errorf("cannot specify multiple modes (-host-only, -host-wrappers, -capability-only, -schemas, -pdk)") } if *outputDir == "" { @@ -260,6 +395,7 @@ func parseConfig() (*config, error) { hostWrappers: *hostWrappers, capabilityOnly: *capabilityOnly, schemasOnly: *schemasOnly, + pdkOnly: *pdkOnly, generateGoClient: *goClient || !anyLangFlag, generatePyClient: *pyClient, generateRsClient: *rsClient, @@ -508,6 +644,11 @@ func generateAllCode(cfg *config, services []internal.Service) error { if err := generateGoModFile(cfg.goOutputDir, cfg.dryRun, cfg.verbose); err != nil { return fmt.Errorf("generating Go go.mod: %w", err) } + // Generate PDK abstraction layer alongside host client code + pdkDir := filepath.Join(filepath.Dir(cfg.goOutputDir), "pdk") + if err := generatePDKPackage(pdkDir, cfg.dryRun, cfg.verbose); err != nil { + return fmt.Errorf("generating PDK package: %w", err) + } } return nil diff --git a/plugins/cmd/ndpgen/testdata/codec_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/codec_client_expected.go.txt index d4b1ece46..93d5cb27c 100644 --- a/plugins/cmd/ndpgen/testdata/codec_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/codec_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // codec_encode is the host function provided by Navidrome. diff --git a/plugins/cmd/ndpgen/testdata/counter_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/counter_client_expected.go.txt index 959a92b47..32f24e042 100644 --- a/plugins/cmd/ndpgen/testdata/counter_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/counter_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // counter_count is the host function provided by Navidrome. diff --git a/plugins/cmd/ndpgen/testdata/echo_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/echo_client_expected.go.txt index 0e42c78ce..7a495acf4 100644 --- a/plugins/cmd/ndpgen/testdata/echo_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/echo_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // echo_echo is the host function provided by Navidrome. diff --git a/plugins/cmd/ndpgen/testdata/list_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/list_client_expected.go.txt index d0fa2218c..ea825ee43 100644 --- a/plugins/cmd/ndpgen/testdata/list_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/list_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // Filter represents the Filter data structure. diff --git a/plugins/cmd/ndpgen/testdata/math_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/math_client_expected.go.txt index af6a54a3e..9b95b50e9 100644 --- a/plugins/cmd/ndpgen/testdata/math_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/math_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // math_add is the host function provided by Navidrome. diff --git a/plugins/cmd/ndpgen/testdata/meta_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/meta_client_expected.go.txt index 9997aeaf9..4147f35f1 100644 --- a/plugins/cmd/ndpgen/testdata/meta_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/meta_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // meta_get is the host function provided by Navidrome. diff --git a/plugins/cmd/ndpgen/testdata/ping_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/ping_client_expected.go.txt index d3c270416..be9668314 100644 --- a/plugins/cmd/ndpgen/testdata/ping_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/ping_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // ping_ping is the host function provided by Navidrome. diff --git a/plugins/cmd/ndpgen/testdata/search_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/search_client_expected.go.txt index 6ccbcd899..6ea002cf3 100644 --- a/plugins/cmd/ndpgen/testdata/search_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/search_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // Result represents the Result data structure. diff --git a/plugins/cmd/ndpgen/testdata/store_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/store_client_expected.go.txt index bda29eb04..89618026c 100644 --- a/plugins/cmd/ndpgen/testdata/store_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/store_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // Item represents the Item data structure. diff --git a/plugins/cmd/ndpgen/testdata/users_client_expected.go.txt b/plugins/cmd/ndpgen/testdata/users_client_expected.go.txt index 7f11e5042..ddd71f3dc 100644 --- a/plugins/cmd/ndpgen/testdata/users_client_expected.go.txt +++ b/plugins/cmd/ndpgen/testdata/users_client_expected.go.txt @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // User represents the User data structure. diff --git a/plugins/cmd/ndpgen/tools.go b/plugins/cmd/ndpgen/tools.go new file mode 100644 index 000000000..961d4e805 --- /dev/null +++ b/plugins/cmd/ndpgen/tools.go @@ -0,0 +1,8 @@ +//go:build tools + +// This file ensures the extism/go-pdk dependency stays in go.mod. +// The PDK parser loads this package at runtime using go/packages. +// Without this import, `go mod tidy` would remove it since it's not directly imported elsewhere. +package main + +import _ "github.com/extism/go-pdk" diff --git a/plugins/examples/crypto-ticker/go.mod b/plugins/examples/crypto-ticker/go.mod index 632debd7b..f2884679a 100755 --- a/plugins/examples/crypto-ticker/go.mod +++ b/plugins/examples/crypto-ticker/go.mod @@ -2,9 +2,15 @@ module crypto-ticker go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/examples/crypto-ticker/go.sum b/plugins/examples/crypto-ticker/go.sum index c15d38292..af880eb51 100644 --- a/plugins/examples/crypto-ticker/go.sum +++ b/plugins/examples/crypto-ticker/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/examples/crypto-ticker/main.go b/plugins/examples/crypto-ticker/main.go index 9d05ddb8f..c2ef313bb 100755 --- a/plugins/examples/crypto-ticker/main.go +++ b/plugins/examples/crypto-ticker/main.go @@ -9,9 +9,9 @@ import ( "fmt" "strings" - "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/host" "github.com/navidrome/navidrome/plugins/pdk/go/lifecycle" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" "github.com/navidrome/navidrome/plugins/pdk/go/scheduler" "github.com/navidrome/navidrome/plugins/pdk/go/websocket" ) diff --git a/plugins/examples/discord-rich-presence/go.mod b/plugins/examples/discord-rich-presence/go.mod index 723598854..a3ac0d326 100644 --- a/plugins/examples/discord-rich-presence/go.mod +++ b/plugins/examples/discord-rich-presence/go.mod @@ -2,13 +2,11 @@ module discord-rich-presence go 1.25 -require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 -) +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect diff --git a/plugins/examples/discord-rich-presence/main.go b/plugins/examples/discord-rich-presence/main.go index 10099d87d..55c6e6be2 100644 --- a/plugins/examples/discord-rich-presence/main.go +++ b/plugins/examples/discord-rich-presence/main.go @@ -15,8 +15,8 @@ import ( "strings" "time" - "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" "github.com/navidrome/navidrome/plugins/pdk/go/scheduler" "github.com/navidrome/navidrome/plugins/pdk/go/scrobbler" "github.com/navidrome/navidrome/plugins/pdk/go/websocket" diff --git a/plugins/examples/discord-rich-presence/rpc.go b/plugins/examples/discord-rich-presence/rpc.go index 142d26484..229bc0f22 100644 --- a/plugins/examples/discord-rich-presence/rpc.go +++ b/plugins/examples/discord-rich-presence/rpc.go @@ -10,8 +10,8 @@ import ( "fmt" "strings" - "github.com/extism/go-pdk" - host "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" "github.com/navidrome/navidrome/plugins/pdk/go/websocket" ) diff --git a/plugins/examples/minimal/go.mod b/plugins/examples/minimal/go.mod index 23d6c25bb..b8f6c5fc0 100644 --- a/plugins/examples/minimal/go.mod +++ b/plugins/examples/minimal/go.mod @@ -4,6 +4,13 @@ go 1.25 require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 -require github.com/extism/go-pdk v1.1.3 // indirect +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/examples/minimal/go.sum b/plugins/examples/minimal/go.sum index c15d38292..af880eb51 100644 --- a/plugins/examples/minimal/go.sum +++ b/plugins/examples/minimal/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/examples/wikimedia/go.mod b/plugins/examples/wikimedia/go.mod index e376c66f4..17f14b065 100644 --- a/plugins/examples/wikimedia/go.mod +++ b/plugins/examples/wikimedia/go.mod @@ -2,9 +2,15 @@ module wikimedia-plugin go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/examples/wikimedia/go.sum b/plugins/examples/wikimedia/go.sum index c15d38292..af880eb51 100644 --- a/plugins/examples/wikimedia/go.sum +++ b/plugins/examples/wikimedia/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/examples/wikimedia/main.go b/plugins/examples/wikimedia/main.go index 7b8aa426f..0e2146f56 100644 --- a/plugins/examples/wikimedia/main.go +++ b/plugins/examples/wikimedia/main.go @@ -14,8 +14,8 @@ import ( "net/url" "strings" - pdk "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/metadata" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // wikimediaPlugin implements the metadata provider interfaces for the methods we support. diff --git a/plugins/pdk/go/README.md b/plugins/pdk/go/README.md index 2bd0155ff..70c680b1f 100644 --- a/plugins/pdk/go/README.md +++ b/plugins/pdk/go/README.md @@ -255,6 +255,66 @@ make plugin.wasm The PDK includes [testify/mock](https://github.com/stretchr/testify) implementations for all host services, allowing you to unit test your plugin code on non-WASM platforms (your development machine). +### PDK Abstraction Layer + +The `pdk` subpackage provides a testable wrapper around the Extism PDK functions. Instead of importing +`github.com/extism/go-pdk` directly, import the abstraction layer: + +```go +import "github.com/navidrome/navidrome/plugins/pdk/go/pdk" + +func myFunction() { + // Use pdk functions - same API as extism/go-pdk + config, ok := pdk.GetConfig("my_setting") + if ok { + pdk.Log(pdk.LogInfo, "Setting: " + config) + } + + var input MyInput + if err := pdk.InputJSON(&input); err != nil { + pdk.SetError(err) + return + } + + output := processInput(input) + pdk.OutputJSON(output) +} +``` + +For WASM builds, these functions delegate directly to `extism/go-pdk` with zero overhead. +For native builds (tests), they use mocks that you can configure: + +```go +package myplugin + +import ( + "testing" + + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" +) + +func TestMyFunction(t *testing.T) { + // Reset mock state before each test + pdk.ResetMock() + + // Set up expectations + pdk.PDKMock.On("GetConfig", "my_setting").Return("test_value", true) + pdk.PDKMock.On("Log", pdk.LogInfo, "Setting: test_value").Return() + pdk.PDKMock.On("InputJSON", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + // Populate the input struct + input := args.Get(0).(*MyInput) + input.Name = "test" + }) + pdk.PDKMock.On("OutputJSON", mock.Anything).Return(nil) + + // Call your function + myFunction() + + // Verify expectations + pdk.PDKMock.AssertExpectations(t) +} +``` + ### Mock Instances Each host service has an auto-instantiated mock instance: @@ -312,3 +372,8 @@ go test ./... The stub files with mocks are only compiled for non-WASM builds (`//go:build !wasip1`), so they won't affect your production WASM binary. + +### Complete Examples + +For more comprehensive examples including HTTP requests, Memory handling, and various testing patterns, +see [pdk/example_test.go](pdk/example_test.go). diff --git a/plugins/pdk/go/host/nd_host_artwork.go b/plugins/pdk/go/host/nd_host_artwork.go index 78c157352..05fcdebe2 100644 --- a/plugins/pdk/go/host/nd_host_artwork.go +++ b/plugins/pdk/go/host/nd_host_artwork.go @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // artwork_getartisturl is the host function provided by Navidrome. diff --git a/plugins/pdk/go/host/nd_host_cache.go b/plugins/pdk/go/host/nd_host_cache.go index 075185f2f..7fd9d10fa 100644 --- a/plugins/pdk/go/host/nd_host_cache.go +++ b/plugins/pdk/go/host/nd_host_cache.go @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // cache_setstring is the host function provided by Navidrome. diff --git a/plugins/pdk/go/host/nd_host_kvstore.go b/plugins/pdk/go/host/nd_host_kvstore.go index 107898194..92ac9d772 100644 --- a/plugins/pdk/go/host/nd_host_kvstore.go +++ b/plugins/pdk/go/host/nd_host_kvstore.go @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // kvstore_set is the host function provided by Navidrome. diff --git a/plugins/pdk/go/host/nd_host_library.go b/plugins/pdk/go/host/nd_host_library.go index 4449eed2c..0107d1afe 100644 --- a/plugins/pdk/go/host/nd_host_library.go +++ b/plugins/pdk/go/host/nd_host_library.go @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // Library represents the Library data structure. diff --git a/plugins/pdk/go/host/nd_host_scheduler.go b/plugins/pdk/go/host/nd_host_scheduler.go index be43d984f..0159533ce 100644 --- a/plugins/pdk/go/host/nd_host_scheduler.go +++ b/plugins/pdk/go/host/nd_host_scheduler.go @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // scheduler_scheduleonetime is the host function provided by Navidrome. diff --git a/plugins/pdk/go/host/nd_host_subsonicapi.go b/plugins/pdk/go/host/nd_host_subsonicapi.go index d729c1bc9..87469ce32 100644 --- a/plugins/pdk/go/host/nd_host_subsonicapi.go +++ b/plugins/pdk/go/host/nd_host_subsonicapi.go @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // subsonicapi_call is the host function provided by Navidrome. diff --git a/plugins/pdk/go/host/nd_host_websocket.go b/plugins/pdk/go/host/nd_host_websocket.go index 38d58ff24..956f63c21 100644 --- a/plugins/pdk/go/host/nd_host_websocket.go +++ b/plugins/pdk/go/host/nd_host_websocket.go @@ -11,7 +11,7 @@ import ( "encoding/json" "errors" - "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // websocket_connect is the host function provided by Navidrome. diff --git a/plugins/pdk/go/lifecycle/lifecycle.go b/plugins/pdk/go/lifecycle/lifecycle.go index 7aa29901a..93b5cf37b 100644 --- a/plugins/pdk/go/lifecycle/lifecycle.go +++ b/plugins/pdk/go/lifecycle/lifecycle.go @@ -8,7 +8,7 @@ package lifecycle import ( - pdk "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // Lifecycle is the marker interface for lifecycle plugins. diff --git a/plugins/pdk/go/metadata/metadata.go b/plugins/pdk/go/metadata/metadata.go index ca744d8df..2d0b45947 100644 --- a/plugins/pdk/go/metadata/metadata.go +++ b/plugins/pdk/go/metadata/metadata.go @@ -8,7 +8,7 @@ package metadata import ( - pdk "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // AlbumImagesResponse is the response for GetAlbumImages. diff --git a/plugins/pdk/go/pdk/example_test.go b/plugins/pdk/go/pdk/example_test.go new file mode 100644 index 000000000..5678bddd4 --- /dev/null +++ b/plugins/pdk/go/pdk/example_test.go @@ -0,0 +1,324 @@ +// Example test demonstrating how to use the PDK mock for unit testing. +// This file is only compiled for non-WASM builds. +// +//go:build !wasip1 + +package pdk_test + +import ( + "testing" + + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" + "github.com/stretchr/testify/mock" +) + +// ExamplePlugin demonstrates a simple plugin that uses PDK functions. +type ExamplePlugin struct{} + +// ProcessMessage reads input, logs it, and outputs a response. +func (p *ExamplePlugin) ProcessMessage() error { + // Get configuration + prefix, ok := pdk.GetConfig("message_prefix") + if !ok { + prefix = "Hello" + } + + // Read input + message := pdk.InputString() + + // Log the message + pdk.Log(pdk.LogInfo, "Processing: "+message) + + // Output the response + pdk.OutputString(prefix + ", " + message + "!") + + return nil +} + +func TestExamplePlugin_ProcessMessage(t *testing.T) { + // Reset mock state before the test + pdk.ResetMock() + + // Set up expectations + pdk.PDKMock.On("GetConfig", "message_prefix").Return("Hi", true) + pdk.PDKMock.On("InputString").Return("World") + pdk.PDKMock.On("Log", pdk.LogInfo, "Processing: World").Return() + pdk.PDKMock.On("OutputString", "Hi, World!").Return() + + // Call the plugin function + plugin := &ExamplePlugin{} + err := plugin.ProcessMessage() + + // Verify no error + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Verify all expected calls were made + pdk.PDKMock.AssertExpectations(t) +} + +func TestExamplePlugin_ProcessMessage_DefaultPrefix(t *testing.T) { + // Reset mock state before the test + pdk.ResetMock() + + // Set up expectations - config key not found + pdk.PDKMock.On("GetConfig", "message_prefix").Return("", false) + pdk.PDKMock.On("InputString").Return("Test") + pdk.PDKMock.On("Log", pdk.LogInfo, "Processing: Test").Return() + pdk.PDKMock.On("OutputString", "Hello, Test!").Return() + + // Call the plugin function + plugin := &ExamplePlugin{} + err := plugin.ProcessMessage() + + // Verify no error + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Verify all expected calls were made + pdk.PDKMock.AssertExpectations(t) +} + +// Example of testing JSON input/output +type Request struct { + Name string `json:"name"` + Count int `json:"count"` +} + +type Response struct { + Message string `json:"message"` + Total int `json:"total"` +} + +func ProcessJSONRequest() error { + var req Request + if err := pdk.InputJSON(&req); err != nil { + pdk.SetError(err) + return err + } + + resp := Response{ + Message: "Hello, " + req.Name, + Total: req.Count * 2, + } + + return pdk.OutputJSON(resp) +} + +func TestProcessJSONRequest(t *testing.T) { + pdk.ResetMock() + + // Mock InputJSON to populate the request struct + pdk.PDKMock.On("InputJSON", mock.AnythingOfType("*pdk_test.Request")). + Return(nil). + Run(func(args mock.Arguments) { + req := args.Get(0).(*Request) + req.Name = "Alice" + req.Count = 5 + }) + + // Expect OutputJSON with the correct response + pdk.PDKMock.On("OutputJSON", Response{ + Message: "Hello, Alice", + Total: 10, + }).Return(nil) + + // Call the function + err := ProcessJSONRequest() + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + pdk.PDKMock.AssertExpectations(t) +} + +// ============================================================================= +// Examples using stub types (Memory, HTTPRequest, HTTPResponse) +// ============================================================================= + +// FetchData demonstrates a plugin function that makes an HTTP request. +func FetchData(url string) ([]byte, error) { + // Create and configure the HTTP request + // Note: SetHeader and SetBody work directly on the stub - no mocking needed! + req := pdk.NewHTTPRequest(pdk.MethodGet, url) + req.SetHeader("Accept", "application/json") + req.SetHeader("User-Agent", "MyPlugin/1.0") + + // Send the request - this is mocked because it requires host interaction + resp := req.Send() + + // Check status - works directly on the stub + if resp.Status() != 200 { + return nil, nil + } + + // Return body - works directly on the stub + return resp.Body(), nil +} + +func TestFetchData(t *testing.T) { + pdk.ResetMock() + + // Create a stub response with test data + expectedBody := []byte(`{"result": "success"}`) + stubResponse := pdk.NewStubHTTPResponse(200, map[string]string{ + "Content-Type": "application/json", + }, expectedBody) + + // Mock NewHTTPRequest to return a real HTTPRequest struct + // The struct methods (SetHeader, SetBody) work without mocking + pdk.PDKMock.On("NewHTTPRequest", pdk.MethodGet, "https://api.example.com/data"). + Return(&pdk.HTTPRequest{}) + + // Mock Send to return our stub response + pdk.PDKMock.On("Send", mock.AnythingOfType("*pdk.HTTPRequest")). + Return(stubResponse) + + // Call the function + body, err := FetchData("https://api.example.com/data") + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if string(body) != string(expectedBody) { + t.Errorf("expected body %q, got %q", expectedBody, body) + } + + pdk.PDKMock.AssertExpectations(t) +} + +func TestFetchData_NonOKStatus(t *testing.T) { + pdk.ResetMock() + + // Create a stub response with 404 status + stubResponse := pdk.NewStubHTTPResponse(404, nil, []byte("Not Found")) + + pdk.PDKMock.On("NewHTTPRequest", pdk.MethodGet, "https://api.example.com/missing"). + Return(&pdk.HTTPRequest{}) + pdk.PDKMock.On("Send", mock.AnythingOfType("*pdk.HTTPRequest")). + Return(stubResponse) + + // Call the function + body, err := FetchData("https://api.example.com/missing") + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Should return nil for non-200 status + if body != nil { + t.Errorf("expected nil body for 404, got %q", body) + } + + pdk.PDKMock.AssertExpectations(t) +} + +// ProcessMemoryData demonstrates working with Memory type. +func ProcessMemoryData(mem pdk.Memory) string { + // Memory methods work directly on the stub - no mocking needed! + data := mem.ReadBytes() + return "Processed " + string(data) + " (length: " + formatUint64(mem.Length()) + ")" +} + +func formatUint64(n uint64) string { + return string(rune('0' + n%10)) // Simplified for demo +} + +func TestProcessMemoryData(t *testing.T) { + // Create stub memory with test data - no mocking needed! + mem := pdk.NewStubMemory(0, 5, []byte("hello")) + + result := ProcessMemoryData(mem) + + expected := "Processed hello (length: 5)" + if result != expected { + t.Errorf("expected %q, got %q", expected, result) + } +} + +// StoreAndRetrieve demonstrates Memory Store/Load methods. +func TestMemoryStoreAndLoad(t *testing.T) { + // Create empty memory + mem := pdk.NewStubMemory(100, 0, nil) + + // Store data - works directly, no mock needed + mem.Store([]byte("test data")) + + // Verify the data was stored + if mem.Length() != 9 { + t.Errorf("expected length 9, got %d", mem.Length()) + } + + // Load into buffer + buffer := make([]byte, 9) + mem.Load(buffer) + + if string(buffer) != "test data" { + t.Errorf("expected 'test data', got %q", buffer) + } + + // Free the memory + mem.Free() + + if mem.Length() != 0 { + t.Errorf("expected length 0 after free, got %d", mem.Length()) + } +} + +// HTTPMethodString demonstrates that HTTPMethod.String() works without mocking. +func TestHTTPMethodString(t *testing.T) { + // These work directly - no mocking needed! + tests := []struct { + method pdk.HTTPMethod + expected string + }{ + {pdk.MethodGet, "GET"}, + {pdk.MethodPost, "POST"}, + {pdk.MethodPut, "PUT"}, + {pdk.MethodDelete, "DELETE"}, + } + + for _, tc := range tests { + result := tc.method.String() + if result != tc.expected { + t.Errorf("expected %q for method %d, got %q", tc.expected, tc.method, result) + } + } +} + +// PostJSON demonstrates a more complex HTTP request with body. +func PostJSON(url string, data []byte) (int, error) { + req := pdk.NewHTTPRequest(pdk.MethodPost, url) + req.SetHeader("Content-Type", "application/json") + req.SetBody(data) // Works directly on stub + + resp := req.Send() // This is mocked + return int(resp.Status()), nil +} + +func TestPostJSON(t *testing.T) { + pdk.ResetMock() + + stubResponse := pdk.NewStubHTTPResponse(201, nil, nil) + + pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, "https://api.example.com/items"). + Return(&pdk.HTTPRequest{}) + pdk.PDKMock.On("Send", mock.AnythingOfType("*pdk.HTTPRequest")). + Return(stubResponse) + + status, err := PostJSON("https://api.example.com/items", []byte(`{"name":"test"}`)) + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if status != 201 { + t.Errorf("expected status 201, got %d", status) + } + + pdk.PDKMock.AssertExpectations(t) +} diff --git a/plugins/pdk/go/pdk/pdk.go b/plugins/pdk/go/pdk/pdk.go new file mode 100644 index 000000000..35394d700 --- /dev/null +++ b/plugins/pdk/go/pdk/pdk.go @@ -0,0 +1,204 @@ +// Code generated by ndpgen. DO NOT EDIT. +// +// This file contains wrapper functions for the extism/go-pdk package. +// For WASM builds, it provides type aliases and function wrappers that delegate +// to the real extism/go-pdk package with zero overhead. +// +//go:build wasip1 + +package pdk + +import ( + extism "github.com/extism/go-pdk" +) + +// Type aliases - zero overhead, full compatibility +type HTTPMethod = extism.HTTPMethod +type HTTPRequest = extism.HTTPRequest +type HTTPRequestMeta = extism.HTTPRequestMeta +type HTTPResponse = extism.HTTPResponse +type LogLevel = extism.LogLevel +type Memory = extism.Memory + +// Constants + +const ( + LogDebug = extism.LogDebug + LogError = extism.LogError + LogInfo = extism.LogInfo + LogTrace = extism.LogTrace + LogWarn = extism.LogWarn +) + +const ( + MethodConnect = extism.MethodConnect + MethodDelete = extism.MethodDelete + MethodGet = extism.MethodGet + MethodHead = extism.MethodHead + MethodOptions = extism.MethodOptions + MethodPatch = extism.MethodPatch + MethodPost = extism.MethodPost + MethodPut = extism.MethodPut + MethodTrace = extism.MethodTrace +) + +// Functions +func Allocate(length int) Memory { + return extism.Allocate(length) +} +func AllocateBytes(data []byte) Memory { + return extism.AllocateBytes(data) +} + +// AllocateJSON AllocateJSON allocates and saves the type `any` into Memory on the host. +func AllocateJSON(v any) (Memory, error) { + return extism.AllocateJSON(v) +} + +// AllocateString AllocateString allocates and saves the UTF-8 string `data` into Memory on the host. +func AllocateString(data string) Memory { + return extism.AllocateString(data) +} + +// FindMemory FindMemory finds the host memory block at the given `offset`. +func FindMemory(offset uint64) Memory { + return extism.FindMemory(offset) +} + +// GetConfig GetConfig returns the config string associated with `key` (if any). +func GetConfig(key string) (string, bool) { + return extism.GetConfig(key) +} + +// GetVar GetVar returns the byte slice (if any) associated with `key`. +func GetVar(key string) []byte { + return extism.GetVar(key) +} + +// GetVarInt GetVarInt returns the int associated with `key` (or 0 if none). +func GetVarInt(key string) int { + return extism.GetVarInt(key) +} + +// Input Input returns a slice of bytes from the host. +func Input() []byte { + return extism.Input() +} + +// InputJSON InputJSON returns unmartialed JSON data from the host "input". +func InputJSON(v any) error { + return extism.InputJSON(v) +} + +// InputString InputString returns the input data from the host as a UTF-8 string. +func InputString() string { + return extism.InputString() +} + +// JSONFrom JSONFrom unmarshals a `Memory` block located at `offset` from the host into the provided data `v`. +func JSONFrom(offset uint64, v any) error { + return extism.JSONFrom(offset, v) +} + +// Log Log logs the provided UTF-8 string `s` on the host using the provided log `level`. +func Log(level LogLevel, s string) { + extism.Log(level, s) +} + +// LogMemory LogMemory logs the `memory` block on the host using the provided log `level`. +func LogMemory(level LogLevel, m Memory) { + extism.LogMemory(level, m) +} + +// NewHTTPRequest NewHTTPRequest returns a new `HTTPRequest`. +func NewHTTPRequest(method HTTPMethod, url string) *HTTPRequest { + return extism.NewHTTPRequest(method, url) +} +func NewMemory(offset uint64, length uint64) Memory { + return extism.NewMemory(offset, length) +} + +// Output Output sends the `data` slice of bytes to the host output. +func Output(data []byte) { + extism.Output(data) +} + +// OutputJSON OutputJSON marshals the provided data `v` as output to the host. +func OutputJSON(v any) error { + return extism.OutputJSON(v) +} + +// OutputMemory OutputMemory sends the `mem` Memory to the host output. +func OutputMemory(mem Memory) { + extism.OutputMemory(mem) +} + +// OutputString OutputString sends the UTF-8 string `s` to the host output. +func OutputString(s string) { + extism.OutputString(s) +} + +// ParamBytes ParamBytes returns bytes from Extism host memory given an offset. +func ParamBytes(offset uint64) []byte { + return extism.ParamBytes(offset) +} + +// ParamString ParamString returns UTF-8 string data from Extism host memory given an offset. +func ParamString(offset uint64) string { + return extism.ParamString(offset) +} + +// ParamU32 ParamU32 returns a uint32 from Extism host memory given an offset. +func ParamU32(offset uint64) uint32 { + return extism.ParamU32(offset) +} + +// ParamU64 ParamU64 returns a uint64 from Extism host memory given an offset. +func ParamU64(offset uint64) uint64 { + return extism.ParamU64(offset) +} + +// RemoveVar RemoveVar removes (and frees) the host variable associated with `key`. +func RemoveVar(key string) { + extism.RemoveVar(key) +} + +// ResultBytes ResultBytes allocates bytes and returns the offset in Extism host memory. +func ResultBytes(d []byte) uint64 { + return extism.ResultBytes(d) +} + +// ResultString ResultString allocates a UTF-8 string and returns the offset in Extism host memory. +func ResultString(s string) uint64 { + return extism.ResultString(s) +} + +// ResultU32 ResultU32 allocates a uint32 and returns the offset in Extism host memory. +func ResultU32(d uint32) uint64 { + return extism.ResultU32(d) +} + +// ResultU64 ResultU64 allocates a uint64 and returns the offset in Extism host memory. +func ResultU64(d uint64) uint64 { + return extism.ResultU64(d) +} + +// SetError SetError sets the host error string from `err`. +func SetError(err error) { + extism.SetError(err) +} + +// SetErrorString SetErrorString sets the host error string from `err`. +func SetErrorString(err string) { + extism.SetErrorString(err) +} + +// SetVar SetVar sets the host variable associated with `key` to the `value` byte slice. +func SetVar(key string, value []byte) { + extism.SetVar(key, value) +} + +// SetVarInt SetVarInt sets the host variable associated with `key` to the `value` int. +func SetVarInt(key string, value int) { + extism.SetVarInt(key, value) +} diff --git a/plugins/pdk/go/pdk/pdk_stub.go b/plugins/pdk/go/pdk/pdk_stub.go new file mode 100644 index 000000000..6b725417c --- /dev/null +++ b/plugins/pdk/go/pdk/pdk_stub.go @@ -0,0 +1,210 @@ +// Code generated by ndpgen. DO NOT EDIT. +// +// This file contains mock implementations for non-WASM builds. +// These mocks allow IDE support, compilation, and unit testing on non-WASM platforms. +// Plugin authors can use the exported PDKMock instance to set expectations in tests. +// +//go:build !wasip1 + +package pdk + +import "github.com/stretchr/testify/mock" + +// MockPDK is the mock implementation for testing PDK functions. +type MockPDK struct { + mock.Mock +} + +// PDKMock is the auto-instantiated mock instance for testing. +// Use this to set expectations: pdk.PDKMock.On("GetConfig", "key").Return("value", true) +var PDKMock = &MockPDK{} + +// ResetMock resets the mock to its initial state. +// Call this in test setup/teardown to ensure clean state between tests. +func ResetMock() { + PDKMock = &MockPDK{} +} + +// Functions +func Allocate(length int) Memory { + args := PDKMock.Called(length) + return args.Get(0).(Memory) +} +func AllocateBytes(data []byte) Memory { + args := PDKMock.Called(data) + return args.Get(0).(Memory) +} + +// AllocateJSON AllocateJSON allocates and saves the type `any` into Memory on the host. +func AllocateJSON(v any) (Memory, error) { + args := PDKMock.Called(v) + return args.Get(0).(Memory), args.Error(1) +} + +// AllocateString AllocateString allocates and saves the UTF-8 string `data` into Memory on the host. +func AllocateString(data string) Memory { + args := PDKMock.Called(data) + return args.Get(0).(Memory) +} + +// FindMemory FindMemory finds the host memory block at the given `offset`. +func FindMemory(offset uint64) Memory { + args := PDKMock.Called(offset) + return args.Get(0).(Memory) +} + +// GetConfig GetConfig returns the config string associated with `key` (if any). +func GetConfig(key string) (string, bool) { + args := PDKMock.Called(key) + return args.String(0), args.Bool(1) +} + +// GetVar GetVar returns the byte slice (if any) associated with `key`. +func GetVar(key string) []byte { + args := PDKMock.Called(key) + return args.Get(0).([]byte) +} + +// GetVarInt GetVarInt returns the int associated with `key` (or 0 if none). +func GetVarInt(key string) int { + args := PDKMock.Called(key) + return args.Int(0) +} + +// Input Input returns a slice of bytes from the host. +func Input() []byte { + args := PDKMock.Called() + return args.Get(0).([]byte) +} + +// InputJSON InputJSON returns unmartialed JSON data from the host "input". +func InputJSON(v any) error { + args := PDKMock.Called(v) + return args.Error(0) +} + +// InputString InputString returns the input data from the host as a UTF-8 string. +func InputString() string { + args := PDKMock.Called() + return args.String(0) +} + +// JSONFrom JSONFrom unmarshals a `Memory` block located at `offset` from the host into the provided data `v`. +func JSONFrom(offset uint64, v any) error { + args := PDKMock.Called(offset, v) + return args.Error(0) +} + +// Log Log logs the provided UTF-8 string `s` on the host using the provided log `level`. +func Log(level LogLevel, s string) { + PDKMock.Called(level, s) +} + +// LogMemory LogMemory logs the `memory` block on the host using the provided log `level`. +func LogMemory(level LogLevel, m Memory) { + PDKMock.Called(level, m) +} + +// NewHTTPRequest NewHTTPRequest returns a new `HTTPRequest`. +func NewHTTPRequest(method HTTPMethod, url string) *HTTPRequest { + args := PDKMock.Called(method, url) + return args.Get(0).(*HTTPRequest) +} +func NewMemory(offset uint64, length uint64) Memory { + args := PDKMock.Called(offset, length) + return args.Get(0).(Memory) +} + +// Output Output sends the `data` slice of bytes to the host output. +func Output(data []byte) { + PDKMock.Called(data) +} + +// OutputJSON OutputJSON marshals the provided data `v` as output to the host. +func OutputJSON(v any) error { + args := PDKMock.Called(v) + return args.Error(0) +} + +// OutputMemory OutputMemory sends the `mem` Memory to the host output. +func OutputMemory(mem Memory) { + PDKMock.Called(mem) +} + +// OutputString OutputString sends the UTF-8 string `s` to the host output. +func OutputString(s string) { + PDKMock.Called(s) +} + +// ParamBytes ParamBytes returns bytes from Extism host memory given an offset. +func ParamBytes(offset uint64) []byte { + args := PDKMock.Called(offset) + return args.Get(0).([]byte) +} + +// ParamString ParamString returns UTF-8 string data from Extism host memory given an offset. +func ParamString(offset uint64) string { + args := PDKMock.Called(offset) + return args.String(0) +} + +// ParamU32 ParamU32 returns a uint32 from Extism host memory given an offset. +func ParamU32(offset uint64) uint32 { + args := PDKMock.Called(offset) + return args.Get(0).(uint32) +} + +// ParamU64 ParamU64 returns a uint64 from Extism host memory given an offset. +func ParamU64(offset uint64) uint64 { + args := PDKMock.Called(offset) + return args.Get(0).(uint64) +} + +// RemoveVar RemoveVar removes (and frees) the host variable associated with `key`. +func RemoveVar(key string) { + PDKMock.Called(key) +} + +// ResultBytes ResultBytes allocates bytes and returns the offset in Extism host memory. +func ResultBytes(d []byte) uint64 { + args := PDKMock.Called(d) + return args.Get(0).(uint64) +} + +// ResultString ResultString allocates a UTF-8 string and returns the offset in Extism host memory. +func ResultString(s string) uint64 { + args := PDKMock.Called(s) + return args.Get(0).(uint64) +} + +// ResultU32 ResultU32 allocates a uint32 and returns the offset in Extism host memory. +func ResultU32(d uint32) uint64 { + args := PDKMock.Called(d) + return args.Get(0).(uint64) +} + +// ResultU64 ResultU64 allocates a uint64 and returns the offset in Extism host memory. +func ResultU64(d uint64) uint64 { + args := PDKMock.Called(d) + return args.Get(0).(uint64) +} + +// SetError SetError sets the host error string from `err`. +func SetError(err error) { + PDKMock.Called(err) +} + +// SetErrorString SetErrorString sets the host error string from `err`. +func SetErrorString(err string) { + PDKMock.Called(err) +} + +// SetVar SetVar sets the host variable associated with `key` to the `value` byte slice. +func SetVar(key string, value []byte) { + PDKMock.Called(key, value) +} + +// SetVarInt SetVarInt sets the host variable associated with `key` to the `value` int. +func SetVarInt(key string, value int) { + PDKMock.Called(key, value) +} diff --git a/plugins/pdk/go/pdk/types_stub.go b/plugins/pdk/go/pdk/types_stub.go new file mode 100644 index 000000000..06cbb4f1f --- /dev/null +++ b/plugins/pdk/go/pdk/types_stub.go @@ -0,0 +1,192 @@ +// Code generated by ndpgen. DO NOT EDIT. +// +// This file contains type definitions for non-WASM builds. +// These types match the extism/go-pdk signatures to allow compilation and testing +// on native platforms without importing the WASM-only extism package. +// +//go:build !wasip1 + +package pdk + +// LogLevel represents a logging level. +type LogLevel int + +// Log level constants +const ( + LogTrace LogLevel = iota + LogDebug + LogInfo + LogWarn + LogError +) + +// HTTPMethod represents an HTTP method. +type HTTPMethod int32 + +// HTTP method constants +const ( + MethodGet HTTPMethod = iota + MethodHead + MethodPost + MethodPut + MethodPatch + MethodDelete + MethodConnect + MethodOptions + MethodTrace +) + +// String returns the string representation of the HTTP method. +func (m HTTPMethod) String() string { + switch m { + case MethodGet: + return "GET" + case MethodHead: + return "HEAD" + case MethodPost: + return "POST" + case MethodPut: + return "PUT" + case MethodPatch: + return "PATCH" + case MethodDelete: + return "DELETE" + case MethodConnect: + return "CONNECT" + case MethodOptions: + return "OPTIONS" + case MethodTrace: + return "TRACE" + default: + return "UNKNOWN" + } +} + +// Memory represents memory allocated by (and shared with) the host. +// This is a stub implementation for non-WASM platforms. +type Memory struct { + offset uint64 + length uint64 + data []byte +} + +// Offset returns the offset of the memory block. +func (m Memory) Offset() uint64 { + return m.offset +} + +// Length returns the length of the memory block. +func (m Memory) Length() uint64 { + return m.length +} + +// ReadBytes reads all bytes from the memory block. +func (m Memory) ReadBytes() []byte { + return m.data +} + +// Load reads the memory block into the provided buffer. +func (m *Memory) Load(buffer []byte) { + copy(buffer, m.data) +} + +// Store writes data to the memory block. +func (m *Memory) Store(data []byte) { + m.data = make([]byte, len(data)) + copy(m.data, data) + m.length = uint64(len(data)) +} + +// Free frees the memory block. +func (m *Memory) Free() { + m.data = nil + m.length = 0 +} + +// NewStubMemory creates a new stub Memory for testing. +// This is a helper function not present in the real PDK. +func NewStubMemory(offset, length uint64, data []byte) Memory { + return Memory{ + offset: offset, + length: length, + data: data, + } +} + +// HTTPRequest represents an HTTP request sent by the host. +// This is a stub implementation for non-WASM platforms. +type HTTPRequest struct { + method HTTPMethod + url string + headers map[string]string + body []byte +} + +// SetHeader sets an HTTP header key to value. +func (r *HTTPRequest) SetHeader(key string, value string) *HTTPRequest { + if r.headers == nil { + r.headers = make(map[string]string) + } + r.headers[key] = value + return r +} + +// SetBody sets the HTTP request body. +func (r *HTTPRequest) SetBody(body []byte) *HTTPRequest { + r.body = body + return r +} + +// Send sends the HTTP request and returns the response. +// In the stub implementation, this delegates to the mock. +func (r *HTTPRequest) Send() HTTPResponse { + args := PDKMock.Called(r) + return args.Get(0).(HTTPResponse) +} + +// HTTPRequestMeta represents the metadata associated with an HTTP request. +type HTTPRequestMeta struct { + URL string `json:"url"` + Method string `json:"method"` + Headers map[string]string `json:"headers"` +} + +// HTTPResponse represents an HTTP response returned from the host. +// This is a stub implementation for non-WASM platforms. +type HTTPResponse struct { + status uint16 + headers map[string]string + body []byte + memory Memory +} + +// Status returns the status code from the response. +func (r HTTPResponse) Status() uint16 { + return r.status +} + +// Headers returns the HTTP response headers. +func (r *HTTPResponse) Headers() map[string]string { + return r.headers +} + +// Body returns the body byte slice from the response. +func (r HTTPResponse) Body() []byte { + return r.body +} + +// Memory returns the memory associated with the response. +func (r HTTPResponse) Memory() Memory { + return r.memory +} + +// NewStubHTTPResponse creates a new stub HTTPResponse for testing. +// This is a helper function not present in the real PDK. +func NewStubHTTPResponse(status uint16, headers map[string]string, body []byte) HTTPResponse { + return HTTPResponse{ + status: status, + headers: headers, + body: body, + memory: NewStubMemory(0, uint64(len(body)), body), + } +} diff --git a/plugins/pdk/go/scheduler/scheduler.go b/plugins/pdk/go/scheduler/scheduler.go index 690134a2b..b3dd67bd2 100644 --- a/plugins/pdk/go/scheduler/scheduler.go +++ b/plugins/pdk/go/scheduler/scheduler.go @@ -8,7 +8,7 @@ package scheduler import ( - pdk "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // SchedulerCallbackRequest is the request provided when a scheduled task fires. diff --git a/plugins/pdk/go/scrobbler/scrobbler.go b/plugins/pdk/go/scrobbler/scrobbler.go index 10c8d946a..46b7de58b 100644 --- a/plugins/pdk/go/scrobbler/scrobbler.go +++ b/plugins/pdk/go/scrobbler/scrobbler.go @@ -8,7 +8,7 @@ package scrobbler import ( - pdk "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // ScrobblerError represents an error type for scrobbling operations. diff --git a/plugins/pdk/go/websocket/websocket.go b/plugins/pdk/go/websocket/websocket.go index 6ce291915..0ad2cb549 100644 --- a/plugins/pdk/go/websocket/websocket.go +++ b/plugins/pdk/go/websocket/websocket.go @@ -8,7 +8,7 @@ package websocket import ( - pdk "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // OnBinaryMessageRequest is the request provided when a binary message is received. diff --git a/plugins/testdata/test-artwork/go.mod b/plugins/testdata/test-artwork/go.mod index a79f3b832..553f5e575 100644 --- a/plugins/testdata/test-artwork/go.mod +++ b/plugins/testdata/test-artwork/go.mod @@ -2,9 +2,15 @@ module test-artwork go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-artwork/go.sum b/plugins/testdata/test-artwork/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-artwork/go.sum +++ b/plugins/testdata/test-artwork/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-artwork/main.go b/plugins/testdata/test-artwork/main.go index e7da56cfa..de857dd97 100644 --- a/plugins/testdata/test-artwork/main.go +++ b/plugins/testdata/test-artwork/main.go @@ -5,8 +5,8 @@ package main import ( "strings" - pdk "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // TestInput is the input for nd_test_artwork callback. diff --git a/plugins/testdata/test-cache-plugin/go.mod b/plugins/testdata/test-cache-plugin/go.mod index ee8127428..c41110b70 100644 --- a/plugins/testdata/test-cache-plugin/go.mod +++ b/plugins/testdata/test-cache-plugin/go.mod @@ -2,9 +2,15 @@ module test-cache-plugin go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-cache-plugin/go.sum b/plugins/testdata/test-cache-plugin/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-cache-plugin/go.sum +++ b/plugins/testdata/test-cache-plugin/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-cache-plugin/main.go b/plugins/testdata/test-cache-plugin/main.go index f2538f48d..1d193ac39 100644 --- a/plugins/testdata/test-cache-plugin/main.go +++ b/plugins/testdata/test-cache-plugin/main.go @@ -3,8 +3,8 @@ package main import ( - pdk "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // TestCacheInput is the input for nd_test_cache callback. diff --git a/plugins/testdata/test-kvstore/go.mod b/plugins/testdata/test-kvstore/go.mod index 13fed8a9d..19fa0a47c 100644 --- a/plugins/testdata/test-kvstore/go.mod +++ b/plugins/testdata/test-kvstore/go.mod @@ -2,9 +2,15 @@ module test-kvstore go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-kvstore/go.sum b/plugins/testdata/test-kvstore/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-kvstore/go.sum +++ b/plugins/testdata/test-kvstore/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-kvstore/main.go b/plugins/testdata/test-kvstore/main.go index 8bfe45d4f..9c289df01 100644 --- a/plugins/testdata/test-kvstore/main.go +++ b/plugins/testdata/test-kvstore/main.go @@ -3,8 +3,8 @@ package main import ( - pdk "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // TestKVStoreInput is the input for nd_test_kvstore callback. diff --git a/plugins/testdata/test-library/go.mod b/plugins/testdata/test-library/go.mod index 62e3eba28..d3efcf2fd 100644 --- a/plugins/testdata/test-library/go.mod +++ b/plugins/testdata/test-library/go.mod @@ -2,9 +2,15 @@ module test-library go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-library/go.sum b/plugins/testdata/test-library/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-library/go.sum +++ b/plugins/testdata/test-library/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-library/main.go b/plugins/testdata/test-library/main.go index 084a71450..9c04c5f9b 100644 --- a/plugins/testdata/test-library/main.go +++ b/plugins/testdata/test-library/main.go @@ -8,8 +8,8 @@ import ( "os" "path/filepath" - pdk "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // TestLibraryInput is the input for nd_test_library callback. diff --git a/plugins/testdata/test-metadata-agent/go.mod b/plugins/testdata/test-metadata-agent/go.mod index aace37bc0..02aff736c 100644 --- a/plugins/testdata/test-metadata-agent/go.mod +++ b/plugins/testdata/test-metadata-agent/go.mod @@ -2,9 +2,15 @@ module test-metadata-agent go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-metadata-agent/go.sum b/plugins/testdata/test-metadata-agent/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-metadata-agent/go.sum +++ b/plugins/testdata/test-metadata-agent/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-metadata-agent/main.go b/plugins/testdata/test-metadata-agent/main.go index 7fb1528fe..c3228098f 100644 --- a/plugins/testdata/test-metadata-agent/main.go +++ b/plugins/testdata/test-metadata-agent/main.go @@ -6,8 +6,8 @@ import ( "errors" "strconv" - "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/metadata" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) func init() { diff --git a/plugins/testdata/test-scheduler/go.mod b/plugins/testdata/test-scheduler/go.mod index bf35c55ce..337cc9650 100644 --- a/plugins/testdata/test-scheduler/go.mod +++ b/plugins/testdata/test-scheduler/go.mod @@ -4,6 +4,13 @@ go 1.25 require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 -require github.com/extism/go-pdk v1.1.3 // indirect +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-scheduler/go.sum b/plugins/testdata/test-scheduler/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-scheduler/go.sum +++ b/plugins/testdata/test-scheduler/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-scrobbler/go.mod b/plugins/testdata/test-scrobbler/go.mod index fc8e98f9e..b9e2c3887 100644 --- a/plugins/testdata/test-scrobbler/go.mod +++ b/plugins/testdata/test-scrobbler/go.mod @@ -2,9 +2,15 @@ module test-scrobbler go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-scrobbler/go.sum b/plugins/testdata/test-scrobbler/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-scrobbler/go.sum +++ b/plugins/testdata/test-scrobbler/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-scrobbler/main.go b/plugins/testdata/test-scrobbler/main.go index a40bd6329..defb37316 100644 --- a/plugins/testdata/test-scrobbler/main.go +++ b/plugins/testdata/test-scrobbler/main.go @@ -6,7 +6,7 @@ import ( "fmt" "strconv" - pdk "github.com/extism/go-pdk" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" "github.com/navidrome/navidrome/plugins/pdk/go/scrobbler" ) diff --git a/plugins/testdata/test-subsonicapi-plugin/go.mod b/plugins/testdata/test-subsonicapi-plugin/go.mod index 6158bb779..1fb943418 100644 --- a/plugins/testdata/test-subsonicapi-plugin/go.mod +++ b/plugins/testdata/test-subsonicapi-plugin/go.mod @@ -2,9 +2,15 @@ module test-subsonicapi-plugin go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-subsonicapi-plugin/go.sum b/plugins/testdata/test-subsonicapi-plugin/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-subsonicapi-plugin/go.sum +++ b/plugins/testdata/test-subsonicapi-plugin/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-subsonicapi-plugin/main.go b/plugins/testdata/test-subsonicapi-plugin/main.go index c442c050e..03b912801 100644 --- a/plugins/testdata/test-subsonicapi-plugin/main.go +++ b/plugins/testdata/test-subsonicapi-plugin/main.go @@ -3,8 +3,8 @@ package main import ( - "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) // call_subsonic_api is the exported function that tests the SubsonicAPI host function. diff --git a/plugins/testdata/test-websocket/go.mod b/plugins/testdata/test-websocket/go.mod index ce81f97bc..f786b8658 100644 --- a/plugins/testdata/test-websocket/go.mod +++ b/plugins/testdata/test-websocket/go.mod @@ -2,9 +2,15 @@ module test-websocket go 1.25 +require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + require ( - github.com/extism/go-pdk v1.1.3 - github.com/navidrome/navidrome/plugins/pdk/go v0.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/extism/go-pdk v1.1.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go diff --git a/plugins/testdata/test-websocket/go.sum b/plugins/testdata/test-websocket/go.sum index c15d38292..af880eb51 100644 --- a/plugins/testdata/test-websocket/go.sum +++ b/plugins/testdata/test-websocket/go.sum @@ -1,2 +1,14 @@ +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/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/testdata/test-websocket/main.go b/plugins/testdata/test-websocket/main.go index a59800048..7e2683e4d 100644 --- a/plugins/testdata/test-websocket/main.go +++ b/plugins/testdata/test-websocket/main.go @@ -5,8 +5,8 @@ package main import ( "errors" - pdk "github.com/extism/go-pdk" "github.com/navidrome/navidrome/plugins/pdk/go/host" + "github.com/navidrome/navidrome/plugins/pdk/go/pdk" "github.com/navidrome/navidrome/plugins/pdk/go/websocket" )