diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index ac94083a2a1482d..56e4d01cba48925 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -22,9 +22,6 @@
/src/content/docs/workers-ai/ @rita3ko @craigsdennis @markdembo @mchenco @daisyfaithauma @cloudflare/pcx-technical-writing
/src/content/docs/vectorize/ @elithrar @vy-ton @sejoker @mchenco @cloudflare/pcx-technical-writing
/src/content/partials/vectorize/ @elithrar @mchenco @sejoker @cloudflare/pcx-technical-writing
-/src/content/changelogs/workers-ai.yaml @kathayl @G4brym @mchenco @daisyfaithauma @cloudflare/pcx-technical-writing
-/src/content/changelogs/ai-gateway.yaml @kathayl @G4brym @mchenco @daisyfaithauma @cloudflare/pcx-technical-writing
-/src/content/changelogs/vectorize.yaml @elithrar @mchenco @sejoker @cloudflare/pcx-technical-writing
# Analytics & Logs
@@ -45,8 +42,8 @@
# Changelogs
-/src/content/changelogs-next/ @cloudflare/pm-changelogs
-/src/assets/images/changelog-next/ @cloudflare/pm-changelogs
+/src/content/changelog/ @cloudflare/pm-changelogs
+/src/assets/images/changelog/ @cloudflare/pm-changelogs
# Cloudflare One
@@ -62,8 +59,6 @@
/src/content/docs/cloudflare-one/policies/data-loss-prevention/ @maxvp @cloudflare/pcx-technical-writing
/src/content/docs/cloudflare-one/insights/dex/ @deadlypants1973 @cloudflare/pcx-technical-writing
/src/content/docs/email-security/ @Maddy-Cloudflare @cloudflare/pcx-technical-writing
-/src/content/changelogs/access.yaml @ranbel @kennyj42 @cloudflare/pcx-technical-writing
-/src/content/changelogs/warp.yaml @kokolocomotion1 @ranbel @cloudflare/pcx-technical-writing
# Consumer products
@@ -92,42 +87,30 @@
/src/assets/images/calls/ @cloudflare/pcx-technical-writing @cloudflare/calls
/public/calls/ @cloudflare/pcx-technical-writing @cloudflare/calls
/src/content/docs/d1/ @elithrar @rozenmd @vy-ton @joshthoward @oxyjun @cloudflare/pcx-technical-writing
-/src/content/changelogs/d1.yaml @elithrar @rozenmd @vy-ton @joshthoward @oxyjun @cloudflare/pcx-technical-writing
/src/content/partials/d1/ @elithrar @rozenmd @vy-ton @joshthoward @oxyjun @cloudflare/pcx-technical-writing
/src/content/docs/durable-objects/ @elithrar @vy-ton @joshthoward @oxyjun @cloudflare/pcx-technical-writing
-/src/content/changelogs/durable-objects.yaml @elithrar @rozenmd @vy-ton @joshthoward @oxyjun @cloudflare/pcx-technical-writing
/src/content/docs/email-routing/ @cloudflare/pcx-technical-writing
/src/content/docs/hyperdrive/ @elithrar @thomasgauvin @sejoker @oxyjun @cloudflare/pcx-technical-writing
-/src/content/changelogs/hyperdrive.yaml @elithrar @thomasgauvin @sejoker @oxyjun @cloudflare/pcx-technical-writing
/src/content/partials/hyperdrive/ @elithrar @thomasgauvin @sejoker @oxyjun @cloudflare/pcx-technical-writing
-/src/content/changelogs/durable-objects.yaml @elithrar @vy-ton @joshthoward @oxyjun @cloudflare/pcx-technical-writing
/src/content/docs/images/ @dcpena @cloudflare/pcx-technical-writing @renandincer @third774
/src/content/docs/pages/ @cloudflare/workers-docs @GregBrimble @WalshyDev @aninibread @irvinebroque @cloudflare/pcx-technical-writing @ToriLindsay
/src/assets/images/pages/ @cloudflare/workers-docs @GregBrimble @WalshyDev @aninibread @cloudflare/pcx-technical-writing @ToriLindsay
-/src/content/changelogs/pages.yaml @cloudflare/workers-docs @GregBrimble @WalshyDev @aninibread @cloudflare/pcx-technical-writing
/src/content/docs/kv/ @elithrar @thomasgauvin @rts-rob @oxyjun @cloudflare/pcx-technical-writing
-/src/content/changelogs/kv.yaml @elithrar @thomasgauvin @rts-rob @oxyjun @cloudflare/pcx-technical-writing
/src/content/partials/kv/ @elithrar @thomasgauvin @rts-rob @oxyjun @cloudflare/pcx-technical-writing
/src/content/docs/pub-sub/ @elithrar @dcpena @cloudflare/pcx-technical-writing
/src/content/docs/queues/ @elithrar @toddmantell @maheshwarip @cloudflare/pcx-technical-writing
-/src/content/changelogs/queues.yaml @elithrar @toddmantell @maheshwarip @cloudflare/pcx-technical-writing
/src/content/docs/r2/ @oxyjun @elithrar @jonesphillip @cloudflare/workers-docs @cloudflare/pcx-technical-writing
-/src/content/changelogs/r2.yaml @oxyjun @elithrar @cloudflare/workers-docs @cloudflare/pcx-technical-writing
/src/content/docs/stream/ @tsmith512 @dcpena @cloudflare/pcx-technical-writing @renandincer @third774
-/src/content/changelogs/stream.yaml @tsmith512 @dcpena @cloudflare/pcx-technical-writing
/src/content/docs/workers/ @cloudflare/workers-docs @GregBrimble @irvinebroque @mikenomitch @WalshyDev @cloudflare/deploy-config @cloudflare/pcx-technical-writing @ToriLindsay
/src/content/partials/workers/ @cloudflare/workers-docs @GregBrimble @irvinebroque @mikenomitch @WalshyDev @cloudflare/deploy-config @cloudflare/pcx-technical-writing @ToriLindsay
/src/assets/images/workers/ @cloudflare/workers-docs @GregBrimble @irvinebroque @WalshyDev @cloudflare/deploy-config @cloudflare/pcx-technical-writing @ToriLindsay
-/src/content/changelogs/workers.yaml @cloudflare/workers-docs @GregBrimble @WalshyDev @aninibread @cloudflare/deploy-config @cloudflare/pcx-technical-writing @irvinebroque @mikenomitch
/src/content/docs/zaraz/ @bjesus @jonnyparris @simonabadoiu @cloudflare/pcx-technical-writing
-/src/content/changelogs/zaraz.yaml @bjesus @jonnyparris @simonabadoiu @cloudflare/pcx-technical-writing
/src/content/docs/workers/ci-cd/ @irvinebroque @aninibread @GregBrimble @ToriLindsay @cloudflare/pcx-technical-writing
/src/content/docs/workers/runtime-apis/ @irvinebroque @jasnell @mikenomitch @GregBrimble @ToriLindsay @cloudflare/pcx-technical-writing
/src/content/docs/workers/runtime-apis/bindings/ @irvinebroque @mikenomitch @GregBrimble @ToriLindsay @cloudflare/pcx-technical-writing
/src/content/docs/workers/platform/ @irvinebroque @GregBrimble @cloudflare/deploy-config @cloudflare/pcx-technical-writing
/src/content/docs/workers/configuration/compatibility-dates.mdx @irvinebroque @mikenomitch @GregBrimble @ToriLindsay @cloudflare/deploy-config @cloudflare/pcx-technical-writing
/src/content/docs/workers/configuration/compatibility-flags.mdx @irvinebroque @mikenomitch @GregBrimble @ToriLindsay @cloudflare/deploy-config @cloudflare/pcx-technical-writing
-
/src/content/docs/workers/reference/migrate-to-module-workers.mdx @irvinebroque @GregBrimble @ToriLindsay @cloudflare/deploy-config @cloudflare/pcx-technical-writing
/src/content/docs/workers/reference/security-model.mdx @irvinebroque @GregBrimble @ToriLindsay @cloudflare/pcx-technical-writing
/src/content/compatibility-flags/ @irvinebroque @mikenomitch @GregBrimble @cloudflare/pcx-technical-writing
@@ -144,8 +127,6 @@
/src/content/docs/ddos-protection/ @patriciasantaana @cloudflare/pcx-technical-writing
/src/content/docs/ddos-protection/change-log/ @antoinecordelle @patriciasantaana @cloudflare/pcx-technical-writing
-/src/content/changelogs/ddos-http.yaml @antoinecordelle @patriciasantaana @cloudflare/pcx-technical-writing
-/src/content/changelogs/ddos-network.yaml @antoinecordelle @patriciasantaana @cloudflare/pcx-technical-writing
# Docs team areas
@@ -184,7 +165,6 @@
# Radar
/src/content/docs/radar/ @meddulla @G4brym @tiagoad @andre-j3sus @cloudflare/pcx-technical-writing
-/src/content/changelogs/radar.yaml @meddulla @G4brym @tiagoad @andre-j3sus @cloudflare/pcx-technical-writing
# Reference architecture
@@ -202,7 +182,6 @@
/src/content/docs/ssl/ @RebeccaTamachiro @cloudflare/pcx-technical-writing
/src/content/docs/waf/ @pedrosousa @cloudflare/firewall @cloudflare/pcx-technical-writing
/src/content/docs/waf/change-log/ @pedrosousa @cloudflare/firewall @vs-mg @cloudflare/pcx-technical-writing
-/src/content/changelogs/waf.yaml @pedrosousa @cloudflare/firewall @vs-mg @cloudflare/pcx-technical-writing
# Support
@@ -216,7 +195,6 @@
# Waiting Room
/src/content/docs/waiting-room/ @angelampcosta @cloudflare/pcx-technical-writing
-/src/content/changelogs/waiting-room.yaml @angelampcosta @cloudflare/pcx-technical-writing
# Web Analytics
diff --git a/package-lock.json b/package-lock.json
index c3a9238ffad04dd..306d5f48a860310 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31,7 +31,7 @@
"@types/react-dom": "19.0.3",
"@typescript-eslint/parser": "8.24.0",
"algoliasearch": "5.20.1",
- "astro": "5.2.1",
+ "astro": "5.2.5",
"astro-breadcrumbs": "3.3.1",
"astro-icon": "1.1.5",
"astro-live-code": "0.0.5",
@@ -70,9 +70,12 @@
"rehype-autolink-headings": "7.1.0",
"rehype-external-links": "3.0.0",
"rehype-parse": "9.0.1",
+ "rehype-remark": "10.0.0",
"rehype-stringify": "10.0.1",
"rehype-title-figure": "0.1.2",
"remark": "15.0.1",
+ "remark-gfm": "4.0.1",
+ "remark-stringify": "11.0.0",
"sharp": "0.33.5",
"solarflare-theme": "0.0.2",
"starlight-image-zoom": "0.10.1",
@@ -488,9 +491,9 @@
"license": "MIT"
},
"node_modules/@astrojs/internal-helpers": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.5.0.tgz",
- "integrity": "sha512-CgB5ZaZO1PFG+rbjF3HnA7G6gIBjJ070xb7bUjeu5Gqqufma+t6fpuRWMXnK2iEO3zVyX7e/xplPlqtFKy/lvw==",
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.5.1.tgz",
+ "integrity": "sha512-M7rAge1n2+aOSxNvKUFa0u/KFn0W+sZy7EW91KOSERotm2Ti8qs+1K0xx3zbOxtAVrmJb5/J98eohVvvEqtNkw==",
"dev": true,
"license": "MIT"
},
@@ -3448,67 +3451,67 @@
]
},
"node_modules/@shikijs/core": {
- "version": "1.29.1",
- "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.29.1.tgz",
- "integrity": "sha512-Mo1gGGkuOYjDu5H8YwzmOuly9vNr8KDVkqj9xiKhhhFS8jisAtDSEWB9hzqRHLVQgFdA310e8XRJcW4tYhRB2A==",
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.29.2.tgz",
+ "integrity": "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@shikijs/engine-javascript": "1.29.1",
- "@shikijs/engine-oniguruma": "1.29.1",
- "@shikijs/types": "1.29.1",
+ "@shikijs/engine-javascript": "1.29.2",
+ "@shikijs/engine-oniguruma": "1.29.2",
+ "@shikijs/types": "1.29.2",
"@shikijs/vscode-textmate": "^10.0.1",
"@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.4"
}
},
"node_modules/@shikijs/engine-javascript": {
- "version": "1.29.1",
- "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.29.1.tgz",
- "integrity": "sha512-Hpi8k9x77rCQ7F/7zxIOUruNkNidMyBnP5qAGbLFqg4kRrg1HZhkB8btib5EXbQWTtLb5gBHOdBwshk20njD7Q==",
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.29.2.tgz",
+ "integrity": "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@shikijs/types": "1.29.1",
+ "@shikijs/types": "1.29.2",
"@shikijs/vscode-textmate": "^10.0.1",
"oniguruma-to-es": "^2.2.0"
}
},
"node_modules/@shikijs/engine-oniguruma": {
- "version": "1.29.1",
- "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.1.tgz",
- "integrity": "sha512-gSt2WhLNgEeLstcweQOSp+C+MhOpTsgdNXRqr3zP6M+BUBZ8Md9OU2BYwUYsALBxHza7hwaIWtFHjQ/aOOychw==",
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz",
+ "integrity": "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@shikijs/types": "1.29.1",
+ "@shikijs/types": "1.29.2",
"@shikijs/vscode-textmate": "^10.0.1"
}
},
"node_modules/@shikijs/langs": {
- "version": "1.29.1",
- "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-1.29.1.tgz",
- "integrity": "sha512-iERn4HlyuT044/FgrvLOaZgKVKf3PozjKjyV/RZ5GnlyYEAZFcgwHGkYboeBv2IybQG1KVS/e7VGgiAU4JY2Gw==",
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-1.29.2.tgz",
+ "integrity": "sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@shikijs/types": "1.29.1"
+ "@shikijs/types": "1.29.2"
}
},
"node_modules/@shikijs/themes": {
- "version": "1.29.1",
- "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-1.29.1.tgz",
- "integrity": "sha512-lb11zf72Vc9uxkl+aec2oW1HVTHJ2LtgZgumb4Rr6By3y/96VmlU44bkxEb8WBWH3RUtbqAJEN0jljD9cF7H7g==",
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-1.29.2.tgz",
+ "integrity": "sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@shikijs/types": "1.29.1"
+ "@shikijs/types": "1.29.2"
}
},
"node_modules/@shikijs/types": {
- "version": "1.29.1",
- "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.29.1.tgz",
- "integrity": "sha512-aBqAuhYRp5vSir3Pc9+QPu9WESBOjUo03ao0IHLC4TyTioSsp/SkbAZSrIH4ghYYC1T1KTEpRSBa83bas4RnPA==",
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.29.2.tgz",
+ "integrity": "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5167,14 +5170,14 @@
}
},
"node_modules/astro": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/astro/-/astro-5.2.1.tgz",
- "integrity": "sha512-OYR2kUo9EqX6OYZ1OmM14xP8mjFwgrk1FzIr+3K3tS0gCCKJsXtfboCUhX3lODZFIsmY/on7NPZd+2PURA0R2Q==",
+ "version": "5.2.5",
+ "resolved": "https://registry.npmjs.org/astro/-/astro-5.2.5.tgz",
+ "integrity": "sha512-AYXyYkc+c5xbKTm48FyQA91y81nXyNPAaoyafR0LUugE4lAwuvIUcXDBfMzmbuP1lGRvsE33G2oypv6gbGaPFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@astrojs/compiler": "^2.10.3",
- "@astrojs/internal-helpers": "0.5.0",
+ "@astrojs/internal-helpers": "0.5.1",
"@astrojs/markdown-remark": "6.1.0",
"@astrojs/telemetry": "3.2.0",
"@oslojs/encoding": "^1.1.0",
@@ -5201,7 +5204,7 @@
"fast-glob": "^3.3.3",
"flattie": "^1.1.1",
"github-slugger": "^2.0.0",
- "html-escaper": "^3.0.3",
+ "html-escaper": "3.0.3",
"http-cache-semantics": "^4.1.1",
"js-yaml": "^4.1.0",
"kleur": "^4.1.5",
@@ -5211,12 +5214,12 @@
"mrmime": "^2.0.0",
"neotraverse": "^0.6.18",
"p-limit": "^6.2.0",
- "p-queue": "^8.0.1",
- "preferred-pm": "^4.0.0",
+ "p-queue": "^8.1.0",
+ "preferred-pm": "^4.1.1",
"prompts": "^2.4.2",
"rehype": "^13.0.2",
- "semver": "^7.6.3",
- "shiki": "^1.29.1",
+ "semver": "^7.7.1",
+ "shiki": "^1.29.2",
"tinyexec": "^0.3.2",
"tsconfck": "^3.1.4",
"ultrahtml": "^1.5.3",
@@ -5225,10 +5228,10 @@
"vfile": "^6.0.3",
"vite": "^6.0.11",
"vitefu": "^1.0.5",
- "which-pm": "^3.0.0",
+ "which-pm": "^3.0.1",
"xxhash-wasm": "^1.1.0",
"yargs-parser": "^21.1.1",
- "yocto-spinner": "^0.1.2",
+ "yocto-spinner": "^0.2.0",
"zod": "^3.24.1",
"zod-to-json-schema": "^3.24.1",
"zod-to-ts": "^1.2.0"
@@ -10157,6 +10160,33 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/hast-util-to-mdast": {
+ "version": "10.1.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-mdast/-/hast-util-to-mdast-10.1.2.tgz",
+ "integrity": "sha512-FiCRI7NmOvM4y+f5w32jPRzcxDIz+PUqDwEqn1A+1q2cdp3B8Gx7aVrXORdOKjMNDQsD1ogOr896+0jJHW1EFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-phrasing": "^3.0.0",
+ "hast-util-to-html": "^9.0.0",
+ "hast-util-to-text": "^4.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "rehype-minify-whitespace": "^6.0.0",
+ "trim-trailing-lines": "^2.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/hast-util-to-parse5": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz",
@@ -11644,13 +11674,6 @@
"node": ">=6"
}
},
- "node_modules/load-yaml-file/node_modules/sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
- "dev": true,
- "license": "BSD-3-Clause"
- },
"node_modules/local-pkg": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz",
@@ -13640,9 +13663,9 @@
}
},
"node_modules/p-queue": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.0.1.tgz",
- "integrity": "sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.0.tgz",
+ "integrity": "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -14278,15 +14301,15 @@
}
},
"node_modules/preferred-pm": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-4.0.0.tgz",
- "integrity": "sha512-gYBeFTZLu055D8Vv3cSPox/0iTPtkzxpLroSYYA7WXgRi31WCJ51Uyl8ZiPeUUjyvs2MBzK+S8v9JVUgHU/Sqw==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-4.1.1.tgz",
+ "integrity": "sha512-rU+ZAv1Ur9jAUZtGPebQVQPzdGhNzaEiQ7VL9+cjsAWPHFYOccNXPNiev1CCDSOg/2j7UujM7ojNhpkuILEVNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"find-up-simple": "^1.0.0",
"find-yarn-workspace-root2": "1.2.16",
- "which-pm": "^3.0.0"
+ "which-pm": "^3.0.1"
},
"engines": {
"node": ">=18.12"
@@ -14824,6 +14847,21 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/rehype-minify-whitespace": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-6.0.2.tgz",
+ "integrity": "sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-minify-whitespace": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/rehype-parse": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz",
@@ -14872,6 +14910,24 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/rehype-remark": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-remark/-/rehype-remark-10.0.0.tgz",
+ "integrity": "sha512-+aDXY/icqMFOafJQomVjxe3BAP7aR3lIsQ3GV6VIwpbCD2nvNFOXjGvotMe5p0Ny+Gt6L13DhEf/FjOOpTuUbQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "hast-util-to-mdast": "^10.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/rehype-stringify": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz",
@@ -15058,9 +15114,9 @@
}
},
"node_modules/remark-gfm": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz",
- "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -15550,9 +15606,9 @@
"license": "MIT"
},
"node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -15675,18 +15731,18 @@
}
},
"node_modules/shiki": {
- "version": "1.29.1",
- "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.1.tgz",
- "integrity": "sha512-TghWKV9pJTd/N+IgAIVJtr0qZkB7FfFCUrrEJc0aRmZupo3D1OCVRknQWVRVA7AX/M0Ld7QfoAruPzr3CnUJuw==",
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.2.tgz",
+ "integrity": "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@shikijs/core": "1.29.1",
- "@shikijs/engine-javascript": "1.29.1",
- "@shikijs/engine-oniguruma": "1.29.1",
- "@shikijs/langs": "1.29.1",
- "@shikijs/themes": "1.29.1",
- "@shikijs/types": "1.29.1",
+ "@shikijs/core": "1.29.2",
+ "@shikijs/engine-javascript": "1.29.2",
+ "@shikijs/engine-oniguruma": "1.29.2",
+ "@shikijs/langs": "1.29.2",
+ "@shikijs/themes": "1.29.2",
+ "@shikijs/types": "1.29.2",
"@shikijs/vscode-textmate": "^10.0.1",
"@types/hast": "^3.0.4"
}
@@ -15910,6 +15966,13 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/stackback": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -16716,6 +16779,17 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/trim-trailing-lines": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-2.1.0.tgz",
+ "integrity": "sha512-5UR5Biq4VlVOtzqkm2AZlgvSlDJtME46uV0br0gENbwN4l5+mMKT4b9gJKqWtuL2zAIqajGJGuvbCbcAJUZqBg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/trough": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
@@ -18913,9 +18987,9 @@
}
},
"node_modules/which-pm": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-3.0.0.tgz",
- "integrity": "sha512-ysVYmw6+ZBhx3+ZkcPwRuJi38ZOTLJJ33PSHaitLxSKUMsh0LkKd0nC69zZCwt5D+AYUcMK2hhw4yWny20vSGg==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-3.0.1.tgz",
+ "integrity": "sha512-v2JrMq0waAI4ju1xU5x3blsxBBMgdgZve580iYMN5frDaLGjbA24fok7wKCsya8KLVO19Ju4XDc5+zTZCJkQfg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -19460,9 +19534,9 @@
}
},
"node_modules/yocto-spinner": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.1.2.tgz",
- "integrity": "sha512-VfmLIh/ZSZOJnVRQZc/dvpPP90lWL4G0bmxQMP0+U/2vKBA8GSpcBuWv17y7F+CZItRuO97HN1wdbb4p10uhOg==",
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.0.tgz",
+ "integrity": "sha512-Qu6WAqNLGleB687CCGcmgHIo8l+J19MX/32UrSMfbf/4L8gLoxjpOYoiHT1asiWyqvjRZbgvOhLlvne6E5Tbdw==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index aede6cc92b7d684..97450da9ba03692 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
"@types/react-dom": "19.0.3",
"@typescript-eslint/parser": "8.24.0",
"algoliasearch": "5.20.1",
- "astro": "5.2.1",
+ "astro": "5.2.5",
"astro-breadcrumbs": "3.3.1",
"astro-icon": "1.1.5",
"astro-live-code": "0.0.5",
@@ -87,9 +87,12 @@
"rehype-autolink-headings": "7.1.0",
"rehype-external-links": "3.0.0",
"rehype-parse": "9.0.1",
+ "rehype-remark": "10.0.0",
"rehype-stringify": "10.0.1",
"rehype-title-figure": "0.1.2",
"remark": "15.0.1",
+ "remark-gfm": "4.0.1",
+ "remark-stringify": "11.0.0",
"sharp": "0.33.5",
"solarflare-theme": "0.0.2",
"starlight-image-zoom": "0.10.1",
diff --git a/public/_redirects b/public/_redirects
index c908414ac18ed37..3d7ae0560ac2ec3 100644
--- a/public/_redirects
+++ b/public/_redirects
@@ -4,9 +4,186 @@
/dashboard-landing/ / 301
/tutorials/ /search/?content_type%5B0%5D=Tutorial 301
/sitemap.xml /sitemap-index.xml
-/changelog/index.xml /release-notes/index.xml 301
/deprecations/ /fundamentals/api/reference/deprecations/ 301
+# changelogs
+
+## legacy
+/release-notes/ /changelog/ 301
+/release-notes/index.xml /changelog/rss/index.xml 301
+
+## rss
+/changelog/index.xml /changelog/rss/index.xml 301
+/changelog/rss.xml /changelog/rss/index.xml 301
+
+### areas
+/cloudflare-one/changelog/index.xml /changelog/rss/cloudflare-one.xml 301
+/fundamentals/reference/changelog/performance/index.xml /changelog/rss/application-performance.xml 301
+/fundamentals/reference/changelog/platform/index.xml /changelog/rss/core-platform.xml 301
+/fundamentals/reference/changelog/security/index.xml /changelog/rss/application-security.xml 301
+/workers/platform/changelog/platform/index.xml /changelog/rss/developer-platform.xml 301
+
+### products
+/cloudflare-one/changelog/access/index.xml /changelog/rss/access.xml 301
+/ai-gateway/changelog/index.xml /changelog/rss/ai-gateway.xml 301
+/fundamentals/api/reference/deprecations/index.xml /changelog/rss/api-deprecations.xml 301
+/api-shield/changelog/index.xml /changelog/rss/api-shield.xml 301
+/web-analytics/changelog/index.xml /changelog/rss/beacon-min-js.xml 301
+/bots/changelog/index.xml /changelog/rss/bots.xml 301
+/cloudflare-one/changelog/browser-isolation/index.xml /changelog/rss/browser-isolation.xml 301
+/browser-rendering/changelog/index.xml /changelog/rss/browser-rendering.xml 301
+/byoip/changelog/index.xml /changelog/rss/byoip.xml 301
+/cache/changelog/index.xml /changelog/rss/cache.xml 301
+/calls/changelog/index.xml /changelog/rss/calls.xml 301
+/cloudflare-one/changelog/casb/index.xml /changelog/rss/casb.xml 301
+/d1/platform/changelog/index.xml /changelog/rss/d1.xml 301
+
+/workers/platform/changelog/wrangler/ https://github.com/cloudflare/workers-sdk/releases 301
+/workers/platform/changelog/wrangler/index.xml https://github.com/cloudflare/workers-sdk/releases.atom 301
+
+# todo
+
+# ddos-http.yaml
+# "/ddos-protection/change-log/http/"
+
+# ddos-network.yaml
+# "/ddos-protection/change-log/network/"
+
+# ddos.yaml
+# "/ddos-protection/change-log/general-updates/"
+
+# dex.yaml
+# "/cloudflare-one/changelog/dex/"
+
+# dlp.yaml
+# "/cloudflare-one/changelog/dlp/"
+
+# dns.yaml
+# "/dns/changelog/"
+
+# durable-objects.yaml
+# "/durable-objects/changelog/"
+
+# email-security.yaml
+# "/cloudflare-one/changelog/email-security/"
+
+# fundamentals.yaml
+# "/fundamentals/changelog/"
+
+# gateway.yaml
+# "/cloudflare-one/changelog/gateway/"
+
+# hyperdrive.yaml
+# "/hyperdrive/platform/changelog/"
+
+# images.yaml
+# "/images/platform/changelog/"
+
+# kv.yaml
+# "/kv/platform/changelog/"
+
+# load-balancing.yaml
+# "/load-balancing/changelog/"
+
+# logs.yaml
+# "/logs/changelog/"
+
+# magic-cloud-networking.yaml
+# "/magic-cloud-networking/changelog/"
+
+# magic-firewall.yaml
+# "/magic-firewall/changelog/"
+
+# magic-network-monitoring.yaml
+# "/magic-network-monitoring/changelog/"
+
+# magic-transit.yaml
+# "/magic-transit/changelog/"
+
+# magic-wan.yaml
+# "/magic-wan/changelog/"
+
+# network-interconnect.yaml
+# "/network-interconnect/changelog/"
+
+# notifications.yaml
+# "/notifications/changelog/"
+
+# pages.yaml
+# "/pages/platform/changelog/"
+
+# queues.yaml
+# "/queues/platform/changelog/"
+
+# r2.yaml
+# "/r2/platform/changelog/"
+
+# radar.yaml
+# "/radar/changelog/"
+
+# risk-score.yaml
+# "/cloudflare-one/changelog/risk-score/"
+
+# rules.yaml
+# "/rules/changelog/"
+
+# security-center.yaml
+# "/security-center/changelog/"
+
+# spectrum.yaml
+# "/spectrum/changelog/"
+
+# ssl.yaml
+# "/ssl/changelog/"
+
+# stream.yaml
+# "/stream/changelog/"
+
+# tenant.yaml
+# "/tenant/changelog/"
+
+# trace.yaml
+# "/fundamentals/security/trace-request/changelog/"
+
+# tunnel.yaml
+# "/cloudflare-one/changelog/tunnel/"
+
+# turnstile.yaml
+# "/turnstile/changelog/"
+
+# vectorize.yaml
+# "/vectorize/platform/changelog/"
+
+# version-management.yaml
+# "/version-management/changelog/"
+
+# waf-general.yaml
+# "/waf/change-log/general-updates/"
+
+# waf.yaml
+# "/waf/change-log/"
+
+# waiting-room.yaml
+# "/waiting-room/changelog/"
+
+# warp.yaml
+# "/cloudflare-one/changelog/warp/"
+
+# workers-ai.yaml
+# "/workers-ai/changelog/"
+
+# workers-for-platforms.yaml
+# "/cloudflare-for-platforms/workers-for-platforms/platform/changelog/"
+
+# workers.yaml
+# "/workers/platform/changelog/"
+
+# workflows.yaml
+# "/workflows/reference/changelog/"
+
+# zaraz.yaml
+# "/zaraz/changelog/"
+
# 1dot1_redirect
/1.1.1.1/1.1.1.1-for-families/ /1.1.1.1/setup/ 301
/1.1.1.1/1.1.1.1-for-families/android/ /1.1.1.1/setup/android/ 301
diff --git a/src/assets/images/changelog-next/2024-02-03-terraform-v5-screenshot.png b/src/assets/images/changelog/2024-02-03-terraform-v5-screenshot.png
similarity index 100%
rename from src/assets/images/changelog-next/2024-02-03-terraform-v5-screenshot.png
rename to src/assets/images/changelog/2024-02-03-terraform-v5-screenshot.png
diff --git a/src/assets/images/changelog-next/hero.svg b/src/assets/images/changelog/hero.svg
similarity index 100%
rename from src/assets/images/changelog-next/hero.svg
rename to src/assets/images/changelog/hero.svg
diff --git a/src/components/ProductChangelog.astro b/src/components/ProductChangelog.astro
index 2c2274400ea3e91..fde22ac51842cd0 100644
--- a/src/components/ProductChangelog.astro
+++ b/src/components/ProductChangelog.astro
@@ -1,133 +1,40 @@
---
-import { getEntry, type CollectionEntry } from "astro:content";
-import { marked } from "marked";
-import { getChangelogs } from "~/util/changelogs";
-import AnchorHeading from "~/components/AnchorHeading.astro";
-import { entryToString } from "~/util/container";
+import { getChangelogs, type GetChangelogsOptions } from "~/util/changelog";
+import { reference } from "astro:content";
+import { z } from "astro:schema";
+import { getCollection } from "astro:content";
-const page = await getEntry("docs", Astro.params.slug!);
+const props = z.object({
+ products: z.array(reference("products")),
+}).or(z.object({
+ area: z.string()
+}));
-if (!page) {
- throw new Error(
- `[ProductChangelog] Failed to find entry for ${Astro.params.slug}.`,
- );
-}
-
-if (!page.data.changelog_file_name && !page.data.changelog_product_area_name) {
- throw new Error(
- `[ProductChangelog] ${Astro.params.slug} does not have a 'changelog_file_name' or 'changaelog_product_area_name' frontmatter property.`,
- );
-}
-
-if (page.data.changelog_file_name && page.data.changelog_file_name.length > 1) {
- throw new Error(
- `[ProductChangelog] This component cannot be used on files that have more than 1 entry in their 'changelog_file_name' frontmatter property.`,
- );
-}
+const input = await props.parseAsync(Astro.props);
-const name =
- page.data.changelog_product_area_name ?? page.data.changelog_file_name?.[0];
+let filter: GetChangelogsOptions["filter"];
-let changelogs;
-
-if (page.data.changelog_product_area_name) {
- const opts = {
- filter: (entry: CollectionEntry<"changelogs">) => {
- return entry.data.productArea === name;
- },
- };
- ({ changelogs } = await getChangelogs(opts));
+if ("products" in input) {
+ filter = (e) => {
+ return e.data.products.some((x) => input.products.some((y) => x.id === y.id))
+ }
} else {
- if (name === "wrangler") {
- const opts = {
- wranglerOnly: true,
- };
- ({ changelogs } = await getChangelogs(opts));
- } else if (name === "api-deprecations") {
- const opts = {
- deprecationsOnly: true,
- };
- ({ changelogs } = await getChangelogs(opts));
- } else {
- const opts = {
- filter: (entry: CollectionEntry<"changelogs">) => {
- return entry.id === name;
- },
- };
- ({ changelogs } = await getChangelogs(opts));
+ const products = await getCollection("products", (e) => {
+ return e.data.product.group === input.area;
+ });
+
+ filter = (e) => {
+ return e.data.products.some((x) => products.some((y) => x.id === y.id))
}
}
-if (!changelogs) {
- throw new Error(
- `[ProductChangelog] Failed to find changelog called ${name}.`,
- );
-}
+const changelogs = await getChangelogs({ filter });
---
{
- page.data.pcx_content_type === "changelog" && (
-
-
- Subscribe to RSS
-
-
- )
-}
-{
- changelogs.map(([date, entries]) => (
-
- {(entries ?? []).map(async (entry) => {
- let description;
- if (entry.individual_page) {
- const link = entry.individual_page;
-
- if (!link)
- throw new Error(
- `Changelog entry points to individual page but no link is provided`,
- );
-
- const page = await getEntry("docs", link.slice(1, -1));
-
- if (!page)
- throw new Error(
- `Changelog entry points to ${link.slice(1, -1)} but unable to find entry with that slug`,
- );
-
- description = (await entryToString(page, Astro.locals)) ?? page.body;
-
- return (
-
-
-
-
-
{entry.date}
- {page.data.changelog_product_area_name && (
-
- )}
- {
}
-
- );
- } else {
- description = marked.parse(entry.description as string);
- return (
- <>
-
-
- {page.data.changelog_product_area_name && (
-
- )}
- {entry.title &&
{entry.title}}
- {
}
-
- >
- );
- }
- })}
-
+ changelogs.map((entry) => (
+ {entry.data.date.toISOString().slice(0, 10)}
+ {entry.data.description}
+ For more information, refer to the dedicated changelog post.
))
-}
+}
\ No newline at end of file
diff --git a/src/components/changelog-next/Header.astro b/src/components/changelog/Header.astro
similarity index 89%
rename from src/components/changelog-next/Header.astro
rename to src/components/changelog/Header.astro
index 1083309fd6d8e83..8886a3ef34bd54f 100644
--- a/src/components/changelog-next/Header.astro
+++ b/src/components/changelog/Header.astro
@@ -4,7 +4,7 @@ import { z } from "astro:schema";
import { getEntry, type CollectionEntry } from "astro:content";
import { StarlightIcon } from "..";
-import HeroImage from "~/assets/images/changelog-next/hero.svg";
+import HeroImage from "~/assets/images/changelog/hero.svg";
type Props = z.input;
@@ -14,9 +14,7 @@ const props = z.object({
const { notes } = props.parse(Astro.props);
-async function uniqueProducts(
- notes: Array>,
-) {
+async function uniqueProducts(notes: Array>) {
const unique = [
...new Set(notes.flatMap((e) => e.data.products.map((p) => p.id))),
];
@@ -49,7 +47,7 @@ const products = await uniqueProducts(notes);
{
products.length > 0 && (
-
+
+
+
diff --git a/src/pages/release-notes/index.xml.ts b/src/pages/release-notes/index.xml.ts
deleted file mode 100644
index e463d5fb7b660e4..000000000000000
--- a/src/pages/release-notes/index.xml.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import rss from "@astrojs/rss";
-import { getCollection, getEntry } from "astro:content";
-import type { APIRoute } from "astro";
-import { marked, type Token } from "marked";
-import { getWranglerChangelog } from "~/util/changelogs";
-import { slug } from "github-slugger";
-import { entryToString } from "~/util/container";
-
-export const GET: APIRoute = async (context) => {
- function walkTokens(token: Token) {
- if (token.type === "image" || token.type === "link") {
- if (token.href.startsWith("/")) {
- token.href = context.site + token.href.slice(1);
- }
- }
- }
-
- marked.use({ walkTokens });
-
- const changelogs = await getCollection("changelogs");
-
- changelogs.push(await getWranglerChangelog());
-
- const mapped = await Promise.all(
- changelogs.flatMap((product) => {
- return product.data.entries.map(async (entry) => {
- let description;
- if (entry.individual_page) {
- const link = entry.link;
-
- if (!link)
- throw new Error(
- `Changelog entry points to individual page but no link is provided`,
- );
-
- const page = await getEntry("docs", link.slice(1, -1));
-
- if (!page)
- throw new Error(
- `Changelog entry points to ${link.slice(1, -1)} but unable to find entry with that slug`,
- );
-
- description =
- (await entryToString(page, context.locals)) ?? page.body;
- } else {
- description = entry.description;
- }
-
- let link;
- if (entry.link) {
- link = entry.link;
- } else {
- const anchor = slug(entry.title ?? entry.publish_date);
- link = product.data.link.concat(`#${anchor}`);
- }
-
- let title;
- if (entry.scheduled) {
- title = `Scheduled for ${entry.scheduled_date}`;
- } else {
- title = entry.title;
- }
-
- return {
- product: product.data.productName,
- link,
- date: entry.publish_date,
- description,
- title,
- };
- });
- }),
- );
-
- const entries = mapped.sort((a, b) => {
- return a.date < b.date ? 1 : a.date > b.date ? -1 : 0;
- });
-
- return rss({
- title: `Cloudflare release notes`,
- description: `Updates to various Cloudflare products.`,
- site: "https://developers.cloudflare.com/release-notes/",
- trailingSlash: false,
- items: entries.map((entry) => {
- return {
- title: `${entry.product} - ${entry.title ?? entry.date}`,
- description: marked.parse(entry.description ?? "", {
- async: false,
- }) as string,
- pubDate: new Date(entry.date),
- link: entry.link,
- customData: `${entry.product}`,
- };
- }),
- });
-};
diff --git a/src/plugins/rehype/base-url.ts b/src/plugins/rehype/base-url.ts
new file mode 100644
index 000000000000000..a7f26e45f259529
--- /dev/null
+++ b/src/plugins/rehype/base-url.ts
@@ -0,0 +1,20 @@
+import { visit } from "unist-util-visit";
+import type { Root } from "hast";
+
+export default function () {
+ return function (tree: Root) {
+ visit(tree, "element", function (element) {
+ if (element.tagName === "a") {
+ const href = element.properties.href as string | undefined;
+
+ if (href) {
+ if (href.startsWith("/")) {
+ const url = new URL(href, "https://developers.cloudflare.com/");
+
+ element.properties.href = url.href;
+ }
+ }
+ }
+ });
+ };
+}
diff --git a/src/plugins/rehype/filter-elements.ts b/src/plugins/rehype/filter-elements.ts
new file mode 100644
index 000000000000000..251db3eb0933e06
--- /dev/null
+++ b/src/plugins/rehype/filter-elements.ts
@@ -0,0 +1,142 @@
+import { SKIP, visit, type VisitorResult } from "unist-util-visit";
+import type { Root, Element, Parents } from "hast";
+
+const remove = (index: number, parent: Parents): VisitorResult => {
+ parent.children.splice(index, 1);
+ return [SKIP, index];
+};
+
+const unwrap = (
+ index: number,
+ parent: Parents,
+ element: Element,
+): VisitorResult => {
+ parent.children.splice(index, 1, ...element.children);
+ return [SKIP, index];
+};
+
+const ALLOWED_ELEMENTS = [
+ // Content sectioning
+ "address",
+ "article",
+ "aside",
+ "footer",
+ "header",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "hgroup",
+ "main",
+ "nav",
+ "section",
+ // Text content
+ "blockquote",
+ "dd",
+ "div",
+ "dl",
+ "dt",
+ "figcaption",
+ "figure",
+ "hr",
+ "li",
+ "menu",
+ "ol",
+ "p",
+ "pre",
+ "ul",
+ // Inline text semantics
+ "a",
+ "abbr",
+ "b",
+ "bdi",
+ "bdo",
+ "br",
+ "cite",
+ "code",
+ "data",
+ "dfn",
+ "em",
+ "i",
+ "kbd",
+ "mark",
+ "q",
+ "rb",
+ "rp",
+ "rt",
+ "rtc",
+ "ruby",
+ "s",
+ "samp",
+ "small",
+ "span",
+ "strong",
+ "sub",
+ "sup",
+ "time",
+ "u",
+ "var",
+ "wbr",
+ // Table content
+ "caption",
+ "col",
+ "colgroup",
+ "table",
+ "tbody",
+ "td",
+ "tfoot",
+ "th",
+ "thead",
+ "tr",
+ // Custom elements
+ "rule-id",
+];
+
+const ALLOWED_ATTRIBUTES: Record = {
+ a: ["href", "id", "target"],
+ "rule-id": ["id"],
+};
+
+const UNWRAP_CLASS_NAMES = ["heading-wrapper"];
+
+const DISALLOWED_CLASS_NAMES = ["external-link", "anchor-link"];
+
+export default function () {
+ return function (tree: Root) {
+ visit(tree, "element", function (element, index, parent) {
+ if (typeof index === "number" && parent) {
+ const tag = element.tagName;
+ const classNames = (element.properties.className as string[]) ?? [];
+
+ if (!ALLOWED_ELEMENTS.includes(tag)) {
+ return remove(index, parent);
+ }
+
+ if (DISALLOWED_CLASS_NAMES.some((v) => classNames.includes(v))) {
+ return remove(index, parent);
+ }
+
+ if (UNWRAP_CLASS_NAMES.some((v) => classNames.includes(v))) {
+ return unwrap(index, parent, element);
+ }
+
+ for (const key of Object.keys(element.properties)) {
+ if (!ALLOWED_ATTRIBUTES[tag]?.includes(key)) {
+ delete element.properties[key];
+ }
+ }
+
+ if (tag === "rule-id") {
+ return unwrap(index, parent, {
+ ...element,
+ children: [
+ { type: "text", value: element.properties.id as string },
+ ],
+ });
+ }
+ }
+ });
+ };
+}
diff --git a/src/schemas/base.ts b/src/schemas/base.ts
index 03d5f4a5fb4e035..66b66cbce14ee69 100644
--- a/src/schemas/base.ts
+++ b/src/schemas/base.ts
@@ -66,8 +66,6 @@ export const baseSchema = z.object({
"This is used to automatically add the LastReviewed component to a page. Refer to https://developers.cloudflare.com/style-guide/components/last-reviewed/.",
),
spotlight: spotlightAuthorDetails,
- changelog_file_name: z.string().array().optional(),
- changelog_product_area_name: z.string().optional(),
products: z.string().array().optional(),
languages: z.string().array().optional(),
summary: z.string().optional(),
diff --git a/src/schemas/changelogs-next.ts b/src/schemas/changelog.ts
similarity index 51%
rename from src/schemas/changelogs-next.ts
rename to src/schemas/changelog.ts
index f1ef260920bb4da..57a0efd7e47d461 100644
--- a/src/schemas/changelogs-next.ts
+++ b/src/schemas/changelog.ts
@@ -1,9 +1,15 @@
import { reference } from "astro:content";
import { z } from "astro:schema";
-export const changelogsNextSchema = z.object({
+export const changelogSchema = z.object({
title: z.string(),
description: z.string(),
date: z.coerce.date(),
products: z.array(reference("products")),
+ link: z
+ .string()
+ .optional()
+ .describe(
+ 'Please do not use the "link" property in changelog entry frontmatter, it is reserved.',
+ ),
});
diff --git a/src/schemas/changelogs.ts b/src/schemas/changelogs.ts
deleted file mode 100644
index a637d8ecd70e976..000000000000000
--- a/src/schemas/changelogs.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { z } from "astro:schema";
-
-export const changelogsSchema = z.object({
- link: z.string(),
- productName: z.string(),
- productLink: z.string(),
- productArea: z.string(),
- productAreaLink: z.string(),
- entries: z
- .object({
- publish_date: z.string(),
- title: z.string().optional(),
- description: z.string().optional(),
- individual_page: z.boolean().optional(),
- link: z.string().optional(),
- scheduled: z.boolean().optional(),
- scheduled_date: z.string().optional(),
- })
- .array(),
-});
diff --git a/src/schemas/index.ts b/src/schemas/index.ts
index 1e47fcdc05af01b..a85e9a1574c59c4 100644
--- a/src/schemas/index.ts
+++ b/src/schemas/index.ts
@@ -1,7 +1,6 @@
export * from "./apps";
export * from "./base";
-export * from "./changelogs-next";
-export * from "./changelogs";
+export * from "./changelog";
export * from "./compatibility-flags";
export * from "./fields";
export * from "./glossary";
diff --git a/src/util/changelog.ts b/src/util/changelog.ts
new file mode 100644
index 000000000000000..b69b1019a1761ec
--- /dev/null
+++ b/src/util/changelog.ts
@@ -0,0 +1,215 @@
+import type { RSSFeedItem } from "@astrojs/rss";
+import { getCollection, getEntries, type CollectionEntry } from "astro:content";
+import { entryToString } from "~/util/container";
+
+import { unified, type PluggableList } from "unified";
+
+import rehypeParse from "rehype-parse";
+import rehypeStringify from "rehype-stringify";
+import rehypeBaseUrl from "~/plugins/rehype/base-url";
+import rehypeFilterElements from "~/plugins/rehype/filter-elements";
+
+import rehypeRemark from "rehype-remark";
+import remarkGfm from "remark-gfm";
+import remarkStringify from "remark-stringify";
+
+// TODO
+// function toISODate(date: Date) {
+// return date.toISOString().slice(0, 10);
+// }
+
+// type DocsToChangelogOptions = {
+// /**
+// * An optional title to be prefixed before the date.
+// * This is only necessary if you require an extra product name.
+// *
+// * @example
+// * `HTTP DDoS managed ruleset`
+// */
+// name?: string;
+// /**
+// * Name of a product which must match a filename in the
+// * src/content/products/ collection, without the
+// * file extension.
+// *
+// * @example
+// * `ddos-protection`
+// */
+// product: string;
+// /**
+// * A changelog entry from the `getChangelogs({})` function.
+// * @see {@link getChangelogs}
+// */
+// entry: CollectionEntry<"docs">;
+// };
+
+// function docsToChangelog({
+// name,
+// product,
+// entry,
+// }: DocsToChangelogOptions): CollectionEntry<"changelog"> {
+// const { data } = entry;
+
+// // `data.changelog` will exist as the existence of this
+// // property is part of the `getChangelogs` filter.
+// const date = data.changelog!.date;
+// const scheduled = data.changelog!.scheduled;
+
+// const iso8601 = toISODate(date);
+
+// let title;
+// if (scheduled) {
+// title = `Scheduled for ${toISODate(scheduled)}`;
+// } else {
+// title = iso8601;
+// }
+
+// if (name) {
+// title = `${name} - ${title}`;
+// }
+
+// return {
+// ...entry,
+// collection: "changelog",
+// data: {
+// title,
+// description: `${name} - ${toISODate(date)}`,
+// date,
+// products: [{ collection: "products", id: product }],
+// link: `/${entry.id}/`,
+// },
+// };
+// }
+
+export type GetChangelogsOptions = {
+ filter?: (entry: CollectionEntry<"changelog">) => boolean;
+};
+
+export async function getChangelogs({
+ filter,
+}: GetChangelogsOptions): Promise>> {
+ let entries = await getCollection("changelog");
+
+ entries = entries.map((e) => {
+ e.data.link = `/changelog/${e.id}/`;
+
+ return e;
+ });
+
+ // TODO
+ // const ddosHttp = await getCollection("docs", (e) => {
+ // return (
+ // e.id.startsWith("ddos-protection/change-log/http/") && e.data.changelog
+ // );
+ // });
+
+ // ddosHttp
+ // .map((e) =>
+ // docsToChangelog({
+ // name: "HTTP DDoS managed ruleset",
+ // product: "ddos-protection",
+ // entry: e,
+ // }),
+ // )
+ // .forEach((e) => entries.push(e));
+
+ // const ddosNetwork = await getCollection("docs", (e) => {
+ // return (
+ // e.id.startsWith("ddos-protection/change-log/network/") && e.data.changelog
+ // );
+ // });
+
+ // ddosNetwork
+ // .map((e) =>
+ // docsToChangelog({
+ // name: "Network-layer DDoS managed ruleset",
+ // product: "ddos-protection",
+ // entry: e,
+ // }),
+ // )
+ // .forEach((e) => entries.push(e));
+
+ // const waf = await getCollection("docs", (e) => {
+ // return e.id.startsWith("waf/change-log/") && e.data.changelog;
+ // });
+
+ // waf
+ // .map((e) =>
+ // docsToChangelog({
+ // product: "waf",
+ // entry: e,
+ // }),
+ // )
+ // .forEach((e) => entries.push(e));
+
+ if (filter) {
+ entries = entries.filter((e) => filter(e));
+ }
+
+ return entries.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
+}
+
+type GetRSSItemsOptions = {
+ /**
+ * An array of changelog entries from the `getChangelogs({})` function.
+ * @see {@link getChangelogs}
+ */
+ notes: Array>;
+ /**
+ * `locals`, either from `Astro.locals` in custom pages or
+ * `context.locals` in endpoints.
+ * @see {@link https://docs.astro.build/en/reference/api-reference/#locals}
+ */
+ locals: App.Locals;
+ /**
+ * Returns Markdown in the `` field instead of HTML.
+ */
+ markdown?: boolean;
+};
+
+export async function getRSSItems({
+ notes,
+ locals,
+ markdown,
+}: GetRSSItemsOptions): Promise> {
+ return await Promise.all(
+ notes.map(async (note) => {
+ const { title, date, products, link } = note.data;
+
+ const productEntries = await getEntries(products);
+ const productTitles = productEntries.map((p) => p.data.name as string);
+
+ const html = await entryToString(note, locals);
+
+ const plugins: PluggableList = [
+ rehypeParse,
+ rehypeBaseUrl,
+ rehypeFilterElements,
+ ];
+
+ if (markdown) {
+ plugins.push(...[rehypeRemark, remarkGfm, remarkStringify]);
+ } else {
+ plugins.push(...[rehypeStringify]);
+ }
+
+ const file = await unified()
+ .data("settings", {
+ fragment: true,
+ })
+ .use(plugins)
+ .process(html);
+
+ const content = String(file).trim();
+
+ return {
+ title: `${productTitles.join(", ")} - ${title}`,
+ description: content,
+ pubDate: date,
+ categories: productTitles,
+ link,
+ customData: `${productTitles.at(0)}`,
+ };
+ }),
+ );
+}
diff --git a/src/util/changelogs.ts b/src/util/changelogs.ts
deleted file mode 100644
index 21cc14bf00c63fb..000000000000000
--- a/src/util/changelogs.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { z } from "astro:schema";
-import { getCollection } from "astro:content";
-import { type CollectionEntry } from "astro:content";
-
-export async function getChangelogs(opts?: {
- filter?: Parameters>[1];
- wranglerOnly?: boolean;
- deprecationsOnly?: boolean;
-}) {
- let changelogs;
-
- if (opts?.wranglerOnly) {
- changelogs = [await getWranglerChangelog()];
- } else if (opts?.filter) {
- changelogs = await getCollection("changelogs", opts.filter);
- } else {
- changelogs = await getCollection("changelogs");
- }
-
- if (!changelogs) {
- throw new Error(
- `[getChangelogs] Unable to find any changelogs with ${JSON.stringify(opts)}`,
- );
- }
-
- if (opts?.deprecationsOnly) {
- changelogs = changelogs.filter((x) => x.id === "api-deprecations");
- } else {
- changelogs = changelogs.filter((x) => x.id !== "api-deprecations");
- }
-
- const products = [...new Set(changelogs.flatMap((x) => x.data.productName))];
- const productAreas = [
- ...new Set(changelogs.flatMap((x) => x.data.productArea)),
- ];
-
- const mapped = changelogs.flatMap((product) => {
- return product.data.entries.map((entry) => {
- return {
- product: product.data.productName,
- link: product.data.link,
- date: entry.publish_date,
- description: entry.description,
- title: entry.title,
- scheduled: entry.scheduled,
- productLink: product.data.productLink,
- productAreaName: product.data.productArea,
- productAreaLink: product.data.productAreaLink,
- individual_page: entry.individual_page && entry.link,
- };
- });
- });
-
- const grouped = Object.entries(Object.groupBy(mapped, (entry) => entry.date));
- const entries = grouped.sort().reverse();
-
- return { products, productAreas, changelogs: entries };
-}
-
-export async function getWranglerChangelog(): Promise<
- CollectionEntry<"changelogs">
-> {
- const response = await fetch(
- "https://api.github.com/repos/cloudflare/workers-sdk/releases?per_page=100",
- );
-
- if (!response.ok) {
- throw new Error(
- `[GetWranglerChangelog] Received ${response.status} response from GitHub API.`,
- );
- }
-
- const json = await response.json();
-
- let releases = z
- .object({
- published_at: z.coerce.date(),
- name: z.string(),
- body: z.string(),
- })
- .array()
- .parse(json);
-
- releases = releases.filter((x) => x.name.startsWith("wrangler@"));
-
- return {
- id: "wrangler",
- collection: "changelogs",
- data: {
- link: "/workers/platform/changelog/wrangler/",
- productName: "wrangler",
- productLink: "/workers/wrangler/",
- productArea: "Developer platform",
- productAreaLink: "/workers/platform/changelog/platform/",
- entries: releases.map((release) => {
- return {
- publish_date: release.published_at.toISOString().substring(0, 10),
- title: release.name.split("@")[1],
- link: `https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%40${release.name.split("@")[1]}`,
- description: release.body,
- };
- }),
- },
- };
-}
diff --git a/src/util/container.ts b/src/util/container.ts
index e4424e5ba228952..df2d0d3cb48483b 100644
--- a/src/util/container.ts
+++ b/src/util/container.ts
@@ -4,7 +4,7 @@ import { loadRenderers } from "astro:container";
import { render, type CollectionEntry } from "astro:content";
export async function entryToString(
- entry: CollectionEntry<"docs">,
+ entry: CollectionEntry<"docs" | "changelog">,
locals: any,
) {
if (entry.rendered?.html) {