Repository: google/go-github
Pull request #3515 (2025-03-13 18:24:06)

refactor!: Update package types to align with webhook event
BREAKING CHANGE: `PackageVersion.Body` and `PackageVersion.Metadata` are both now `json.RawMessage`.

Hello,

Firstly, thank you for maintaining this project! It has made interacting with GitHub significantly easier. I know this is a little backward to raise a PR without even a comment on the original issue so sorry but I had to get some legal approval first. Anyway...

This PR is meant to better align the package types with the webhook event schema.

Fixes: #2033

Breaking changes:
* `PackageVersion.Body` is now `json.RawMessage` - I'm not sure in what scenarios this is a string or object but it can be both.
* `PackageVersion.Metadata` is now `json.json.RawMessage` - webhooks return an array here, rather than a `PackageMetadata`

I've added getters for both of these fields.

References:
Webhooks: https://github.com/octokit/webhooks/tree/main/payload-schemas/api.github.com/package
REST: https://docs.github.com/en/rest/packages/packages?apiVersion=2022-11-28

Please do let me know your comments and if there is anything I haven't thought of!

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/google/go-github/pull/3515?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 91.30%. Comparing base [(`d0976cc`)](https://app.codecov.io/gh/google/go-github/commit/d0976cc1b1715743296c1dba5c8f8ec422a5e8c6?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) to head [(`f9b5bf6`)](https://app.codecov.io/gh/google/go-github/commit/f9b5bf68d6cfb2b1265a8a5616ce03d96a8984f2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).
> Report is 10 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #3515      +/-   ##
==========================================
+ Coverage   91.17%   91.30%   +0.12%     
==========================================
  Files         181      183       +2     
  Lines       15859    16094     +235     
==========================================
+ Hits        14460    14695     +235     
  Misses       1225     1225              
  Partials      174      174              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/google/go-github/pull/3515?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
gmlewis: Oh, sorry, I see your responses now in email.
I'll continue the review.

Patch:
diff --git a/README.md b/README.md
index c7b9dcef..397a7a74 100644
--- a/README.md
+++ b/README.md
@@ -233,7 +233,7 @@ repos, _, err := client.Repositories.List(context.WithValue(ctx, github.SleepUnt
 ```
 
 You can use [gofri/go-github-ratelimit](https://github.com/gofri/go-github-ratelimit) to handle
-secondary rate limit sleep-and-retry for you.
+secondary rate limit sleep-and-retry for you, as well as primary rate limit abuse-prevention and callback triggering.
 
 Learn more about GitHub secondary rate limiting in
 ["About secondary rate limits"](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits).
@@ -347,6 +347,13 @@ for repo := range repos.All() {
 
 For complete usage of `enrichman/gh-iter`, see the full [package docs](https://github.com/enrichman/gh-iter).
 
+#### Middleware ####
+
+You can use [gofri/go-github-pagination](https://github.com/gofri/go-github-pagination) to handle
+pagination for you. It supports both sync and async modes, as well as customizations.  
+By default, the middleware automatically paginates through all pages, aggregates results, and returns them as an array.  
+See `example/ratelimit/main.go` for usage.
+
 ### Webhooks ###
 
 `go-github` provides structs for almost all [GitHub webhook events][] as well as functions to validate them and unmarshal JSON payloads from `http.Request` structs.
diff --git a/example/go.mod b/example/go.mod
index 54289ff1..34b1f2e4 100644
--- a/example/go.mod
+++ b/example/go.mod
@@ -5,7 +5,8 @@ go 1.23.0
 require (
 	github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371
 	github.com/bradleyfalzon/ghinstallation/v2 v2.0.4
-	github.com/gofri/go-github-ratelimit v1.0.3
+	github.com/gofri/go-github-pagination v1.0.0
+	github.com/gofri/go-github-ratelimit/v2 v2.0.2
 	github.com/google/go-github/v69 v69.2.0
 	github.com/sigstore/sigstore-go v0.6.1
 	golang.org/x/crypto v0.35.0
diff --git a/example/go.sum b/example/go.sum
index 70f84d9e..7a39a6ed 100644
--- a/example/go.sum
+++ b/example/go.sum
@@ -134,8 +134,10 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
 github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
 github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
 github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofri/go-github-ratelimit v1.0.3 h1:Ocs2jaYokZDzgvqaajX+g04dqFyVqL0JQzoO7d2wmlk=
-github.com/gofri/go-github-ratelimit v1.0.3/go.mod h1:OnCi5gV+hAG/LMR7llGhU7yHt44se9sYgKPnafoL7RY=
+github.com/gofri/go-github-pagination v1.0.0 h1:nnCi+1xT5ybqY/plctISgiQPWZOtfSciVQlbx/hM/Yw=
+github.com/gofri/go-github-pagination v1.0.0/go.mod h1:Qij55Fb4fNPjam3SB+8cLnqp4pgR8RGMyIspYXcyHX0=
+github.com/gofri/go-github-ratelimit/v2 v2.0.2 h1:gS8wAS1jTmlWGdTjAM7KIpsLjwY1S0S/gKK5hthfSXM=
+github.com/gofri/go-github-ratelimit/v2 v2.0.2/go.mod h1:YBQt4gTbdcbMjJFT05YFEaECwH78P5b0IwrnbLiHGdE=
 github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
 github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
diff --git a/example/ratelimit/main.go b/example/ratelimit/main.go
index 32596bc3..70c7424c 100644
--- a/example/ratelimit/main.go
+++ b/example/ratelimit/main.go
@@ -3,16 +3,20 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// The ratelimit command demonstrates using the github_ratelimit.SecondaryRateLimitWaiter.
+// The ratelimit command demonstrates using the github_ratelimit as well as github_pagination.
 // By using the waiter, the client automatically sleeps and retry requests
 // when it hits secondary rate limits.
+// It also prevents the client from abusing the API in case of a primary rate limit.
 package main
 
 import (
 	"context"
 	"fmt"
 
-	"github.com/gofri/go-github-ratelimit/github_ratelimit"
+	"github.com/gofri/go-github-pagination/githubpagination"
+	"github.com/gofri/go-github-ratelimit/v2/github_ratelimit"
+	"github.com/gofri/go-github-ratelimit/v2/github_ratelimit/github_primary_ratelimit"
+	"github.com/gofri/go-github-ratelimit/v2/github_ratelimit/github_secondary_ratelimit"
 	"github.com/google/go-github/v69/github"
 )
 
@@ -21,22 +25,28 @@ func main() {
 	fmt.Print("Enter GitHub username: ")
 	fmt.Scanf("%s", &username)
 
-	rateLimiter, err := github_ratelimit.NewRateLimitWaiterClient(nil)
-	if err != nil {
-		fmt.Printf("Error: %v\n", err)
-		return
-	}
+	rateLimiter := github_ratelimit.New(nil,
+		github_primary_ratelimit.WithLimitDetectedCallback(func(ctx *github_primary_ratelimit.CallbackContext) {
+			fmt.Printf("Primary rate limit detected: category %s, reset time: %v\n", ctx.Category, ctx.ResetTime)
+		}),
+		github_secondary_ratelimit.WithLimitDetectedCallback(func(ctx *github_secondary_ratelimit.CallbackContext) {
+			fmt.Printf("Secondary rate limit detected: reset time: %v, total sleep time: %v\n", ctx.ResetTime, ctx.TotalSleepTime)
+		}),
+	)
 
-	client := github.NewClient(rateLimiter)
+	paginator := githubpagination.NewClient(rateLimiter,
+		githubpagination.WithPerPage(100), // default to 100 results per page
+	)
+	client := github.NewClient(paginator)
 
 	// arbitrary usage of the client
-	organizations, _, err := client.Organizations.List(context.Background(), username, nil)
+	repos, _, err := client.Repositories.ListByUser(context.Background(), username, nil)
 	if err != nil {
 		fmt.Printf("Error: %v\n", err)
 		return
 	}
 
-	for i, organization := range organizations {
-		fmt.Printf("%v. %v\n", i+1, organization.GetLogin())
+	for i, repo := range repos {
+		fmt.Printf("%v. %v\n", i+1, repo.GetName())
 	}
 }
diff --git a/github/gen-accessors.go b/github/gen-accessors.go
index 261b945e..66450fb7 100644
--- a/github/gen-accessors.go
+++ b/github/gen-accessors.go
@@ -45,6 +45,8 @@ var (
 		"ErrorResponse.GetResponse":       true,
 		"RateLimitError.GetResponse":      true,
 		"AbuseRateLimitError.GetResponse": true,
+		"PackageVersion.GetBody":          true,
+		"PackageVersion.GetMetadata":      true,
 	}
 	// skipStructs lists structs to skip.
 	skipStructs = map[string]bool{
diff --git a/github/github-accessors.go b/github/github-accessors.go
index bb2ee2af..40d935f1 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -15814,6 +15814,22 @@ func (p *Package) GetCreatedAt() Timestamp {
 	return *p.CreatedAt
 }
 
+// GetDescription returns the Description field if it's non-nil, zero value otherwise.
+func (p *Package) GetDescription() string {
+	if p == nil || p.Description == nil {
+		return ""
+	}
+	return *p.Description
+}
+
+// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise.
+func (p *Package) GetEcosystem() string {
+	if p == nil || p.Ecosystem == nil {
+		return ""
+	}
+	return *p.Ecosystem
+}
+
 // GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
 func (p *Package) GetHTMLURL() string {
 	if p == nil || p.HTMLURL == nil {
@@ -15838,6 +15854,14 @@ func (p *Package) GetName() string {
 	return *p.Name
 }
 
+// GetNamespace returns the Namespace field if it's non-nil, zero value otherwise.
+func (p *Package) GetNamespace() string {
+	if p == nil || p.Namespace == nil {
+		return ""
+	}
+	return *p.Namespace
+}
+
 // GetOwner returns the Owner field.
 func (p *Package) GetOwner() *User {
 	if p == nil {
@@ -15870,14 +15894,6 @@ func (p *Package) GetRegistry() *PackageRegistry {
 	return p.Registry
 }
 
-// GetRepository returns the Repository field.
-func (p *Package) GetRepository() *Repository {
-	if p == nil {
-		return nil
-	}
-	return p.Repository
-}
-
 // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
 func (p *Package) GetUpdatedAt() Timestamp {
 	if p == nil || p.UpdatedAt == nil {
@@ -15958,6 +15974,46 @@ func (p *PackageEvent) GetSender() *User {
 	return p.Sender
 }
 
+// GetLabels returns the Labels map if it's non-nil, an empty map otherwise.
+func (p *PackageEventContainerMetadata) GetLabels() map[string]any {
+	if p == nil || p.Labels == nil {
+		return map[string]any{}
+	}
+	return p.Labels
+}
+
+// GetManifest returns the Manifest map if it's non-nil, an empty map otherwise.
+func (p *PackageEventContainerMetadata) GetManifest() map[string]any {
+	if p == nil || p.Manifest == nil {
+		return map[string]any{}
+	}
+	return p.Manifest
+}
+
+// GetTag returns the Tag field.
+func (p *PackageEventContainerMetadata) GetTag() *PackageEventContainerMetadataTag {
+	if p == nil {
+		return nil
+	}
+	return p.Tag
+}
+
+// GetDigest returns the Digest field if it's non-nil, zero value otherwise.
+func (p *PackageEventContainerMetadataTag) GetDigest() string {
+	if p == nil || p.Digest == nil {
+		return ""
+	}
+	return *p.Digest
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (p *PackageEventContainerMetadataTag) GetName() string {
+	if p == nil || p.Name == nil {
+		return ""
+	}
+	return *p.Name
+}
+
 // GetAuthor returns the Author field.
 func (p *PackageFile) GetAuthor() *User {
 	if p == nil {
@@ -16094,6 +16150,262 @@ func (p *PackageMetadata) GetPackageType() string {
 	return *p.PackageType
 }
 
+// GetAuthor returns the Author map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetAuthor() map[string]string {
+	if p == nil || p.Author == nil {
+		return map[string]string{}
+	}
+	return p.Author
+}
+
+// GetBin returns the Bin map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetBin() map[string]any {
+	if p == nil || p.Bin == nil {
+		return map[string]any{}
+	}
+	return p.Bin
+}
+
+// GetBugs returns the Bugs map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetBugs() map[string]string {
+	if p == nil || p.Bugs == nil {
+		return map[string]string{}
+	}
+	return p.Bugs
+}
+
+// GetCommitOID returns the CommitOID field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetCommitOID() string {
+	if p == nil || p.CommitOID == nil {
+		return ""
+	}
+	return *p.CommitOID
+}
+
+// GetDeletedByID returns the DeletedByID field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetDeletedByID() int64 {
+	if p == nil || p.DeletedByID == nil {
+		return 0
+	}
+	return *p.DeletedByID
+}
+
+// GetDependencies returns the Dependencies map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetDependencies() map[string]string {
+	if p == nil || p.Dependencies == nil {
+		return map[string]string{}
+	}
+	return p.Dependencies
+}
+
+// GetDescription returns the Description field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetDescription() string {
+	if p == nil || p.Description == nil {
+		return ""
+	}
+	return *p.Description
+}
+
+// GetDevDependencies returns the DevDependencies map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetDevDependencies() map[string]string {
+	if p == nil || p.DevDependencies == nil {
+		return map[string]string{}
+	}
+	return p.DevDependencies
+}
+
+// GetDirectories returns the Directories map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetDirectories() map[string]string {
+	if p == nil || p.Directories == nil {
+		return map[string]string{}
+	}
+	return p.Directories
+}
+
+// GetDist returns the Dist map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetDist() map[string]string {
+	if p == nil || p.Dist == nil {
+		return map[string]string{}
+	}
+	return p.Dist
+}
+
+// GetEngines returns the Engines map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetEngines() map[string]string {
+	if p == nil || p.Engines == nil {
+		return map[string]string{}
+	}
+	return p.Engines
+}
+
+// GetGitHead returns the GitHead field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetGitHead() string {
+	if p == nil || p.GitHead == nil {
+		return ""
+	}
+	return *p.GitHead
+}
+
+// GetHasShrinkwrap returns the HasShrinkwrap field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetHasShrinkwrap() bool {
+	if p == nil || p.HasShrinkwrap == nil {
+		return false
+	}
+	return *p.HasShrinkwrap
+}
+
+// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetHomepage() string {
+	if p == nil || p.Homepage == nil {
+		return ""
+	}
+	return *p.Homepage
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetID() string {
+	if p == nil || p.ID == nil {
+		return ""
+	}
+	return *p.ID
+}
+
+// GetInstallationCommand returns the InstallationCommand field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetInstallationCommand() string {
+	if p == nil || p.InstallationCommand == nil {
+		return ""
+	}
+	return *p.InstallationCommand
+}
+
+// GetLicense returns the License field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetLicense() string {
+	if p == nil || p.License == nil {
+		return ""
+	}
+	return *p.License
+}
+
+// GetMain returns the Main field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetMain() string {
+	if p == nil || p.Main == nil {
+		return ""
+	}
+	return *p.Main
+}
+
+// GetMan returns the Man map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetMan() map[string]any {
+	if p == nil || p.Man == nil {
+		return map[string]any{}
+	}
+	return p.Man
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetName() string {
+	if p == nil || p.Name == nil {
+		return ""
+	}
+	return *p.Name
+}
+
+// GetNodeVersion returns the NodeVersion field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetNodeVersion() string {
+	if p == nil || p.NodeVersion == nil {
+		return ""
+	}
+	return *p.NodeVersion
+}
+
+// GetNPMUser returns the NPMUser field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetNPMUser() string {
+	if p == nil || p.NPMUser == nil {
+		return ""
+	}
+	return *p.NPMUser
+}
+
+// GetNPMVersion returns the NPMVersion field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetNPMVersion() string {
+	if p == nil || p.NPMVersion == nil {
+		return ""
+	}
+	return *p.NPMVersion
+}
+
+// GetOptionalDependencies returns the OptionalDependencies map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetOptionalDependencies() map[string]string {
+	if p == nil || p.OptionalDependencies == nil {
+		return map[string]string{}
+	}
+	return p.OptionalDependencies
+}
+
+// GetPeerDependencies returns the PeerDependencies map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetPeerDependencies() map[string]string {
+	if p == nil || p.PeerDependencies == nil {
+		return map[string]string{}
+	}
+	return p.PeerDependencies
+}
+
+// GetPublishedViaActions returns the PublishedViaActions field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetPublishedViaActions() bool {
+	if p == nil || p.PublishedViaActions == nil {
+		return false
+	}
+	return *p.PublishedViaActions
+}
+
+// GetReadme returns the Readme field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetReadme() string {
+	if p == nil || p.Readme == nil {
+		return ""
+	}
+	return *p.Readme
+}
+
+// GetReleaseID returns the ReleaseID field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetReleaseID() int64 {
+	if p == nil || p.ReleaseID == nil {
+		return 0
+	}
+	return *p.ReleaseID
+}
+
+// GetRepository returns the Repository map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetRepository() map[string]string {
+	if p == nil || p.Repository == nil {
+		return map[string]string{}
+	}
+	return p.Repository
+}
+
+// GetScripts returns the Scripts map if it's non-nil, an empty map otherwise.
+func (p *PackageNPMMetadata) GetScripts() map[string]any {
+	if p == nil || p.Scripts == nil {
+		return map[string]any{}
+	}
+	return p.Scripts
+}
+
+// GetVersion returns the Version field if it's non-nil, zero value otherwise.
+func (p *PackageNPMMetadata) GetVersion() string {
+	if p == nil || p.Version == nil {
+		return ""
+	}
+	return *p.Version
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (p *PackageNugetMetadata) GetName() string {
+	if p == nil || p.Name == nil {
+		return ""
+	}
+	return *p.Name
+}
+
 // GetAboutURL returns the AboutURL field if it's non-nil, zero value otherwise.
 func (p *PackageRegistry) GetAboutURL() string {
 	if p == nil || p.AboutURL == nil {
@@ -16230,14 +16542,6 @@ func (p *PackageVersion) GetAuthor() *User {
 	return p.Author
 }
 
-// GetBody returns the Body field if it's non-nil, zero value otherwise.
-func (p *PackageVersion) GetBody() string {
-	if p == nil || p.Body == nil {
-		return ""
-	}
-	return *p.Body
-}
-
 // GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise.
 func (p *PackageVersion) GetBodyHTML() string {
 	if p == nil || p.BodyHTML == nil {
@@ -16246,6 +16550,14 @@ func (p *PackageVersion) GetBodyHTML() string {
 	return *p.BodyHTML
 }
 
+// GetContainerMetadata returns the ContainerMetadata field.
+func (p *PackageVersion) GetContainerMetadata() *PackageEventContainerMetadata {
+	if p == nil {
+		return nil
+	}
+	return p.ContainerMetadata
+}
+
 // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
 func (p *PackageVersion) GetCreatedAt() Timestamp {
 	if p == nil || p.CreatedAt == nil {
@@ -16254,6 +16566,22 @@ func (p *PackageVersion) GetCreatedAt() Timestamp {
 	return *p.CreatedAt
 }
 
+// GetDeletedAt returns the DeletedAt field if it's non-nil, zero value otherwise.
+func (p *PackageVersion) GetDeletedAt() Timestamp {
+	if p == nil || p.DeletedAt == nil {
+		return Timestamp{}
+	}
+	return *p.DeletedAt
+}
+
+// GetDescription returns the Description field if it's non-nil, zero value otherwise.
+func (p *PackageVersion) GetDescription() string {
+	if p == nil || p.Description == nil {
+		return ""
+	}
+	return *p.Description
+}
+
 // GetDraft returns the Draft field if it's non-nil, zero value otherwise.
 func (p *PackageVersion) GetDraft() bool {
 	if p == nil || p.Draft == nil {
@@ -16286,6 +16614,14 @@ func (p *PackageVersion) GetInstallationCommand() string {
 	return *p.InstallationCommand
 }
 
+// GetLicense returns the License field if it's non-nil, zero value otherwise.
+func (p *PackageVersion) GetLicense() string {
+	if p == nil || p.License == nil {
+		return ""
+	}
+	return *p.License
+}
+
 // GetManifest returns the Manifest field if it's non-nil, zero value otherwise.
 func (p *PackageVersion) GetManifest() string {
 	if p == nil || p.Manifest == nil {
@@ -16294,14 +16630,6 @@ func (p *PackageVersion) GetManifest() string {
 	return *p.Manifest
 }
 
-// GetMetadata returns the Metadata field.
-func (p *PackageVersion) GetMetadata() *PackageMetadata {
-	if p == nil {
-		return nil
-	}
-	return p.Metadata
-}
-
 // GetName returns the Name field if it's non-nil, zero value otherwise.
 func (p *PackageVersion) GetName() string {
 	if p == nil || p.Name == nil {
@@ -16310,6 +16638,14 @@ func (p *PackageVersion) GetName() string {
 	return *p.Name
 }
 
+// GetNPMMetadata returns the NPMMetadata field.
+func (p *PackageVersion) GetNPMMetadata() *PackageNPMMetadata {
+	if p == nil {
+		return nil
+	}
+	return p.NPMMetadata
+}
+
 // GetPackageHTMLURL returns the PackageHTMLURL field if it's non-nil, zero value otherwise.
 func (p *PackageVersion) GetPackageHTMLURL() string {
 	if p == nil || p.PackageHTMLURL == nil {
@@ -16318,6 +16654,14 @@ func (p *PackageVersion) GetPackageHTMLURL() string {
 	return *p.PackageHTMLURL
 }
 
+// GetPackageURL returns the PackageURL field if it's non-nil, zero value otherwise.
+func (p *PackageVersion) GetPackageURL() string {
+	if p == nil || p.PackageURL == nil {
+		return ""
+	}
+	return *p.PackageURL
+}
+
 // GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise.
 func (p *PackageVersion) GetPrerelease() bool {
 	if p == nil || p.Prerelease == nil {
@@ -16334,6 +16678,22 @@ func (p *PackageVersion) GetRelease() *PackageRelease {
 	return p.Release
 }
 
+// GetRubyMetadata returns the RubyMetadata map if it's non-nil, an empty map otherwise.
+func (p *PackageVersion) GetRubyMetadata() map[string]any {
+	if p == nil || p.RubyMetadata == nil {
+		return map[string]any{}
+	}
+	return p.RubyMetadata
+}
+
+// GetSourceURL returns the SourceURL field if it's non-nil, zero value otherwise.
+func (p *PackageVersion) GetSourceURL() string {
+	if p == nil || p.SourceURL == nil {
+		return ""
+	}
+	return *p.SourceURL
+}
+
 // GetSummary returns the Summary field if it's non-nil, zero value otherwise.
 func (p *PackageVersion) GetSummary() string {
 	if p == nil || p.Summary == nil {
@@ -16390,6 +16750,78 @@ func (p *PackageVersion) GetVersion() string {
 	return *p.Version
 }
 
+// GetInfo returns the Info field.
+func (p *PackageVersionBody) GetInfo() *PackageVersionBodyInfo {
+	if p == nil {
+		return nil
+	}
+	return p.Info
+}
+
+// GetRepo returns the Repo field.
+func (p *PackageVersionBody) GetRepo() *Repository {
+	if p == nil {
+		return nil
+	}
+	return p.Repo
+}
+
+// GetCollection returns the Collection field if it's non-nil, zero value otherwise.
+func (p *PackageVersionBodyInfo) GetCollection() bool {
+	if p == nil || p.Collection == nil {
+		return false
+	}
+	return *p.Collection
+}
+
+// GetMode returns the Mode field if it's non-nil, zero value otherwise.
+func (p *PackageVersionBodyInfo) GetMode() int64 {
+	if p == nil || p.Mode == nil {
+		return 0
+	}
+	return *p.Mode
+}
+
+// GetName returns the Name field if it's non-nil, zero value otherwise.
+func (p *PackageVersionBodyInfo) GetName() string {
+	if p == nil || p.Name == nil {
+		return ""
+	}
+	return *p.Name
+}
+
+// GetOID returns the OID field if it's non-nil, zero value otherwise.
+func (p *PackageVersionBodyInfo) GetOID() string {
+	if p == nil || p.OID == nil {
+		return ""
+	}
+	return *p.OID
+}
+
+// GetPath returns the Path field if it's non-nil, zero value otherwise.
+func (p *PackageVersionBodyInfo) GetPath() string {
+	if p == nil || p.Path == nil {
+		return ""
+	}
+	return *p.Path
+}
+
+// GetSize returns the Size field if it's non-nil, zero value otherwise.
+func (p *PackageVersionBodyInfo) GetSize() int64 {
+	if p == nil || p.Size == nil {
+		return 0
+	}
+	return *p.Size
+}
+
+// GetType returns the Type field if it's non-nil, zero value otherwise.
+func (p *PackageVersionBodyInfo) GetType() string {
+	if p == nil || p.Type == nil {
+		return ""
+	}
+	return *p.Type
+}
+
 // GetAction returns the Action field if it's non-nil, zero value otherwise.
 func (p *Page) GetAction() string {
 	if p == nil || p.Action == nil {
diff --git a/github/packages.go b/github/packages.go
index ef7df074..e7c32796 100644
--- a/github/packages.go
+++ b/github/packages.go
@@ -5,21 +5,31 @@
 
 package github
 
+import (
+	"encoding/json"
+)
+
 // Package represents a GitHub package.
 type Package struct {
-	ID             *int64           `json:"id,omitempty"`
-	Name           *string          `json:"name,omitempty"`
-	PackageType    *string          `json:"package_type,omitempty"`
-	HTMLURL        *string          `json:"html_url,omitempty"`
-	CreatedAt      *Timestamp       `json:"created_at,omitempty"`
-	UpdatedAt      *Timestamp       `json:"updated_at,omitempty"`
-	Owner          *User            `json:"owner,omitempty"`
+	ID          *int64     `json:"id,omitempty"`
+	Name        *string    `json:"name,omitempty"`
+	PackageType *string    `json:"package_type,omitempty"` // One of "npm", "maven", "rubygems", "docker", "nuget", "container". For webhook events "container" is "CONTAINER"
+	HTMLURL     *string    `json:"html_url,omitempty"`
+	Visibility  *string    `json:"visibility,omitempty"`
+	Owner       *User      `json:"owner,omitempty"`
+	CreatedAt   *Timestamp `json:"created_at,omitempty"`
+	UpdatedAt   *Timestamp `json:"updated_at,omitempty"`
+
+	// The following are only populated for webhook events
+	Namespace      *string          `json:"namespace,omitempty"`
+	Description    *string          `json:"description,omitempty"`
+	Ecosystem      *string          `json:"ecosystem,omitempty"`
 	PackageVersion *PackageVersion  `json:"package_version,omitempty"`
 	Registry       *PackageRegistry `json:"registry,omitempty"`
-	URL            *string          `json:"url,omitempty"`
-	VersionCount   *int64           `json:"version_count,omitempty"`
-	Visibility     *string          `json:"visibility,omitempty"`
-	Repository     *Repository      `json:"repository,omitempty"`
+
+	// The following are NOT populated for webhook events
+	URL          *string `json:"url,omitempty"`
+	VersionCount *int64  `json:"version_count,omitempty"`
 }
 
 func (p Package) String() string {
@@ -28,28 +38,90 @@ func (p Package) String() string {
 
 // PackageVersion represents a GitHub package version.
 type PackageVersion struct {
-	ID                  *int64           `json:"id,omitempty"`
-	Version             *string          `json:"version,omitempty"`
-	Summary             *string          `json:"summary,omitempty"`
-	Body                *string          `json:"body,omitempty"`
-	BodyHTML            *string          `json:"body_html,omitempty"`
-	Release             *PackageRelease  `json:"release,omitempty"`
-	Manifest            *string          `json:"manifest,omitempty"`
-	HTMLURL             *string          `json:"html_url,omitempty"`
-	TagName             *string          `json:"tag_name,omitempty"`
-	TargetCommitish     *string          `json:"target_commitish,omitempty"`
-	TargetOID           *string          `json:"target_oid,omitempty"`
-	Draft               *bool            `json:"draft,omitempty"`
-	Prerelease          *bool            `json:"prerelease,omitempty"`
-	CreatedAt           *Timestamp       `json:"created_at,omitempty"`
-	UpdatedAt           *Timestamp       `json:"updated_at,omitempty"`
-	PackageFiles        []*PackageFile   `json:"package_files,omitempty"`
-	Author              *User            `json:"author,omitempty"`
-	InstallationCommand *string          `json:"installation_command,omitempty"`
-	Metadata            *PackageMetadata `json:"metadata,omitempty"`
-	PackageHTMLURL      *string          `json:"package_html_url,omitempty"`
-	Name                *string          `json:"name,omitempty"`
-	URL                 *string          `json:"url,omitempty"`
+	ID             *int64          `json:"id,omitempty"`
+	Name           *string         `json:"name,omitempty"`
+	URL            *string         `json:"url,omitempty"`
+	PackageHTMLURL *string         `json:"package_html_url,omitempty"`
+	License        *string         `json:"license,omitempty"`
+	Description    *string         `json:"description,omitempty"`
+	CreatedAt      *Timestamp      `json:"created_at,omitempty"`
+	UpdatedAt      *Timestamp      `json:"updated_at,omitempty"`
+	Metadata       json.RawMessage `json:"metadata,omitempty"` // For webhook events this will be []interface, else it will be of type PackageMetadata
+
+	// The following are only populated for webhook events
+	Version             *string                        `json:"version,omitempty"`
+	Summary             *string                        `json:"summary,omitempty"`
+	Body                json.RawMessage                `json:"body,omitempty"` // Can either be a string or of type PackageVersionBody
+	BodyHTML            *string                        `json:"body_html,omitempty"`
+	Release             *PackageRelease                `json:"release,omitempty"`
+	Manifest            *string                        `json:"manifest,omitempty"`
+	HTMLURL             *string                        `json:"html_url,omitempty"`
+	TagName             *string                        `json:"tag_name,omitempty"`
+	TargetCommitish     *string                        `json:"target_commitish,omitempty"`
+	TargetOID           *string                        `json:"target_oid,omitempty"`
+	Draft               *bool                          `json:"draft,omitempty"`
+	Prerelease          *bool                          `json:"prerelease,omitempty"`
+	ContainerMetadata   *PackageEventContainerMetadata `json:"container_metadata,omitempty"`
+	DockerMetadata      []interface{}                  `json:"docker_metadata,omitempty"`
+	NPMMetadata         *PackageNPMMetadata            `json:"npm_metadata,omitempty"`
+	NugetMetadata       []*PackageNugetMetadata        `json:"nuget_metadata,omitempty"`
+	RubyMetadata        map[string]any                 `json:"ruby_metadata,omitempty"`
+	PackageFiles        []*PackageFile                 `json:"package_files,omitempty"`
+	PackageURL          *string                        `json:"package_url,omitempty"`
+	Author              *User                          `json:"author,omitempty"`
+	SourceURL           *string                        `json:"source_url,omitempty"`
+	InstallationCommand *string                        `json:"installation_command,omitempty"`
+
+	// The following are NOT populated for webhook events
+	DeletedAt *Timestamp `json:"deleted_at,omitempty"`
+}
+
+// GetBody returns the body field as a string if it's valid.
+func (pv *PackageVersion) GetBody() (body string, ok bool) {
+	if pv == nil || pv.Body == nil {
+		return "", false
+	}
+
+	if err := json.Unmarshal(pv.Body, &body); err != nil {
+		return "", false
+	}
+
+	return body, true
+}
+
+// GetBodyAsPackageVersionBody returns the body field as a PackageVersionBody if it's valid.
+func (pv *PackageVersion) GetBodyAsPackageVersionBody() (body *PackageVersionBody, ok bool) {
+	if pv == nil || pv.Body == nil {
+		return nil, false
+	}
+
+	if err := json.Unmarshal(pv.Body, &body); err != nil {
+		return nil, false
+	}
+
+	return body, true
+}
+
+// GetMetadata returns the metadata field as PackageMetadata if it's valid.
+func (pv *PackageVersion) GetMetadata() (metadata *PackageMetadata, ok bool) {
+	if pv == nil || pv.Metadata == nil {
+		return nil, false
+	}
+
+	if err := json.Unmarshal(pv.Metadata, &metadata); err != nil {
+		return nil, false
+	}
+
+	return metadata, true
+}
+
+// GetRawMetadata returns the metadata field as a json.RawMessage.
+func (pv *PackageVersion) GetRawMetadata() json.RawMessage {
+	if pv == nil || pv.Metadata == nil {
+		return json.RawMessage{}
+	}
+
+	return pv.Metadata
 }
 
 func (pv PackageVersion) String() string {
@@ -141,3 +213,106 @@ type PackageContainerMetadata struct {
 func (r PackageContainerMetadata) String() string {
 	return Stringify(r)
 }
+
+// PackageVersionBody represents the body field of a package version.
+type PackageVersionBody struct {
+	Repo *Repository             `json:"repository,omitempty"`
+	Info *PackageVersionBodyInfo `json:"info,omitempty"`
+}
+
+func (b PackageVersionBody) String() string {
+	return Stringify(b)
+}
+
+// PackageVersionBodyInfo represents the info field of a PackageVersionBody.
+type PackageVersionBodyInfo struct {
+	Type       *string `json:"type,omitempty"`
+	OID        *string `json:"oid,omitempty"`
+	Mode       *int64  `json:"mode,omitempty"`
+	Name       *string `json:"name,omitempty"`
+	Path       *string `json:"path,omitempty"`
+	Size       *int64  `json:"size,omitempty"`
+	Collection *bool   `json:"collection,omitempty"`
+}
+
+func (bi PackageVersionBodyInfo) String() string {
+	return Stringify(bi)
+}
+
+// PackageEventContainerMetadata represents metadata for container packages as part of a webhook event.
+// See also PackageContainerMetadata.
+type PackageEventContainerMetadata struct {
+	Labels   map[string]any                    `json:"labels,omitempty"`
+	Manifest map[string]any                    `json:"manifest,omitempty"`
+	Tag      *PackageEventContainerMetadataTag `json:"tag,omitempty"`
+}
+
+func (m PackageEventContainerMetadata) String() string {
+	return Stringify(m)
+}
+
+// PackageEventContainerMetadataTag represents a tag of a GitHub container package.
+type PackageEventContainerMetadataTag struct {
+	Name   *string `json:"name,omitempty"`
+	Digest *string `json:"digest,omitempty"`
+}
+
+func (mt PackageEventContainerMetadataTag) String() string {
+	return Stringify(mt)
+}
+
+// PackageNugetMetadata represents nuget metadata for a GitHub package.
+type PackageNugetMetadata struct {
+	ID    json.RawMessage `json:"id,omitempty"` // Can either be a int64 or string
+	Name  *string         `json:"name,omitempty"`
+	Value json.RawMessage `json:"value,omitempty"` // Can either be a bool, string, integer or object
+}
+
+func (nm PackageNugetMetadata) String() string {
+	return Stringify(nm)
+}
+
+// PackageNPMMetadata represents NPM metadata for a GitHub package.
+type PackageNPMMetadata struct {
+	Name                 *string           `json:"name,omitempty"`
+	Version              *string           `json:"version,omitempty"`
+	NPMUser              *string           `json:"npm_user,omitempty"`
+	Author               map[string]string `json:"author,omitempty"`
+	Bugs                 map[string]string `json:"bugs,omitempty"`
+	Dependencies         map[string]string `json:"dependencies,omitempty"`
+	DevDependencies      map[string]string `json:"dev_dependencies,omitempty"`
+	PeerDependencies     map[string]string `json:"peer_dependencies,omitempty"`
+	OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"`
+	Description          *string           `json:"description,omitempty"`
+	Dist                 map[string]string `json:"dist,omitempty"`
+	GitHead              *string           `json:"git_head,omitempty"`
+	Homepage             *string           `json:"homepage,omitempty"`
+	License              *string           `json:"license,omitempty"`
+	Main                 *string           `json:"main,omitempty"`
+	Repository           map[string]string `json:"repository,omitempty"`
+	Scripts              map[string]any    `json:"scripts,omitempty"`
+	ID                   *string           `json:"id,omitempty"`
+	NodeVersion          *string           `json:"node_version,omitempty"`
+	NPMVersion           *string           `json:"npm_version,omitempty"`
+	HasShrinkwrap        *bool             `json:"has_shrinkwrap,omitempty"`
+	Maintainers          []interface{}     `json:"maintainers,omitempty"`
+	Contributors         []interface{}     `json:"contributors,omitempty"`
+	Engines              map[string]string `json:"engines,omitempty"`
+	Keywords             []string          `json:"keywords,omitempty"`
+	Files                []string          `json:"files,omitempty"`
+	Bin                  map[string]any    `json:"bin,omitempty"`
+	Man                  map[string]any    `json:"man,omitempty"`
+	Directories          map[string]string `json:"directories,omitempty"`
+	OS                   []string          `json:"os,omitempty"`
+	CPU                  []string          `json:"cpu,omitempty"`
+	Readme               *string           `json:"readme,omitempty"`
+	InstallationCommand  *string           `json:"installation_command,omitempty"`
+	ReleaseID            *int64            `json:"release_id,omitempty"`
+	CommitOID            *string           `json:"commit_oid,omitempty"`
+	PublishedViaActions  *bool             `json:"published_via_actions,omitempty"`
+	DeletedByID          *int64            `json:"deleted_by_id,omitempty"`
+}
+
+func (nm PackageNPMMetadata) String() string {
+	return Stringify(nm)
+}
diff --git a/github/secret_scanning.go b/github/secret_scanning.go
index 35322cf6..4eeeba3d 100644
--- a/github/secret_scanning.go
+++ b/github/secret_scanning.go
@@ -68,6 +68,10 @@ type SecretScanningAlertListOptions struct {
 	// Valid resolutions are false_positive, wont_fix, revoked, pattern_edited, pattern_deleted or used_in_tests.
 	Resolution string `url:"resolution,omitempty"`
 
+	// A comma-separated list of validities that, when present, will return alerts that match the validities in this list.
+	// Valid options are active, inactive, and unknown.
+	Validity string `url:"validity,omitempty"`
+
 	// The direction to sort the results by. Possible values are: asc, desc. Default: desc.
 	Direction string `url:"direction,omitempty"`
 


Test patch:
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index ec1fe1c1..c9a7b840 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -20510,6 +20510,28 @@ func TestPackage_GetCreatedAt(tt *testing.T) {
 	p.GetCreatedAt()
 }
 
+func TestPackage_GetDescription(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &Package{Description: &zeroValue}
+	p.GetDescription()
+	p = &Package{}
+	p.GetDescription()
+	p = nil
+	p.GetDescription()
+}
+
+func TestPackage_GetEcosystem(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &Package{Ecosystem: &zeroValue}
+	p.GetEcosystem()
+	p = &Package{}
+	p.GetEcosystem()
+	p = nil
+	p.GetEcosystem()
+}
+
 func TestPackage_GetHTMLURL(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue string
@@ -20543,6 +20565,17 @@ func TestPackage_GetName(tt *testing.T) {
 	p.GetName()
 }
 
+func TestPackage_GetNamespace(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &Package{Namespace: &zeroValue}
+	p.GetNamespace()
+	p = &Package{}
+	p.GetNamespace()
+	p = nil
+	p.GetNamespace()
+}
+
 func TestPackage_GetOwner(tt *testing.T) {
 	tt.Parallel()
 	p := &Package{}
@@ -20578,14 +20611,6 @@ func TestPackage_GetRegistry(tt *testing.T) {
 	p.GetRegistry()
 }
 
-func TestPackage_GetRepository(tt *testing.T) {
-	tt.Parallel()
-	p := &Package{}
-	p.GetRepository()
-	p = nil
-	p.GetRepository()
-}
-
 func TestPackage_GetUpdatedAt(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue Timestamp
@@ -20681,6 +20706,58 @@ func TestPackageEvent_GetSender(tt *testing.T) {
 	p.GetSender()
 }
 
+func TestPackageEventContainerMetadata_GetLabels(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]any{}
+	p := &PackageEventContainerMetadata{Labels: zeroValue}
+	p.GetLabels()
+	p = &PackageEventContainerMetadata{}
+	p.GetLabels()
+	p = nil
+	p.GetLabels()
+}
+
+func TestPackageEventContainerMetadata_GetManifest(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]any{}
+	p := &PackageEventContainerMetadata{Manifest: zeroValue}
+	p.GetManifest()
+	p = &PackageEventContainerMetadata{}
+	p.GetManifest()
+	p = nil
+	p.GetManifest()
+}
+
+func TestPackageEventContainerMetadata_GetTag(tt *testing.T) {
+	tt.Parallel()
+	p := &PackageEventContainerMetadata{}
+	p.GetTag()
+	p = nil
+	p.GetTag()
+}
+
+func TestPackageEventContainerMetadataTag_GetDigest(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageEventContainerMetadataTag{Digest: &zeroValue}
+	p.GetDigest()
+	p = &PackageEventContainerMetadataTag{}
+	p.GetDigest()
+	p = nil
+	p.GetDigest()
+}
+
+func TestPackageEventContainerMetadataTag_GetName(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageEventContainerMetadataTag{Name: &zeroValue}
+	p.GetName()
+	p = &PackageEventContainerMetadataTag{}
+	p.GetName()
+	p = nil
+	p.GetName()
+}
+
 func TestPackageFile_GetAuthor(tt *testing.T) {
 	tt.Parallel()
 	p := &PackageFile{}
@@ -20862,6 +20939,358 @@ func TestPackageMetadata_GetPackageType(tt *testing.T) {
 	p.GetPackageType()
 }
 
+func TestPackageNPMMetadata_GetAuthor(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{Author: zeroValue}
+	p.GetAuthor()
+	p = &PackageNPMMetadata{}
+	p.GetAuthor()
+	p = nil
+	p.GetAuthor()
+}
+
+func TestPackageNPMMetadata_GetBin(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]any{}
+	p := &PackageNPMMetadata{Bin: zeroValue}
+	p.GetBin()
+	p = &PackageNPMMetadata{}
+	p.GetBin()
+	p = nil
+	p.GetBin()
+}
+
+func TestPackageNPMMetadata_GetBugs(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{Bugs: zeroValue}
+	p.GetBugs()
+	p = &PackageNPMMetadata{}
+	p.GetBugs()
+	p = nil
+	p.GetBugs()
+}
+
+func TestPackageNPMMetadata_GetCommitOID(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{CommitOID: &zeroValue}
+	p.GetCommitOID()
+	p = &PackageNPMMetadata{}
+	p.GetCommitOID()
+	p = nil
+	p.GetCommitOID()
+}
+
+func TestPackageNPMMetadata_GetDeletedByID(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue int64
+	p := &PackageNPMMetadata{DeletedByID: &zeroValue}
+	p.GetDeletedByID()
+	p = &PackageNPMMetadata{}
+	p.GetDeletedByID()
+	p = nil
+	p.GetDeletedByID()
+}
+
+func TestPackageNPMMetadata_GetDependencies(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{Dependencies: zeroValue}
+	p.GetDependencies()
+	p = &PackageNPMMetadata{}
+	p.GetDependencies()
+	p = nil
+	p.GetDependencies()
+}
+
+func TestPackageNPMMetadata_GetDescription(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{Description: &zeroValue}
+	p.GetDescription()
+	p = &PackageNPMMetadata{}
+	p.GetDescription()
+	p = nil
+	p.GetDescription()
+}
+
+func TestPackageNPMMetadata_GetDevDependencies(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{DevDependencies: zeroValue}
+	p.GetDevDependencies()
+	p = &PackageNPMMetadata{}
+	p.GetDevDependencies()
+	p = nil
+	p.GetDevDependencies()
+}
+
+func TestPackageNPMMetadata_GetDirectories(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{Directories: zeroValue}
+	p.GetDirectories()
+	p = &PackageNPMMetadata{}
+	p.GetDirectories()
+	p = nil
+	p.GetDirectories()
+}
+
+func TestPackageNPMMetadata_GetDist(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{Dist: zeroValue}
+	p.GetDist()
+	p = &PackageNPMMetadata{}
+	p.GetDist()
+	p = nil
+	p.GetDist()
+}
+
+func TestPackageNPMMetadata_GetEngines(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{Engines: zeroValue}
+	p.GetEngines()
+	p = &PackageNPMMetadata{}
+	p.GetEngines()
+	p = nil
+	p.GetEngines()
+}
+
+func TestPackageNPMMetadata_GetGitHead(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{GitHead: &zeroValue}
+	p.GetGitHead()
+	p = &PackageNPMMetadata{}
+	p.GetGitHead()
+	p = nil
+	p.GetGitHead()
+}
+
+func TestPackageNPMMetadata_GetHasShrinkwrap(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue bool
+	p := &PackageNPMMetadata{HasShrinkwrap: &zeroValue}
+	p.GetHasShrinkwrap()
+	p = &PackageNPMMetadata{}
+	p.GetHasShrinkwrap()
+	p = nil
+	p.GetHasShrinkwrap()
+}
+
+func TestPackageNPMMetadata_GetHomepage(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{Homepage: &zeroValue}
+	p.GetHomepage()
+	p = &PackageNPMMetadata{}
+	p.GetHomepage()
+	p = nil
+	p.GetHomepage()
+}
+
+func TestPackageNPMMetadata_GetID(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{ID: &zeroValue}
+	p.GetID()
+	p = &PackageNPMMetadata{}
+	p.GetID()
+	p = nil
+	p.GetID()
+}
+
+func TestPackageNPMMetadata_GetInstallationCommand(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{InstallationCommand: &zeroValue}
+	p.GetInstallationCommand()
+	p = &PackageNPMMetadata{}
+	p.GetInstallationCommand()
+	p = nil
+	p.GetInstallationCommand()
+}
+
+func TestPackageNPMMetadata_GetLicense(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{License: &zeroValue}
+	p.GetLicense()
+	p = &PackageNPMMetadata{}
+	p.GetLicense()
+	p = nil
+	p.GetLicense()
+}
+
+func TestPackageNPMMetadata_GetMain(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{Main: &zeroValue}
+	p.GetMain()
+	p = &PackageNPMMetadata{}
+	p.GetMain()
+	p = nil
+	p.GetMain()
+}
+
+func TestPackageNPMMetadata_GetMan(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]any{}
+	p := &PackageNPMMetadata{Man: zeroValue}
+	p.GetMan()
+	p = &PackageNPMMetadata{}
+	p.GetMan()
+	p = nil
+	p.GetMan()
+}
+
+func TestPackageNPMMetadata_GetName(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{Name: &zeroValue}
+	p.GetName()
+	p = &PackageNPMMetadata{}
+	p.GetName()
+	p = nil
+	p.GetName()
+}
+
+func TestPackageNPMMetadata_GetNodeVersion(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{NodeVersion: &zeroValue}
+	p.GetNodeVersion()
+	p = &PackageNPMMetadata{}
+	p.GetNodeVersion()
+	p = nil
+	p.GetNodeVersion()
+}
+
+func TestPackageNPMMetadata_GetNPMUser(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{NPMUser: &zeroValue}
+	p.GetNPMUser()
+	p = &PackageNPMMetadata{}
+	p.GetNPMUser()
+	p = nil
+	p.GetNPMUser()
+}
+
+func TestPackageNPMMetadata_GetNPMVersion(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{NPMVersion: &zeroValue}
+	p.GetNPMVersion()
+	p = &PackageNPMMetadata{}
+	p.GetNPMVersion()
+	p = nil
+	p.GetNPMVersion()
+}
+
+func TestPackageNPMMetadata_GetOptionalDependencies(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{OptionalDependencies: zeroValue}
+	p.GetOptionalDependencies()
+	p = &PackageNPMMetadata{}
+	p.GetOptionalDependencies()
+	p = nil
+	p.GetOptionalDependencies()
+}
+
+func TestPackageNPMMetadata_GetPeerDependencies(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{PeerDependencies: zeroValue}
+	p.GetPeerDependencies()
+	p = &PackageNPMMetadata{}
+	p.GetPeerDependencies()
+	p = nil
+	p.GetPeerDependencies()
+}
+
+func TestPackageNPMMetadata_GetPublishedViaActions(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue bool
+	p := &PackageNPMMetadata{PublishedViaActions: &zeroValue}
+	p.GetPublishedViaActions()
+	p = &PackageNPMMetadata{}
+	p.GetPublishedViaActions()
+	p = nil
+	p.GetPublishedViaActions()
+}
+
+func TestPackageNPMMetadata_GetReadme(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{Readme: &zeroValue}
+	p.GetReadme()
+	p = &PackageNPMMetadata{}
+	p.GetReadme()
+	p = nil
+	p.GetReadme()
+}
+
+func TestPackageNPMMetadata_GetReleaseID(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue int64
+	p := &PackageNPMMetadata{ReleaseID: &zeroValue}
+	p.GetReleaseID()
+	p = &PackageNPMMetadata{}
+	p.GetReleaseID()
+	p = nil
+	p.GetReleaseID()
+}
+
+func TestPackageNPMMetadata_GetRepository(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]string{}
+	p := &PackageNPMMetadata{Repository: zeroValue}
+	p.GetRepository()
+	p = &PackageNPMMetadata{}
+	p.GetRepository()
+	p = nil
+	p.GetRepository()
+}
+
+func TestPackageNPMMetadata_GetScripts(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]any{}
+	p := &PackageNPMMetadata{Scripts: zeroValue}
+	p.GetScripts()
+	p = &PackageNPMMetadata{}
+	p.GetScripts()
+	p = nil
+	p.GetScripts()
+}
+
+func TestPackageNPMMetadata_GetVersion(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNPMMetadata{Version: &zeroValue}
+	p.GetVersion()
+	p = &PackageNPMMetadata{}
+	p.GetVersion()
+	p = nil
+	p.GetVersion()
+}
+
+func TestPackageNugetMetadata_GetName(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageNugetMetadata{Name: &zeroValue}
+	p.GetName()
+	p = &PackageNugetMetadata{}
+	p.GetName()
+	p = nil
+	p.GetName()
+}
+
 func TestPackageRegistry_GetAboutURL(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue string
@@ -21043,17 +21472,6 @@ func TestPackageVersion_GetAuthor(tt *testing.T) {
 	p.GetAuthor()
 }
 
-func TestPackageVersion_GetBody(tt *testing.T) {
-	tt.Parallel()
-	var zeroValue string
-	p := &PackageVersion{Body: &zeroValue}
-	p.GetBody()
-	p = &PackageVersion{}
-	p.GetBody()
-	p = nil
-	p.GetBody()
-}
-
 func TestPackageVersion_GetBodyHTML(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue string
@@ -21065,6 +21483,14 @@ func TestPackageVersion_GetBodyHTML(tt *testing.T) {
 	p.GetBodyHTML()
 }
 
+func TestPackageVersion_GetContainerMetadata(tt *testing.T) {
+	tt.Parallel()
+	p := &PackageVersion{}
+	p.GetContainerMetadata()
+	p = nil
+	p.GetContainerMetadata()
+}
+
 func TestPackageVersion_GetCreatedAt(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue Timestamp
@@ -21076,6 +21502,28 @@ func TestPackageVersion_GetCreatedAt(tt *testing.T) {
 	p.GetCreatedAt()
 }
 
+func TestPackageVersion_GetDeletedAt(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue Timestamp
+	p := &PackageVersion{DeletedAt: &zeroValue}
+	p.GetDeletedAt()
+	p = &PackageVersion{}
+	p.GetDeletedAt()
+	p = nil
+	p.GetDeletedAt()
+}
+
+func TestPackageVersion_GetDescription(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageVersion{Description: &zeroValue}
+	p.GetDescription()
+	p = &PackageVersion{}
+	p.GetDescription()
+	p = nil
+	p.GetDescription()
+}
+
 func TestPackageVersion_GetDraft(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue bool
@@ -21120,6 +21568,17 @@ func TestPackageVersion_GetInstallationCommand(tt *testing.T) {
 	p.GetInstallationCommand()
 }
 
+func TestPackageVersion_GetLicense(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageVersion{License: &zeroValue}
+	p.GetLicense()
+	p = &PackageVersion{}
+	p.GetLicense()
+	p = nil
+	p.GetLicense()
+}
+
 func TestPackageVersion_GetManifest(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue string
@@ -21131,14 +21590,6 @@ func TestPackageVersion_GetManifest(tt *testing.T) {
 	p.GetManifest()
 }
 
-func TestPackageVersion_GetMetadata(tt *testing.T) {
-	tt.Parallel()
-	p := &PackageVersion{}
-	p.GetMetadata()
-	p = nil
-	p.GetMetadata()
-}
-
 func TestPackageVersion_GetName(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue string
@@ -21150,6 +21601,14 @@ func TestPackageVersion_GetName(tt *testing.T) {
 	p.GetName()
 }
 
+func TestPackageVersion_GetNPMMetadata(tt *testing.T) {
+	tt.Parallel()
+	p := &PackageVersion{}
+	p.GetNPMMetadata()
+	p = nil
+	p.GetNPMMetadata()
+}
+
 func TestPackageVersion_GetPackageHTMLURL(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue string
@@ -21161,6 +21620,17 @@ func TestPackageVersion_GetPackageHTMLURL(tt *testing.T) {
 	p.GetPackageHTMLURL()
 }
 
+func TestPackageVersion_GetPackageURL(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageVersion{PackageURL: &zeroValue}
+	p.GetPackageURL()
+	p = &PackageVersion{}
+	p.GetPackageURL()
+	p = nil
+	p.GetPackageURL()
+}
+
 func TestPackageVersion_GetPrerelease(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue bool
@@ -21180,6 +21650,28 @@ func TestPackageVersion_GetRelease(tt *testing.T) {
 	p.GetRelease()
 }
 
+func TestPackageVersion_GetRubyMetadata(tt *testing.T) {
+	tt.Parallel()
+	zeroValue := map[string]any{}
+	p := &PackageVersion{RubyMetadata: zeroValue}
+	p.GetRubyMetadata()
+	p = &PackageVersion{}
+	p.GetRubyMetadata()
+	p = nil
+	p.GetRubyMetadata()
+}
+
+func TestPackageVersion_GetSourceURL(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageVersion{SourceURL: &zeroValue}
+	p.GetSourceURL()
+	p = &PackageVersion{}
+	p.GetSourceURL()
+	p = nil
+	p.GetSourceURL()
+}
+
 func TestPackageVersion_GetSummary(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue string
@@ -21257,6 +21749,99 @@ func TestPackageVersion_GetVersion(tt *testing.T) {
 	p.GetVersion()
 }
 
+func TestPackageVersionBody_GetInfo(tt *testing.T) {
+	tt.Parallel()
+	p := &PackageVersionBody{}
+	p.GetInfo()
+	p = nil
+	p.GetInfo()
+}
+
+func TestPackageVersionBody_GetRepo(tt *testing.T) {
+	tt.Parallel()
+	p := &PackageVersionBody{}
+	p.GetRepo()
+	p = nil
+	p.GetRepo()
+}
+
+func TestPackageVersionBodyInfo_GetCollection(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue bool
+	p := &PackageVersionBodyInfo{Collection: &zeroValue}
+	p.GetCollection()
+	p = &PackageVersionBodyInfo{}
+	p.GetCollection()
+	p = nil
+	p.GetCollection()
+}
+
+func TestPackageVersionBodyInfo_GetMode(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue int64
+	p := &PackageVersionBodyInfo{Mode: &zeroValue}
+	p.GetMode()
+	p = &PackageVersionBodyInfo{}
+	p.GetMode()
+	p = nil
+	p.GetMode()
+}
+
+func TestPackageVersionBodyInfo_GetName(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageVersionBodyInfo{Name: &zeroValue}
+	p.GetName()
+	p = &PackageVersionBodyInfo{}
+	p.GetName()
+	p = nil
+	p.GetName()
+}
+
+func TestPackageVersionBodyInfo_GetOID(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageVersionBodyInfo{OID: &zeroValue}
+	p.GetOID()
+	p = &PackageVersionBodyInfo{}
+	p.GetOID()
+	p = nil
+	p.GetOID()
+}
+
+func TestPackageVersionBodyInfo_GetPath(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageVersionBodyInfo{Path: &zeroValue}
+	p.GetPath()
+	p = &PackageVersionBodyInfo{}
+	p.GetPath()
+	p = nil
+	p.GetPath()
+}
+
+func TestPackageVersionBodyInfo_GetSize(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue int64
+	p := &PackageVersionBodyInfo{Size: &zeroValue}
+	p.GetSize()
+	p = &PackageVersionBodyInfo{}
+	p.GetSize()
+	p = nil
+	p.GetSize()
+}
+
+func TestPackageVersionBodyInfo_GetType(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	p := &PackageVersionBodyInfo{Type: &zeroValue}
+	p.GetType()
+	p = &PackageVersionBodyInfo{}
+	p.GetType()
+	p = nil
+	p.GetType()
+}
+
 func TestPage_GetAction(tt *testing.T) {
 	tt.Parallel()
 	var zeroValue string
diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go
index e0944471..5abc381a 100644
--- a/github/github-stringify_test.go
+++ b/github/github-stringify_test.go
@@ -1136,17 +1136,19 @@ func TestPackage_String(t *testing.T) {
 		Name:           Ptr(""),
 		PackageType:    Ptr(""),
 		HTMLURL:        Ptr(""),
+		Visibility:     Ptr(""),
+		Owner:          &User{},
 		CreatedAt:      &Timestamp{},
 		UpdatedAt:      &Timestamp{},
-		Owner:          &User{},
+		Namespace:      Ptr(""),
+		Description:    Ptr(""),
+		Ecosystem:      Ptr(""),
 		PackageVersion: &PackageVersion{},
 		Registry:       &PackageRegistry{},
 		URL:            Ptr(""),
 		VersionCount:   Ptr(int64(0)),
-		Visibility:     Ptr(""),
-		Repository:     &Repository{},
 	}
-	want := `github.Package{ID:0, Name:"", PackageType:"", HTMLURL:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Owner:github.User{}, PackageVersion:github.PackageVersion{}, Registry:github.PackageRegistry{}, URL:"", VersionCount:0, Visibility:"", Repository:github.Repository{}}`
+	want := `github.Package{ID:0, Name:"", PackageType:"", HTMLURL:"", Visibility:"", Owner:github.User{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Namespace:"", Description:"", Ecosystem:"", PackageVersion:github.PackageVersion{}, Registry:github.PackageRegistry{}, URL:"", VersionCount:0}`
 	if got := v.String(); got != want {
 		t.Errorf("Package.String = %v, want %v", got, want)
 	}
@@ -1163,6 +1165,29 @@ func TestPackageContainerMetadata_String(t *testing.T) {
 	}
 }
 
+func TestPackageEventContainerMetadata_String(t *testing.T) {
+	t.Parallel()
+	v := PackageEventContainerMetadata{
+		Tag: &PackageEventContainerMetadataTag{},
+	}
+	want := `github.PackageEventContainerMetadata{Tag:github.PackageEventContainerMetadataTag{}}`
+	if got := v.String(); got != want {
+		t.Errorf("PackageEventContainerMetadata.String = %v, want %v", got, want)
+	}
+}
+
+func TestPackageEventContainerMetadataTag_String(t *testing.T) {
+	t.Parallel()
+	v := PackageEventContainerMetadataTag{
+		Name:   Ptr(""),
+		Digest: Ptr(""),
+	}
+	want := `github.PackageEventContainerMetadataTag{Name:"", Digest:""}`
+	if got := v.String(); got != want {
+		t.Errorf("PackageEventContainerMetadataTag.String = %v, want %v", got, want)
+	}
+}
+
 func TestPackageFile_String(t *testing.T) {
 	t.Parallel()
 	v := PackageFile{
@@ -1197,6 +1222,49 @@ func TestPackageMetadata_String(t *testing.T) {
 	}
 }
 
+func TestPackageNPMMetadata_String(t *testing.T) {
+	t.Parallel()
+	v := PackageNPMMetadata{
+		Name:                Ptr(""),
+		Version:             Ptr(""),
+		NPMUser:             Ptr(""),
+		Description:         Ptr(""),
+		GitHead:             Ptr(""),
+		Homepage:            Ptr(""),
+		License:             Ptr(""),
+		Main:                Ptr(""),
+		ID:                  Ptr(""),
+		NodeVersion:         Ptr(""),
+		NPMVersion:          Ptr(""),
+		HasShrinkwrap:       Ptr(false),
+		Keywords:            []string{""},
+		Files:               []string{""},
+		OS:                  []string{""},
+		CPU:                 []string{""},
+		Readme:              Ptr(""),
+		InstallationCommand: Ptr(""),
+		ReleaseID:           Ptr(int64(0)),
+		CommitOID:           Ptr(""),
+		PublishedViaActions: Ptr(false),
+		DeletedByID:         Ptr(int64(0)),
+	}
+	want := `github.PackageNPMMetadata{Name:"", Version:"", NPMUser:"", Description:"", GitHead:"", Homepage:"", License:"", Main:"", ID:"", NodeVersion:"", NPMVersion:"", HasShrinkwrap:false, Keywords:[""], Files:[""], OS:[""], CPU:[""], Readme:"", InstallationCommand:"", ReleaseID:0, CommitOID:"", PublishedViaActions:false, DeletedByID:0}`
+	if got := v.String(); got != want {
+		t.Errorf("PackageNPMMetadata.String = %v, want %v", got, want)
+	}
+}
+
+func TestPackageNugetMetadata_String(t *testing.T) {
+	t.Parallel()
+	v := PackageNugetMetadata{
+		Name: Ptr(""),
+	}
+	want := `github.PackageNugetMetadata{Name:""}`
+	if got := v.String(); got != want {
+		t.Errorf("PackageNugetMetadata.String = %v, want %v", got, want)
+	}
+}
+
 func TestPackageRegistry_String(t *testing.T) {
 	t.Parallel()
 	v := PackageRegistry{
@@ -1237,9 +1305,15 @@ func TestPackageVersion_String(t *testing.T) {
 	t.Parallel()
 	v := PackageVersion{
 		ID:                  Ptr(int64(0)),
+		Name:                Ptr(""),
+		URL:                 Ptr(""),
+		PackageHTMLURL:      Ptr(""),
+		License:             Ptr(""),
+		Description:         Ptr(""),
+		CreatedAt:           &Timestamp{},
+		UpdatedAt:           &Timestamp{},
 		Version:             Ptr(""),
 		Summary:             Ptr(""),
-		Body:                Ptr(""),
 		BodyHTML:            Ptr(""),
 		Release:             &PackageRelease{},
 		Manifest:            Ptr(""),
@@ -1249,21 +1323,49 @@ func TestPackageVersion_String(t *testing.T) {
 		TargetOID:           Ptr(""),
 		Draft:               Ptr(false),
 		Prerelease:          Ptr(false),
-		CreatedAt:           &Timestamp{},
-		UpdatedAt:           &Timestamp{},
+		ContainerMetadata:   &PackageEventContainerMetadata{},
+		NPMMetadata:         &PackageNPMMetadata{},
+		PackageURL:          Ptr(""),
 		Author:              &User{},
+		SourceURL:           Ptr(""),
 		InstallationCommand: Ptr(""),
-		Metadata:            &PackageMetadata{},
-		PackageHTMLURL:      Ptr(""),
-		Name:                Ptr(""),
-		URL:                 Ptr(""),
+		DeletedAt:           &Timestamp{},
 	}
-	want := `github.PackageVersion{ID:0, Version:"", Summary:"", Body:"", BodyHTML:"", Release:github.PackageRelease{}, Manifest:"", HTMLURL:"", TagName:"", TargetCommitish:"", TargetOID:"", Draft:false, Prerelease:false, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Author:github.User{}, InstallationCommand:"", Metadata:github.PackageMetadata{}, PackageHTMLURL:"", Name:"", URL:""}`
+	want := `github.PackageVersion{ID:0, Name:"", URL:"", PackageHTMLURL:"", License:"", Description:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Version:"", Summary:"", BodyHTML:"", Release:github.PackageRelease{}, Manifest:"", HTMLURL:"", TagName:"", TargetCommitish:"", TargetOID:"", Draft:false, Prerelease:false, ContainerMetadata:github.PackageEventContainerMetadata{}, NPMMetadata:github.PackageNPMMetadata{}, PackageURL:"", Author:github.User{}, SourceURL:"", InstallationCommand:"", DeletedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}`
 	if got := v.String(); got != want {
 		t.Errorf("PackageVersion.String = %v, want %v", got, want)
 	}
 }
 
+func TestPackageVersionBody_String(t *testing.T) {
+	t.Parallel()
+	v := PackageVersionBody{
+		Repo: &Repository{},
+		Info: &PackageVersionBodyInfo{},
+	}
+	want := `github.PackageVersionBody{Repo:github.Repository{}, Info:github.PackageVersionBodyInfo{}}`
+	if got := v.String(); got != want {
+		t.Errorf("PackageVersionBody.String = %v, want %v", got, want)
+	}
+}
+
+func TestPackageVersionBodyInfo_String(t *testing.T) {
+	t.Parallel()
+	v := PackageVersionBodyInfo{
+		Type:       Ptr(""),
+		OID:        Ptr(""),
+		Mode:       Ptr(int64(0)),
+		Name:       Ptr(""),
+		Path:       Ptr(""),
+		Size:       Ptr(int64(0)),
+		Collection: Ptr(false),
+	}
+	want := `github.PackageVersionBodyInfo{Type:"", OID:"", Mode:0, Name:"", Path:"", Size:0, Collection:false}`
+	if got := v.String(); got != want {
+		t.Errorf("PackageVersionBodyInfo.String = %v, want %v", got, want)
+	}
+}
+
 func TestPageStats_String(t *testing.T) {
 	t.Parallel()
 	v := PageStats{
diff --git a/github/orgs_packages_test.go b/github/orgs_packages_test.go
index 836584a1..1bc72bc7 100644
--- a/github/orgs_packages_test.go
+++ b/github/orgs_packages_test.go
@@ -7,6 +7,7 @@ package github
 
 import (
 	"context"
+	"encoding/json"
 	"io"
 	"net/http"
 	"testing"
@@ -232,6 +233,15 @@ func TestOrganizationsService_ListPackagesVersions(t *testing.T) {
 	t.Parallel()
 	client, mux, _ := setup(t)
 
+	m := `{
+		"package_type": "container",
+		"container": {
+			"tags": [
+			"latest"
+			]
+		}
+	}`
+
 	// don't url escape the package name here since mux will convert it to a slash automatically
 	mux.HandleFunc("/orgs/o/packages/container/hello%2Fhello_docker/versions", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
@@ -245,14 +255,7 @@ func TestOrganizationsService_ListPackagesVersions(t *testing.T) {
 			  "created_at": `+referenceTimeStr+`,
 			  "updated_at": `+referenceTimeStr+`,
 			  "html_url": "https://github.com/users/octocat/packages/container/hello%2Fhello_docker/45763",
-			  "metadata": {
-				"package_type": "container",
-				"container": {
-				  "tags": [
-					"latest"
-				  ]
-				}
-			  }
+			  "metadata": `+m+`
 			}]`)
 		if err != nil {
 			t.Fatal("Failed to write test response: ", err)
@@ -276,12 +279,7 @@ func TestOrganizationsService_ListPackagesVersions(t *testing.T) {
 		CreatedAt:      &Timestamp{referenceTime},
 		UpdatedAt:      &Timestamp{referenceTime},
 		HTMLURL:        Ptr("https://github.com/users/octocat/packages/container/hello%2Fhello_docker/45763"),
-		Metadata: &PackageMetadata{
-			PackageType: Ptr("container"),
-			Container: &PackageContainerMetadata{
-				Tags: []string{"latest"},
-			},
-		},
+		Metadata:       json.RawMessage(m),
 	}}
 	if !cmp.Equal(packages, want) {
 		t.Errorf("Organizations.PackageGetAllVersions returned %+v, want %+v", packages, want)
@@ -306,6 +304,15 @@ func TestOrganizationsService_PackageGetVersion(t *testing.T) {
 	t.Parallel()
 	client, mux, _ := setup(t)
 
+	m := `{
+		"package_type": "container",
+		"container": {
+			"tags": [
+			"latest"
+			]
+		}
+	}`
+
 	// don't url escape the package name here since mux will convert it to a slash automatically
 	mux.HandleFunc("/orgs/o/packages/container/hello%2Fhello_docker/versions/45763", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
@@ -318,14 +325,7 @@ func TestOrganizationsService_PackageGetVersion(t *testing.T) {
 			  "created_at": `+referenceTimeStr+`,
 			  "updated_at": `+referenceTimeStr+`,
 			  "html_url": "https://github.com/users/octocat/packages/container/hello%2Fhello_docker/45763",
-			  "metadata": {
-				"package_type": "container",
-				"container": {
-				  "tags": [
-					"latest"
-				  ]
-				}
-			  }
+			  "metadata": `+m+`
 			}`)
 		if err != nil {
 			t.Fatal("Failed to write test response: ", err)
@@ -346,12 +346,7 @@ func TestOrganizationsService_PackageGetVersion(t *testing.T) {
 		CreatedAt:      &Timestamp{referenceTime},
 		UpdatedAt:      &Timestamp{referenceTime},
 		HTMLURL:        Ptr("https://github.com/users/octocat/packages/container/hello%2Fhello_docker/45763"),
-		Metadata: &PackageMetadata{
-			PackageType: Ptr("container"),
-			Container: &PackageContainerMetadata{
-				Tags: []string{"latest"},
-			},
-		},
+		Metadata:       json.RawMessage(m),
 	}
 	if !cmp.Equal(packages, want) {
 		t.Errorf("Organizations.PackageGetVersion returned %+v, want %+v", packages, want)
diff --git a/github/packages_test.go b/github/packages_test.go
index 718998f7..414ba503 100644
--- a/github/packages_test.go
+++ b/github/packages_test.go
@@ -5,7 +5,11 @@
 
 package github
 
-import "testing"
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+)
 
 func TestPackageRegistry_Marshal(t *testing.T) {
 	t.Parallel()
@@ -181,12 +185,22 @@ func TestPackageVersion_Marshal(t *testing.T) {
 	t.Parallel()
 	testJSONMarshal(t, &PackageVersion{}, "{}")
 
+	metadata, _ := json.Marshal([]string{"a", "b"})
+
 	o := &PackageVersion{
-		ID:       Ptr(int64(1)),
-		Version:  Ptr("ver"),
-		Summary:  Ptr("sum"),
-		Body:     Ptr("body"),
-		BodyHTML: Ptr("btnhtml"),
+		ID:             Ptr(int64(1)),
+		Name:           Ptr("n"),
+		URL:            Ptr("url"),
+		PackageHTMLURL: Ptr("phurl"),
+		License:        Ptr("l"),
+		Description:    Ptr("d"),
+		CreatedAt:      &Timestamp{referenceTime},
+		UpdatedAt:      &Timestamp{referenceTime},
+		Metadata:       metadata,
+		Version:        Ptr("ver"),
+		Summary:        Ptr("sum"),
+		Body:           json.RawMessage(`"body"`),
+		BodyHTML:       Ptr("btnhtml"),
 		Release: &PackageRelease{
 			URL:             Ptr("url"),
 			HTMLURL:         Ptr("hurl"),
@@ -226,8 +240,17 @@ func TestPackageVersion_Marshal(t *testing.T) {
 		TargetOID:       Ptr("tid"),
 		Draft:           Ptr(true),
 		Prerelease:      Ptr(true),
-		CreatedAt:       &Timestamp{referenceTime},
-		UpdatedAt:       &Timestamp{referenceTime},
+		ContainerMetadata: &PackageEventContainerMetadata{
+			Labels: map[string]any{"l1": true, "l2": "a"},
+		},
+		DockerMetadata: []interface{}{"a", "b"},
+		NPMMetadata: Ptr(PackageNPMMetadata{
+			Name: Ptr("n"),
+		}),
+		NugetMetadata: []*PackageNugetMetadata{
+			{Name: Ptr("n")},
+		},
+		RubyMetadata: map[string]any{"k1": "v1", "k2": "v2"},
 		PackageFiles: []*PackageFile{
 			{
 				DownloadURL: Ptr("durl"),
@@ -263,6 +286,7 @@ func TestPackageVersion_Marshal(t *testing.T) {
 				UpdatedAt: &Timestamp{referenceTime},
 			},
 		},
+		PackageURL: Ptr("purl"),
 		Author: &User{
 			Login:           Ptr("l"),
 			ID:              Ptr(int64(1)),
@@ -283,11 +307,21 @@ func TestPackageVersion_Marshal(t *testing.T) {
 			CreatedAt:       &Timestamp{referenceTime},
 			SuspendedAt:     &Timestamp{referenceTime},
 		},
+		SourceURL:           Ptr("surl"),
 		InstallationCommand: Ptr("ic"),
+		DeletedAt:           &Timestamp{referenceTime},
 	}
 
 	want := `{
 		"id": 1,
+		"name": "n",
+		"url": "url",
+		"package_html_url": "phurl",
+		"license": "l",
+		"description": "d",
+		"created_at": ` + referenceTimeStr + `,
+		"updated_at": ` + referenceTimeStr + `,
+		"metadata": ["a", "b"],
 		"version": "ver",
 		"summary": "sum",
 		"body": "body",
@@ -331,8 +365,21 @@ func TestPackageVersion_Marshal(t *testing.T) {
 		"target_oid": "tid",
 		"draft": true,
 		"prerelease": true,
-		"created_at": ` + referenceTimeStr + `,
-		"updated_at": ` + referenceTimeStr + `,
+		"container_metadata": {
+			"labels": {
+				"l1": true,
+				"l2": "a"
+			}
+		},
+		"docker_metadata": ["a", "b"],
+		"npm_metadata": {
+			"name": "n"
+		},
+		"nuget_metadata": [{"name": "n"}],
+		"ruby_metadata": {
+			"k1": "v1",
+			"k2": "v2"
+		},
 		"package_files": [
 			{
 				"download_url": "durl",
@@ -368,6 +415,7 @@ func TestPackageVersion_Marshal(t *testing.T) {
 				"updated_at": ` + referenceTimeStr + `
 			}
 		],
+		"package_url": "purl",
 		"author": {
 			"login": "l",
 			"id": 1,
@@ -388,12 +436,230 @@ func TestPackageVersion_Marshal(t *testing.T) {
 			"suspended_at": ` + referenceTimeStr + `,
 			"url": "u"
 		},
-		"installation_command": "ic"
+		"source_url": "surl",
+		"installation_command": "ic",
+		"deleted_at": ` + referenceTimeStr + `
 	}`
 
 	testJSONMarshal(t, o, want)
 }
 
+func TestPackageVersion_GetBody(t *testing.T) {
+	t.Parallel()
+
+	tests := map[string]struct {
+		pv        *PackageVersion
+		wantValue string
+		wantOk    bool
+	}{
+		"pv nil": {
+			pv:        nil,
+			wantValue: "",
+			wantOk:    false,
+		},
+		"body nil": {
+			pv: &PackageVersion{
+				Body: nil,
+			},
+			wantValue: "",
+			wantOk:    false,
+		},
+		"invalid body": {
+			pv: &PackageVersion{
+				Body: json.RawMessage(`{
+					"repository": {
+						"name": "n"
+					},
+					"info": {
+						"type": "t"
+					}
+				}`),
+			},
+			wantValue: "",
+			wantOk:    false,
+		},
+		"valid body": {
+			pv: &PackageVersion{
+				Body: json.RawMessage(`"body"`),
+			},
+			wantValue: "body",
+			wantOk:    true,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+
+			resValue, resOk := test.pv.GetBody()
+
+			if resValue != test.wantValue || resOk != test.wantOk {
+				t.Errorf("PackageVersion.GetBody() - got: %v, %v; want: %v, %v", resValue, resOk, test.wantValue, test.wantOk)
+			}
+		})
+	}
+}
+
+func TestPackageVersion_GetBodyAsPackageVersionBody(t *testing.T) {
+	t.Parallel()
+
+	tests := map[string]struct {
+		pv        *PackageVersion
+		wantValue *PackageVersionBody
+		wantOk    bool
+	}{
+		"pv nil": {
+			pv:        nil,
+			wantValue: nil,
+			wantOk:    false,
+		},
+		"body nil": {
+			pv: &PackageVersion{
+				Body: nil,
+			},
+			wantValue: nil,
+			wantOk:    false,
+		},
+		"invalid body": {
+			pv: &PackageVersion{
+				Body: json.RawMessage(`"body"`),
+			},
+			wantValue: nil,
+			wantOk:    false,
+		},
+		"valid body": {
+			pv: &PackageVersion{
+				Body: json.RawMessage(`{
+					"repository": {
+						"name": "n"
+					},
+					"info": {
+						"type": "t"
+					}
+				}`),
+			},
+			wantValue: &PackageVersionBody{
+				Repo: &Repository{
+					Name: Ptr("n"),
+				},
+				Info: &PackageVersionBodyInfo{
+					Type: Ptr("t"),
+				},
+			},
+			wantOk: true,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+
+			resValue, resOk := test.pv.GetBodyAsPackageVersionBody()
+
+			if !reflect.DeepEqual(resValue, test.wantValue) || resOk != test.wantOk {
+				t.Errorf("PackageVersion.GetBodyAsPackageVersionBody() - got: %v, %v; want: %v, %v", resValue, resOk, test.wantValue, test.wantOk)
+			}
+		})
+	}
+}
+
+func TestPackageVersion_GetMetadata(t *testing.T) {
+	t.Parallel()
+
+	tests := map[string]struct {
+		pv        *PackageVersion
+		wantValue *PackageMetadata
+		wantOk    bool
+	}{
+		"pv nil": {
+			pv:        nil,
+			wantValue: nil,
+			wantOk:    false,
+		},
+		"metadata nil": {
+			pv: &PackageVersion{
+				Metadata: nil,
+			},
+			wantValue: nil,
+			wantOk:    false,
+		},
+		"invalid metadata": {
+			pv: &PackageVersion{
+				Metadata: json.RawMessage(`[]`),
+			},
+			wantValue: nil,
+			wantOk:    false,
+		},
+		"valid metadata": {
+			pv: &PackageVersion{
+				Metadata: json.RawMessage(`{
+					"package_type": "container",
+					"container": {
+						"tags": ["a"]
+					}
+				}`),
+			},
+			wantValue: &PackageMetadata{
+				PackageType: Ptr("container"),
+				Container: &PackageContainerMetadata{
+					Tags: []string{"a"},
+				},
+			},
+			wantOk: true,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+
+			resValue, resOk := test.pv.GetMetadata()
+
+			if !reflect.DeepEqual(resValue, test.wantValue) || resOk != test.wantOk {
+				t.Errorf("PackageVersion.GetMetadata() - got: %v, %v; want: %v, %v", resValue, resOk, test.wantValue, test.wantOk)
+			}
+		})
+	}
+}
+
+func TestPackageVersion_GetRawMetadata(t *testing.T) {
+	t.Parallel()
+
+	tests := map[string]struct {
+		pv   *PackageVersion
+		want json.RawMessage
+	}{
+		"pv nil": {
+			pv:   nil,
+			want: nil,
+		},
+		"metadata nil": {
+			pv: &PackageVersion{
+				Metadata: nil,
+			},
+			want: json.RawMessage{},
+		},
+		"valid metadata": {
+			pv: &PackageVersion{
+				Metadata: json.RawMessage(`"a"`),
+			},
+			want: json.RawMessage(`"a"`),
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+
+			res := test.pv.GetRawMetadata()
+
+			if string(res) != string(test.want) {
+				t.Errorf("PackageVersion.GetRawMetadata() - got: %v; want: %v", res, test.want)
+			}
+		})
+	}
+}
+
 func TestPackage_Marshal(t *testing.T) {
 	t.Parallel()
 	testJSONMarshal(t, &Package{}, "{}")
@@ -403,8 +669,6 @@ func TestPackage_Marshal(t *testing.T) {
 		Name:        Ptr("name"),
 		PackageType: Ptr("pt"),
 		HTMLURL:     Ptr("hurl"),
-		CreatedAt:   &Timestamp{referenceTime},
-		UpdatedAt:   &Timestamp{referenceTime},
 		Visibility:  Ptr("private"),
 		Owner: &User{
 			Login:           Ptr("l"),
@@ -426,11 +690,16 @@ func TestPackage_Marshal(t *testing.T) {
 			CreatedAt:       &Timestamp{referenceTime},
 			SuspendedAt:     &Timestamp{referenceTime},
 		},
+		CreatedAt:   &Timestamp{referenceTime},
+		UpdatedAt:   &Timestamp{referenceTime},
+		Namespace:   Ptr("n"),
+		Description: Ptr("d"),
+		Ecosystem:   Ptr("e"),
 		PackageVersion: &PackageVersion{
 			ID:       Ptr(int64(1)),
 			Version:  Ptr("ver"),
 			Summary:  Ptr("sum"),
-			Body:     Ptr("body"),
+			Body:     json.RawMessage(`"body"`),
 			BodyHTML: Ptr("btnhtml"),
 			Release: &PackageRelease{
 				URL:             Ptr("url"),
@@ -537,6 +806,8 @@ func TestPackage_Marshal(t *testing.T) {
 			URL:      Ptr("url"),
 			Vendor:   Ptr("vendor"),
 		},
+		URL:          Ptr("url"),
+		VersionCount: Ptr(int64(1)),
 	}
 
 	want := `{
@@ -544,8 +815,6 @@ func TestPackage_Marshal(t *testing.T) {
 		"name": "name",
 		"package_type": "pt",
 		"html_url": "hurl",
-		"created_at": ` + referenceTimeStr + `,
-		"updated_at": ` + referenceTimeStr + `,
 		"visibility": "private",
 		"owner": {
 			"login": "l",
@@ -567,6 +836,11 @@ func TestPackage_Marshal(t *testing.T) {
 			"suspended_at": ` + referenceTimeStr + `,
 			"url": "u"
 		},
+		"created_at": ` + referenceTimeStr + `,
+		"updated_at": ` + referenceTimeStr + `,
+		"namespace": "n",
+		"description": "d",
+		"ecosystem": "e",
 		"package_version": {
 			"id": 1,
 			"version": "ver",
@@ -677,8 +951,341 @@ func TestPackage_Marshal(t *testing.T) {
 			"type": "type",
 			"url": "url",
 			"vendor": "vendor"
+		},
+		"url": "url",
+		"version_count": 1
+	}`
+
+	testJSONMarshal(t, o, want)
+}
+
+func TestPackageMetadata_Marshal(t *testing.T) {
+	t.Parallel()
+	testJSONMarshal(t, &PackageMetadata{}, "{}")
+
+	o := &PackageMetadata{
+		PackageType: Ptr("pt"),
+		Container: Ptr(PackageContainerMetadata{
+			Tags: []string{"a"},
+		}),
+	}
+
+	want := `{
+		"package_type": "pt",
+		"container": {
+			"tags": ["a"]
 		}
 	}`
 
 	testJSONMarshal(t, o, want)
 }
+
+func TestPackageContainerMetadata_Marshal(t *testing.T) {
+	t.Parallel()
+	testJSONMarshal(t, &PackageContainerMetadata{}, "{}")
+
+	o := &PackageContainerMetadata{
+		Tags: []string{"a", "b"},
+	}
+
+	want := `{
+		"tags": ["a","b"]
+	}`
+
+	testJSONMarshal(t, o, want)
+}
+
+func TestPackageVersionBody_Marshal(t *testing.T) {
+	t.Parallel()
+	testJSONMarshal(t, &PackageVersionBody{}, "{}")
+
+	o := &PackageVersionBody{
+		Repo: Ptr(Repository{
+			ID: Ptr(int64(1)),
+		}),
+		Info: Ptr(PackageVersionBodyInfo{
+			Type: Ptr("t"),
+		}),
+	}
+
+	want := `{
+		"repository": {
+			"id": 1
+		},
+		"info": {
+			"type": "t"
+		}
+	}`
+
+	testJSONMarshal(t, o, want)
+}
+
+func TestPackageVersionBodyInfo_Marshal(t *testing.T) {
+	t.Parallel()
+	testJSONMarshal(t, &PackageVersionBodyInfo{}, "{}")
+
+	o := &PackageVersionBodyInfo{
+		Type:       Ptr("t"),
+		OID:        Ptr("o"),
+		Mode:       Ptr(int64(1)),
+		Name:       Ptr("n"),
+		Path:       Ptr("p"),
+		Size:       Ptr(int64(1)),
+		Collection: Ptr(true),
+	}
+
+	want := `{
+		"type": "t",
+		"oid": "o",
+		"mode": 1,
+		"name": "n",
+		"path": "p",
+		"size": 1,
+		"collection": true
+	}`
+
+	testJSONMarshal(t, o, want)
+}
+
+func TestPackageEventContainerMetadata_Marshal(t *testing.T) {
+	t.Parallel()
+	testJSONMarshal(t, &PackageContainerMetadata{}, "{}")
+
+	o := &PackageEventContainerMetadata{
+		Labels: map[string]any{
+			"k": "v",
+		},
+		Manifest: map[string]any{
+			"k": 1,
+		},
+		Tag: &PackageEventContainerMetadataTag{
+			Name:   Ptr("n"),
+			Digest: Ptr("d"),
+		},
+	}
+
+	want := `{
+		"labels": {
+			"k": "v"
+		},
+		"manifest": {
+			"k": 1
+		},
+		"tag": {
+			"name": "n",
+			"digest": "d"
+		}
+	}`
+
+	testJSONMarshal(t, o, want)
+}
+
+func TestPackageEventContainerMetadataTag_Marshal(t *testing.T) {
+	t.Parallel()
+
+	testJSONMarshal(t, &PackageEventContainerMetadataTag{}, "{}")
+
+	o := &PackageEventContainerMetadataTag{
+		Name:   Ptr("n"),
+		Digest: Ptr("d"),
+	}
+
+	want := `{
+		"name": "n",
+		"digest": "d"
+	}`
+
+	testJSONMarshal(t, o, want)
+}
+
+func TestPackageNugetMetadata_Marshal(t *testing.T) {
+	t.Parallel()
+
+	o, _ := json.Marshal(map[string]string{
+		"k1": "v1",
+		"k2": "v2",
+	})
+
+	tests := map[string]struct {
+		input *PackageNugetMetadata
+		want  string
+	}{
+		"zero": {
+			input: &PackageNugetMetadata{},
+			want:  "{}",
+		},
+		"string": {
+			input: &PackageNugetMetadata{
+				ID:    json.RawMessage(`1`),
+				Name:  Ptr("n"),
+				Value: json.RawMessage(`"s"`),
+			},
+			want: `{
+				"id": 1,
+				"name": "n",
+				"value": "s"
+			}`,
+		},
+		"int": {
+			input: &PackageNugetMetadata{
+				ID:    json.RawMessage(`1`),
+				Name:  Ptr("n"),
+				Value: json.RawMessage(`1`),
+			},
+			want: `{
+				"id": 1,
+				"name": "n",
+				"value": 1
+			}`,
+		},
+		"object": {
+			input: &PackageNugetMetadata{
+				ID:    json.RawMessage(`1`),
+				Name:  Ptr("n"),
+				Value: o,
+			},
+			want: `{
+				"id": 1,
+				"name": "n",
+				"value": {
+					"k1": "v1",
+					"k2": "v2"
+				}
+			}`,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+			testJSONMarshal(t, test.input, test.want)
+		})
+	}
+}
+
+func TestPackageNPMMetadata_Marshal(t *testing.T) {
+	t.Parallel()
+
+	tests := map[string]struct {
+		input *PackageNPMMetadata
+		want  string
+	}{
+		"zero": {
+			input: &PackageNPMMetadata{},
+			want:  "{}",
+		},
+		"string": {
+			input: &PackageNPMMetadata{
+				Name:                 Ptr("n"),
+				Version:              Ptr("v"),
+				NPMUser:              Ptr("nu"),
+				Description:          Ptr("d"),
+				GitHead:              Ptr("gh"),
+				Homepage:             Ptr("h"),
+				License:              Ptr("l"),
+				Main:                 Ptr("m"),
+				ID:                   Ptr("id"),
+				NodeVersion:          Ptr("nv"),
+				NPMVersion:           Ptr("npmv"),
+				Readme:               Ptr("r"),
+				InstallationCommand:  Ptr("ic"),
+				CommitOID:            Ptr("coid"),
+				HasShrinkwrap:        Ptr(true),
+				PublishedViaActions:  Ptr(true),
+				ReleaseID:            Ptr(int64(1)),
+				DeletedByID:          Ptr(int64(1)),
+				Author:               map[string]string{"k1": "v1"},
+				Bugs:                 map[string]string{"k1": "v1"},
+				Dependencies:         map[string]string{"k1": "v1"},
+				DevDependencies:      map[string]string{"k1": "v1"},
+				PeerDependencies:     map[string]string{"k1": "v1"},
+				OptionalDependencies: map[string]string{"k1": "v1"},
+				Dist:                 map[string]string{"k1": "v1"},
+				Repository:           map[string]string{"k1": "v1"},
+				Engines:              map[string]string{"k1": "v1"},
+				Directories:          map[string]string{"k1": "v1"},
+				Scripts:              map[string]interface{}{"k1": 1},
+				Bin:                  map[string]interface{}{"k1": true},
+				Man:                  map[string]interface{}{"k1": "v1"},
+				Keywords:             []string{"kw1", "kw2"},
+				Files:                []string{"f1", "f2"},
+				OS:                   []string{"os1", "os2"},
+				CPU:                  []string{"cpu1", "cpu2"},
+				Maintainers:          []interface{}{"m1", "m2"},
+				Contributors:         []interface{}{"c1", "c2"},
+			},
+			want: `{
+				"name": "n",
+				"version": "v",
+				"npm_user": "nu",
+				"description": "d",
+				"git_head": "gh",
+				"homepage": "h",
+				"license": "l",
+				"main": "m",
+				"ID": "id",
+				"node_version": "nv",
+				"npm_version": "npmv",
+				"readme": "r",
+				"installation_command": "ic",
+				"commit_oid": "coid",
+				"has_shrinkwrap": true,
+				"published_via_actions": true,
+				"release_id": 1,
+				"deleted_by_id": 1,
+				"author": {
+					"k1": "v1"
+				},
+				"bugs": {
+					"k1": "v1"
+				},
+				"dependencies": {
+					"k1": "v1"
+				},
+				"dev_dependencies": {
+					"k1": "v1"
+				},
+				"peer_dependencies": {
+					"k1": "v1"
+				},
+				"optional_dependencies": {
+					"k1": "v1"
+				},
+				"dist": {
+					"k1": "v1"
+				},
+				"repository": {
+					"k1": "v1"
+				},
+				"engines": {
+					"k1": "v1"
+				},
+				"directories": {
+					"k1": "v1"
+				},
+				"scripts": {
+					"k1": 1
+				},
+				"bin": {
+					"k1": true
+				},
+				"man": {
+					"k1": "v1"
+				},
+				"keywords": ["kw1", "kw2"],
+				"files": ["f1", "f2"],
+				"os": ["os1", "os2"],
+				"cpu": ["cpu1", "cpu2"],
+				"maintainers": ["m1", "m2"],
+				"contributors": ["c1", "c2"]
+			}`,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+			testJSONMarshal(t, test.input, test.want)
+		})
+	}
+}
diff --git a/github/users_packages_test.go b/github/users_packages_test.go
index 3d1cffff..3776e94f 100644
--- a/github/users_packages_test.go
+++ b/github/users_packages_test.go
@@ -7,6 +7,7 @@ package github
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 	"net/http"
 	"testing"
@@ -340,6 +341,15 @@ func TestUsersService_Authenticated_ListPackagesVersions(t *testing.T) {
 	t.Parallel()
 	client, mux, _ := setup(t)
 
+	m := `{
+		"package_type": "container",
+		"container": {
+			"tags": [
+			"latest"
+			]
+		}
+	}`
+
 	mux.HandleFunc("/user/packages/container/hello_docker/versions", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
 		fmt.Fprint(w, `[
@@ -351,14 +361,7 @@ func TestUsersService_Authenticated_ListPackagesVersions(t *testing.T) {
 			  "created_at": `+referenceTimeStr+`,
 			  "updated_at": `+referenceTimeStr+`,
 			  "html_url": "https://github.com/users/octocat/packages/container/hello_docker/45763",
-			  "metadata": {
-				"package_type": "container",
-				"container": {
-				  "tags": [
-					"latest"
-				  ]
-				}
-			  }
+			  "metadata": `+m+`
 			}]`)
 	})
 
@@ -379,12 +382,7 @@ func TestUsersService_Authenticated_ListPackagesVersions(t *testing.T) {
 		CreatedAt:      &Timestamp{referenceTime},
 		UpdatedAt:      &Timestamp{referenceTime},
 		HTMLURL:        Ptr("https://github.com/users/octocat/packages/container/hello_docker/45763"),
-		Metadata: &PackageMetadata{
-			PackageType: Ptr("container"),
-			Container: &PackageContainerMetadata{
-				Tags: []string{"latest"},
-			},
-		},
+		Metadata:       json.RawMessage(m),
 	}}
 	if !cmp.Equal(packages, want) {
 		t.Errorf("Users.PackageGetAllVersions returned %+v, want %+v", packages, want)
@@ -409,6 +407,15 @@ func TestUsersService_specifiedUser_ListPackagesVersions(t *testing.T) {
 	t.Parallel()
 	client, mux, _ := setup(t)
 
+	m := `{
+		"package_type": "container",
+		"container": {
+			"tags": [
+			"latest"
+			]
+		}
+	}`
+
 	mux.HandleFunc("/users/u/packages/container/hello_docker/versions", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
 		fmt.Fprint(w, `[
@@ -420,14 +427,7 @@ func TestUsersService_specifiedUser_ListPackagesVersions(t *testing.T) {
 			  "created_at": `+referenceTimeStr+`,
 			  "updated_at": `+referenceTimeStr+`,
 			  "html_url": "https://github.com/users/octocat/packages/container/hello_docker/45763",
-			  "metadata": {
-				"package_type": "container",
-				"container": {
-				  "tags": [
-					"latest"
-				  ]
-				}
-			  }
+			  "metadata": `+m+`
 			}]`)
 	})
 
@@ -448,12 +448,7 @@ func TestUsersService_specifiedUser_ListPackagesVersions(t *testing.T) {
 		CreatedAt:      &Timestamp{referenceTime},
 		UpdatedAt:      &Timestamp{referenceTime},
 		HTMLURL:        Ptr("https://github.com/users/octocat/packages/container/hello_docker/45763"),
-		Metadata: &PackageMetadata{
-			PackageType: Ptr("container"),
-			Container: &PackageContainerMetadata{
-				Tags: []string{"latest"},
-			},
-		},
+		Metadata:       json.RawMessage(m),
 	}}
 	if !cmp.Equal(packages, want) {
 		t.Errorf("Users.specifiedUser_PackageGetAllVersions returned %+v, want %+v", packages, want)
@@ -478,6 +473,15 @@ func TestUsersService_Authenticated_PackageGetVersion(t *testing.T) {
 	t.Parallel()
 	client, mux, _ := setup(t)
 
+	m := `{
+		"package_type": "container",
+		"container": {
+		"tags": [
+			"latest"
+		]
+		}
+	}`
+
 	mux.HandleFunc("/user/packages/container/hello_docker/versions/45763", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
 		fmt.Fprint(w, `
@@ -489,14 +493,7 @@ func TestUsersService_Authenticated_PackageGetVersion(t *testing.T) {
 			  "created_at": `+referenceTimeStr+`,
 			  "updated_at": `+referenceTimeStr+`,
 			  "html_url": "https://github.com/users/octocat/packages/container/hello_docker/45763",
-			  "metadata": {
-				"package_type": "container",
-				"container": {
-				  "tags": [
-					"latest"
-				  ]
-				}
-			  }
+			  "metadata": `+m+`
 			}`)
 	})
 
@@ -514,12 +511,7 @@ func TestUsersService_Authenticated_PackageGetVersion(t *testing.T) {
 		CreatedAt:      &Timestamp{referenceTime},
 		UpdatedAt:      &Timestamp{referenceTime},
 		HTMLURL:        Ptr("https://github.com/users/octocat/packages/container/hello_docker/45763"),
-		Metadata: &PackageMetadata{
-			PackageType: Ptr("container"),
-			Container: &PackageContainerMetadata{
-				Tags: []string{"latest"},
-			},
-		},
+		Metadata:       json.RawMessage(m),
 	}
 	if !cmp.Equal(packages, want) {
 		t.Errorf("Users.Authenticated_PackageGetVersion returned %+v, want %+v", packages, want)
@@ -544,6 +536,15 @@ func TestUsersService_specifiedUser_PackageGetVersion(t *testing.T) {
 	t.Parallel()
 	client, mux, _ := setup(t)
 
+	m := `{
+		"package_type": "container",
+		"container": {
+			"tags": [
+			"latest"
+			]
+		}
+	}`
+
 	mux.HandleFunc("/users/u/packages/container/hello_docker/versions/45763", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
 		fmt.Fprint(w, `
@@ -555,14 +556,7 @@ func TestUsersService_specifiedUser_PackageGetVersion(t *testing.T) {
 			  "created_at": `+referenceTimeStr+`,
 			  "updated_at": `+referenceTimeStr+`,
 			  "html_url": "https://github.com/users/octocat/packages/container/hello_docker/45763",
-			  "metadata": {
-				"package_type": "container",
-				"container": {
-				  "tags": [
-					"latest"
-				  ]
-				}
-			  }
+			  "metadata": `+m+`
 			}`)
 	})
 
@@ -580,12 +574,7 @@ func TestUsersService_specifiedUser_PackageGetVersion(t *testing.T) {
 		CreatedAt:      &Timestamp{referenceTime},
 		UpdatedAt:      &Timestamp{referenceTime},
 		HTMLURL:        Ptr("https://github.com/users/octocat/packages/container/hello_docker/45763"),
-		Metadata: &PackageMetadata{
-			PackageType: Ptr("container"),
-			Container: &PackageContainerMetadata{
-				Tags: []string{"latest"},
-			},
-		},
+		Metadata:       json.RawMessage(m),
 	}
 	if !cmp.Equal(packages, want) {
 		t.Errorf("Users.specifiedUser_PackageGetVersion returned %+v, want %+v", packages, want)