Public: anyone can read this; the crew can change it.
The home page's forge-name field says free, taken or why not from `GET /forges/name/:name` #11383
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/11312-the-home-page-s-forge-name-field-says-fr → main 1 commit fork
2 files
changed +20
−7
src/web/pages/start-forge.ts +4 −3
| @@ -20,7 +20,7 @@ import { notFoundPage, pageHead } from "../chrome.ts"; | ||
| 20 | 20 | import { waitingAddress } from "../claim_code.ts"; |
| 21 | 21 | import { startRefusalHref, startRefusalOf } from "../start_refusal.ts"; |
| 22 | 22 | |
| 23 | /** The home's live name check: 404 from the ready probe means nothing by that name was made here. */ | |
| 23 | /** The home's live name check: asks `GET /forges/name/<name>` and says free, taken, or the route's own reason; a 429 or a failure says nothing. */ | |
| 24 | 24 | export const startNameJs = `(function(d){ |
| 25 | 25 | var i=d.getElementById("start-name"),s=d.getElementById("start-name-says"),t,k=0; |
| 26 | 26 | if(!i||!s||!window.fetch)return; |
| @@ -29,8 +29,9 @@ i.addEventListener("input",function(){ | ||
| 29 | 29 | clearTimeout(t);var n=i.value.trim().toLowerCase(),q=++k; |
| 30 | 30 | if(!n){say("");return} |
| 31 | 31 | if(!/^[a-z0-9][a-z0-9-]*$/.test(n)){say("Use lowercase letters, digits and dashes.");return} |
| 32 | t=setTimeout(function(){fetch("/forges/"+encodeURIComponent(n)+"/ready",{credentials:"omit"}).then(function(r){ | |
| 33 | if(q!==k)return;say(r.status===404?n+" is free.":r.status===200?n+" is taken. Try another.":"")}).catch(function(){})},300)}) | |
| 32 | t=setTimeout(function(){fetch("/forges/name/"+encodeURIComponent(n),{credentials:"omit"}).then(function(r){ | |
| 33 | return r.status===200?r.json():null}).then(function(j){ | |
| 34 | if(q!==k||!j)return;say(j.verdict==="free"?n+" is free.":j.verdict==="taken"?n+" is taken. Try another.":typeof j.why==="string"?j.why:"")}).catch(function(){})},300)}) | |
| 34 | 35 | })(document)`; |
| 35 | 36 | |
| 36 | 37 | /** Waits for the forge, then goes to its claim page; after a minute and a half the link is still there. */ |
test/home-start-forge.test.ts +16 −4
| @@ -195,24 +195,36 @@ const settle = async () => { | ||
| 195 | 195 | for (let i = 0; i < 400; i++) await Promise.resolve(); |
| 196 | 196 | }; |
| 197 | 197 | |
| 198 | test("the live check: 404 reads free, 200 reads taken, a bad name never asks", async () => { | |
| 198 | test("the live check: free, taken and why read in words, a 429 reads nothing, a bad name never asks", async () => { | |
| 199 | 199 | const { startNameJs } = await import("../src/web/pages/start-forge.ts"); |
| 200 | 200 | const input = fake({}, "Sunny-Day"); |
| 201 | 201 | const says = fake(); |
| 202 | const run = runScript(startNameJs, { "start-name": input, "start-name-says": says }, (u) => ({ status: u.includes("sunny-day") ? 404 : 200 })); | |
| 202 | const run = runScript(startNameJs, { "start-name": input, "start-name-says": says }, (u) => ({ | |
| 203 | status: u.includes("rate") ? 429 : 200, | |
| 204 | json: { verdict: u.includes("sunny-day") ? "free" : u.includes("admin") ? "reserved" : "taken", why: "admin can't be used as a name." }, | |
| 205 | })); | |
| 203 | 206 | try { |
| 204 | 207 | input.on["input"]!(); |
| 205 | 208 | await settle(); |
| 206 | expect(run.asked).toEqual(["/forges/sunny-day/ready"]); | |
| 209 | expect(run.asked).toEqual(["/forges/name/sunny-day"]); | |
| 210 | expect(startNameJs).not.toContain("/ready"); | |
| 207 | 211 | expect(says.textContent).toBe("sunny-day is free."); |
| 208 | 212 | input.value = "taken"; |
| 209 | 213 | input.on["input"]!(); |
| 210 | 214 | await settle(); |
| 211 | 215 | expect(says.textContent).toBe("taken is taken. Try another."); |
| 216 | input.value = "admin"; | |
| 217 | input.on["input"]!(); | |
| 218 | await settle(); | |
| 219 | expect(says.textContent).toBe("admin can't be used as a name."); | |
| 220 | input.value = "rate"; | |
| 221 | input.on["input"]!(); | |
| 222 | await settle(); | |
| 223 | expect(says.textContent).toBe("admin can't be used as a name."); | |
| 212 | 224 | input.value = "No Good!"; |
| 213 | 225 | input.on["input"]!(); |
| 214 | 226 | expect(says.textContent).toBe("Use lowercase letters, digits and dashes."); |
| 215 | expect(run.asked.length).toBe(2); | |
| 227 | expect(run.asked.length).toBe(4); | |
| 216 | 228 | input.value = ""; |
| 217 | 229 | input.on["input"]!(); |
| 218 | 230 | expect(says.textContent).toBe(""); |
new above
offline