diff --git a/frontend/deno.json b/frontend/deno.json
index 8eb41dcae..5b921201e 100644
--- a/frontend/deno.json
+++ b/frontend/deno.json
@@ -14,6 +14,7 @@
},
"imports": {
"apexcharts": "npm:apexcharts@^4.5.0",
+ "badge-maker": "npm:badge-maker@^4.1.0",
"tb-icons": "jsr:@preact-icons/tb@^1.0.12",
"@std/fmt": "jsr:@std/fmt@^1.0.6",
"fresh": "jsr:@fresh/core@^2.0.0-alpha.25",
diff --git a/frontend/deno.lock b/frontend/deno.lock
index 2c572e06b..2f0a7b3c3 100644
--- a/frontend/deno.lock
+++ b/frontend/deno.lock
@@ -47,6 +47,7 @@
"npm:@viz-js/viz@^3.11.0": "3.11.0",
"npm:apexcharts@^4.5.0": "4.5.0_@svgdotjs+svg.js@3.2.4_@svgdotjs+svg.select.js@4.0.2__@svgdotjs+svg.js@3.2.4",
"npm:autoprefixer@10.4.17": "10.4.17_postcss@8.4.35",
+ "npm:badge-maker@^4.1.0": "4.1.0",
"npm:cssnano@6.0.3": "6.0.3_postcss@8.4.35",
"npm:esbuild-wasm@0.23.1": "0.23.1",
"npm:esbuild@0.23.1": "0.23.1",
@@ -340,12 +341,12 @@
"@isaacs/cliui@8.0.2": {
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dependencies": [
- "string-width-cjs@npm:string-width@4.2.3",
"string-width@5.1.2",
- "strip-ansi-cjs@npm:strip-ansi@6.0.1",
+ "string-width-cjs@npm:string-width@4.2.3",
"strip-ansi@7.1.0",
- "wrap-ansi-cjs@npm:wrap-ansi@7.0.0",
- "wrap-ansi@8.1.0"
+ "strip-ansi-cjs@npm:strip-ansi@6.0.1",
+ "wrap-ansi@8.1.0",
+ "wrap-ansi-cjs@npm:wrap-ansi@7.0.0"
]
},
"@jridgewell/gen-mapping@0.3.5": {
@@ -577,6 +578,12 @@
"humanize-ms"
]
},
+ "anafanafo@2.0.0": {
+ "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==",
+ "dependencies": [
+ "char-width-table-consumer"
+ ]
+ },
"ansi-regex@5.0.1": {
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
@@ -586,7 +593,7 @@
"ansi-styles@4.3.0": {
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": [
- "color-convert"
+ "color-convert@2.0.1"
]
},
"ansi-styles@6.2.1": {
@@ -627,8 +634,8 @@
"fraction.js",
"normalize-range",
"picocolors",
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"axios@1.8.4": {
@@ -639,12 +646,22 @@
"proxy-from-env"
]
},
+ "badge-maker@4.1.0": {
+ "integrity": "sha512-qYImXoz0WZRMaauqSMo6QNurKp26K3RcOhefuGfno50xmAzHEJsgHbP4gnHs6Ps53KgQgFi4MJKB6Rq8H7siww==",
+ "dependencies": [
+ "anafanafo",
+ "css-color-converter"
+ ]
+ },
"balanced-match@1.0.2": {
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"binary-extensions@2.3.0": {
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="
},
+ "binary-search@1.3.6": {
+ "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
+ },
"boolbase@1.0.0": {
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
@@ -684,6 +701,12 @@
"caniuse-lite@1.0.30001678": {
"integrity": "sha512-RR+4U/05gNtps58PEBDZcPWTgEO2MBeoPZ96aQcjmfkBWRIDfN451fW2qyDA9/+HohLLIL5GqiMwA+IB1pWarw=="
},
+ "char-width-table-consumer@1.0.0": {
+ "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==",
+ "dependencies": [
+ "binary-search"
+ ]
+ },
"chokidar@3.6.0": {
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dependencies": [
@@ -697,6 +720,9 @@
"readdirp"
]
},
+ "color-convert@0.5.3": {
+ "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling=="
+ },
"color-convert@2.0.1": {
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": [
@@ -732,6 +758,14 @@
"which"
]
},
+ "css-color-converter@2.0.0": {
+ "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==",
+ "dependencies": [
+ "color-convert@0.5.3",
+ "color-name",
+ "css-unit-converter"
+ ]
+ },
"css-declaration-sorter@7.2.0_postcss@8.4.35": {
"integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==",
"dependencies": [
@@ -762,6 +796,9 @@
"source-map-js"
]
},
+ "css-unit-converter@1.1.2": {
+ "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA=="
+ },
"css-what@6.1.0": {
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
},
@@ -774,6 +811,7 @@
"browserslist",
"css-declaration-sorter",
"cssnano-utils",
+ "postcss@8.4.35",
"postcss-calc",
"postcss-colormin",
"postcss-convert-values",
@@ -800,8 +838,7 @@
"postcss-reduce-initial",
"postcss-reduce-transforms",
"postcss-svgo",
- "postcss-unique-selectors",
- "postcss@8.4.35"
+ "postcss-unique-selectors"
]
},
"cssnano-utils@4.0.2_postcss@8.4.35": {
@@ -1227,8 +1264,8 @@
"openai@4.71.1": {
"integrity": "sha512-C6JNMaQ1eijM0lrjiRUL3MgThVP5RdwNAghpbJFdW0t11LzmyqON8Eh8MuUuEZ+CeD6bgYl2Fkn2BoptVxv9Ug==",
"dependencies": [
- "@types/node-fetch",
"@types/node@18.19.64",
+ "@types/node-fetch",
"abort-controller",
"agentkeepalive",
"form-data-encoder",
@@ -1270,9 +1307,9 @@
"postcss-calc@9.0.1_postcss@8.4.35": {
"integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
"dependencies": [
+ "postcss@8.4.35",
"postcss-selector-parser",
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss-value-parser"
]
},
"postcss-colormin@6.1.0_postcss@8.4.35": {
@@ -1281,16 +1318,16 @@
"browserslist",
"caniuse-api",
"colord",
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-convert-values@6.1.0_postcss@8.4.35": {
"integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==",
"dependencies": [
"browserslist",
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-discard-comments@6.0.2_postcss@8.4.35": {
@@ -1320,8 +1357,8 @@
"postcss-import@15.1.0_postcss@8.4.47": {
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dependencies": [
- "postcss-value-parser",
"postcss@8.4.47",
+ "postcss-value-parser",
"read-cache",
"resolve"
]
@@ -1344,8 +1381,8 @@
"postcss-merge-longhand@6.0.5_postcss@8.4.35": {
"integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==",
"dependencies": [
- "postcss-value-parser",
"postcss@8.4.35",
+ "postcss-value-parser",
"stylehacks"
]
},
@@ -1355,15 +1392,15 @@
"browserslist",
"caniuse-api",
"cssnano-utils",
- "postcss-selector-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-selector-parser"
]
},
"postcss-minify-font-values@6.1.0_postcss@8.4.35": {
"integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-minify-gradients@6.0.3_postcss@8.4.35": {
@@ -1371,8 +1408,8 @@
"dependencies": [
"colord",
"cssnano-utils",
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-minify-params@6.1.0_postcss@8.4.35": {
@@ -1380,22 +1417,22 @@
"dependencies": [
"browserslist",
"cssnano-utils",
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-minify-selectors@6.0.4_postcss@8.4.35": {
"integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==",
"dependencies": [
- "postcss-selector-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-selector-parser"
]
},
"postcss-nested@6.2.0_postcss@8.4.47": {
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"dependencies": [
- "postcss-selector-parser",
- "postcss@8.4.47"
+ "postcss@8.4.47",
+ "postcss-selector-parser"
]
},
"postcss-normalize-charset@6.0.2_postcss@8.4.35": {
@@ -1407,66 +1444,66 @@
"postcss-normalize-display-values@6.0.2_postcss@8.4.35": {
"integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-normalize-positions@6.0.2_postcss@8.4.35": {
"integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-normalize-repeat-style@6.0.2_postcss@8.4.35": {
"integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-normalize-string@6.0.2_postcss@8.4.35": {
"integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-normalize-timing-functions@6.0.2_postcss@8.4.35": {
"integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-normalize-unicode@6.1.0_postcss@8.4.35": {
"integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==",
"dependencies": [
"browserslist",
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-normalize-url@6.0.2_postcss@8.4.35": {
"integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-normalize-whitespace@6.0.2_postcss@8.4.35": {
"integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-ordered-values@6.0.2_postcss@8.4.35": {
"integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==",
"dependencies": [
"cssnano-utils",
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-reduce-initial@6.1.0_postcss@8.4.35": {
@@ -1480,8 +1517,8 @@
"postcss-reduce-transforms@6.0.2_postcss@8.4.35": {
"integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==",
"dependencies": [
- "postcss-value-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-value-parser"
]
},
"postcss-selector-parser@6.1.2": {
@@ -1494,16 +1531,16 @@
"postcss-svgo@6.0.3_postcss@8.4.35": {
"integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==",
"dependencies": [
- "postcss-value-parser",
"postcss@8.4.35",
+ "postcss-value-parser",
"svgo"
]
},
"postcss-unique-selectors@6.0.4_postcss@8.4.35": {
"integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==",
"dependencies": [
- "postcss-selector-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-selector-parser"
]
},
"postcss-value-parser@4.2.0": {
@@ -1658,8 +1695,8 @@
"integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==",
"dependencies": [
"browserslist",
- "postcss-selector-parser",
- "postcss@8.4.35"
+ "postcss@8.4.35",
+ "postcss-selector-parser"
]
},
"sucrase@3.35.0": {
@@ -1706,12 +1743,12 @@
"normalize-path",
"object-hash",
"picocolors",
+ "postcss@8.4.47",
"postcss-import",
"postcss-js",
"postcss-load-config",
"postcss-nested",
"postcss-selector-parser",
- "postcss@8.4.47",
"resolve",
"sucrase"
]
@@ -1954,6 +1991,7 @@
"npm:@preact/signals@1.2.1",
"npm:@viz-js/viz@^3.11.0",
"npm:apexcharts@^4.5.0",
+ "npm:badge-maker@^4.1.0",
"npm:marked-smartypants@1.1.6",
"npm:postcss@8.4",
"npm:preact-render-to-string@6.3.1",
diff --git a/frontend/routes/badges/scope.ts b/frontend/routes/badges/scope.ts
index b109a30ca..4277c617a 100644
--- a/frontend/routes/badges/scope.ts
+++ b/frontend/routes/badges/scope.ts
@@ -4,19 +4,27 @@ import { accepts } from "@std/http/negotiation";
import { define } from "../../util.ts";
import { Scope } from "../../utils/api_types.ts";
import { path } from "../../utils/api.ts";
+import { makeBadge } from "badge-maker";
+
+const SVG_LOGO = `\
+
+`;
export const handler = define.handlers({
async GET(ctx) {
const req = ctx.req;
+ const scopeResp = await ctx.state.api.get(
+ path`/scopes/${ctx.params.scope}`,
+ );
+
if (
accepts(req, "application/json", "text/html", "image/*") ===
"application/json"
) {
- const scopeResp = await ctx.state.api.get(
- path`/scopes/${ctx.params.scope}`,
- );
-
if (!scopeResp.ok) {
if (scopeResp.code === "scopeNotFound") {
return new Response(null, { status: 404 });
@@ -33,29 +41,37 @@ export const handler = define.handlers({
});
}
} else {
- const url = new URL("https://jsr.io" + ctx.url.pathname + ctx.url.search);
-
- const shieldsUrl = new URL("https://img.shields.io/endpoint");
- shieldsUrl.search = url.search;
- shieldsUrl.searchParams.set("url", url.href);
- shieldsUrl.searchParams.set("logo", "jsr");
- shieldsUrl.searchParams.set("logoSize", "auto");
- shieldsUrl.searchParams.set("cacheSeconds", "300");
+ let badge: string;
- if (!ctx.url.searchParams.has("logoColor")) {
- shieldsUrl.searchParams.set("logoColor", "rgb(8,51,68)");
+ if (!scopeResp.ok) {
+ if (scopeResp.code === "scopeNotFound") {
+ badge = makeBadge({
+ label: "custom badge",
+ message: "resource not found",
+ color: "rgb(206,88,66)",
+ });
+ } else {
+ throw scopeResp;
+ }
+ } else {
+ const url = new URL(
+ "https://jsr.io" + ctx.url.pathname + ctx.url.search,
+ );
+ badge = makeBadge({
+ label: "",
+ message: `@${scopeResp.data.scope}`,
+ labelColor: "rgb(247,223,30)",
+ color: "rgb(8,51,68)",
+ links: [url.toString()],
+ logoBase64: `data:image/svg+xml,${SVG_LOGO}`
+ });
}
- const res = await fetch(shieldsUrl);
-
- return new Response(res.body, {
- status: res.status,
+ return new Response(badge, {
headers: {
- "access-control-allow-origin": res.headers.get(
- "access-control-allow-origin",
- )!,
- "cache-control": res.headers.get("cache-control")!,
- "content-type": res.headers.get("content-type")!,
+ "access-control-allow-origin": "*",
+ "cache-control": "max-age=300,s-maxage=300",
+ "content-type": "image/svg+xml;charset=utf-8",
},
});
}