Skip to content

Commit 69c7d7c

Browse files
ralyodioclaude
andcommitted
feat(pit): land a typed-in name on the shortest path to holding it
`/pit?name=mosh.whatever` is where a resolver or the gateway sends someone whose name did not resolve to a site. They have just demonstrated demand for a name, so the page opens with what they can do about it rather than a 404. Four honest answers, because what can be offered depends on who holds the ending, and today only a TLD's owner may mint names under it: nobody holds `.whatever` -> claim the ending, prefilled; the name and every other one under it comes with it you hold it -> one form, prefilled, registers `mosh.whatever` you already minted it -> it is yours, it is in your list someone else holds it -> say so. `registerName` refuses anyone but the owner and there is no way for them to sell it through the pit yet, so an offer button would be an invitation into a flow that does not exist The decision lives in lib/moshpit-landing.mjs with no database and no request, so the wording is testable and cannot drift from the rule it describes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent b2621a7 commit 69c7d7c

3 files changed

Lines changed: 244 additions & 5 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// What to offer someone who arrived by typing a Moshpit name.
2+
//
3+
// Somebody puts `mosh.whatever` in the address bar. Either a resolver sent
4+
// them here or the gateway did, and the one thing they must not get is a 404 —
5+
// they just demonstrated demand for a name, which is the entire product.
6+
//
7+
// What can honestly be offered depends on who holds the ending and whether they
8+
// put a price on it. This picks the answer without a database or a request, so
9+
// the wording is testable and cannot drift from the rules in `registerName`
10+
// (owners mint for free) and `quoteName` (everyone else buys, if it is listed).
11+
12+
import { parseMoshpitName } from "./moshpit-name.mjs";
13+
14+
/**
15+
* @param {object} state
16+
* @param {boolean} state.tldOwned is `.whatever` claimed by anyone
17+
* @param {boolean} state.ownedByViewer …by the person looking at the page
18+
* @param {boolean} state.nameRegistered is `mosh.whatever` itself minted
19+
* @param {string|null} state.target where the name points, when it points
20+
* @param {number|null} state.priceUsd what the owner charges per name, if listed
21+
*/
22+
export function landingFor(input, state = {}) {
23+
const parsed = parseMoshpitName(input);
24+
if (!parsed) return { kind: "none" };
25+
const { label, tld } = parsed;
26+
const base = { label, tld, name: `${label}.${tld}` };
27+
28+
// Nobody holds the ending. The visitor can have it and everything under it —
29+
// the best possible answer to "this name does not exist yet".
30+
if (!state.tldOwned) return { ...base, kind: "claim-tld" };
31+
32+
// They hold it: this is the one case where the name is one form away.
33+
if (state.ownedByViewer) {
34+
return { ...base, kind: state.nameRegistered ? "yours" : "mint-name" };
35+
}
36+
37+
// Someone else's ending. Taken is taken, whatever the price.
38+
if (state.nameRegistered) {
39+
return { ...base, kind: "taken", target: state.target ?? null };
40+
}
41+
42+
// Free, and the operator has put a price on names under it — so this visitor
43+
// can have the exact name they typed, right now, for that much.
44+
const priceUsd = state.priceUsd;
45+
if (priceUsd !== null && priceUsd !== undefined) {
46+
return { ...base, kind: "buy", priceUsd };
47+
}
48+
49+
// Free, but not for sale. Say so rather than inviting them into a checkout
50+
// that `quoteName` would refuse.
51+
return { ...base, kind: "not-for-sale" };
52+
}

apps/pwa/src/routes/moshpit.mjs

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ import { page, footer, appBar, esc } from "../lib/html.mjs";
1717
import { requireAuth, csrfInput } from "../lib/session.mjs";
1818
import { balance } from "../lib/credits.mjs";
1919
import { resolverConfig } from "../lib/moshpit-resolvers.mjs";
20+
import { landingFor } from "../lib/moshpit-landing.mjs";
2021
import {
2122
getTld, listTlds, listTldsForUser, registerTld, normalizeLabel,
2223
setAlias, clearAlias, listExempt, setExempt, clearExempt,
23-
listNames, registerName, setNameTarget, releaseName,
24+
listNames, getName, getTldWithPrice, registerName, setNameTarget, releaseName,
2425
setTldPrice, listTldsNotOwnedBy, quoteName, openNamePurchase,
25-
resolveMoshpitName, normalizeTld, tldRejection,
26+
resolveMoshpitName, normalizeTld, tldRejection, parseMoshpitName,
2627
normalizeMode, resolutionPreference,
2728
} from "../moshpit.mjs";
2829
import { config } from "../config.mjs";
@@ -256,14 +257,105 @@ moshpitRouter.get("/api/moshpit/resolve", async (req, res) => {
256257

257258
/* ---------- the human page ---------- */
258259

259-
const claimForm = (req) => `
260+
const claimForm = (req, prefill = "") => `
260261
<form method="post" action="/pit/claim" class="pit-form">
261262
${csrfInput(req)}
262263
<label class="pit-field"><span class="pit-dot">.</span
263-
><input name="tld" placeholder="eggs" aria-label="the TLD you want" autocomplete="off" spellcheck="false" required></label>
264+
><input name="tld" placeholder="eggs" aria-label="the TLD you want" autocomplete="off" spellcheck="false"
265+
value="${esc(prefill)}" required></label>
264266
<button class="btn acid" type="submit">Claim it</button>
265267
</form>`;
266268

269+
/**
270+
* The card someone lands on after typing `mosh.whatever` somewhere.
271+
*
272+
* A resolver or the gateway sent them here because the name did not resolve to
273+
* a site. They have just demonstrated demand for a name, so the page opens
274+
* with the shortest path from wanting it to holding it — and says plainly when
275+
* there is no such path, rather than inviting them into a flow that does not
276+
* exist.
277+
*/
278+
const landingCard = (req, landing) => {
279+
if (!landing || landing.kind === "none") return "";
280+
const name = `<span class="mono acid">${esc(landing.name)}</span>`;
281+
const tld = `<span class="mono acid">.${esc(landing.tld)}</span>`;
282+
283+
if (landing.kind === "claim-tld") {
284+
return `<div class="pit-land">
285+
<p class="label">you asked for ${esc(landing.name)}</p>
286+
<h2>Nobody holds ${tld}.</h2>
287+
<p class="pit-copy">Claim the ending and ${name} — plus every other name under it — is yours to
288+
point wherever you like. First come, first served, and nobody can take it back.</p>
289+
${req.user ? claimForm(req, landing.tld)
290+
: `<p class="pit-copy">Sign in with your moshcode account to claim it — the same login the CLI uses.</p>
291+
<p><a class="btn acid" href="/">Sign in →</a></p>`}
292+
</div>`;
293+
}
294+
295+
if (landing.kind === "mint-name") {
296+
return `<div class="pit-land">
297+
<p class="label">you asked for ${esc(landing.name)}</p>
298+
<h2>${tld} is yours. ${name} is one click away.</h2>
299+
<p class="pit-copy">Register the name and point it at whatever should answer for it — a host, an
300+
address, or nothing yet.</p>
301+
<form method="post" action="/pit/${esc(landing.tld)}/names" class="pit-row">
302+
${csrfInput(req)}
303+
<input type="hidden" name="label" value="${esc(landing.label)}">
304+
<span class="mono acid">${esc(landing.name)}</span>
305+
<input name="target" placeholder="points at… (optional)" autocomplete="off">
306+
<button class="btn acid" type="submit">Register it</button>
307+
</form>
308+
</div>`;
309+
}
310+
311+
if (landing.kind === "yours") {
312+
return `<div class="pit-land">
313+
<p class="label">you asked for ${esc(landing.name)}</p>
314+
<h2>${name} is already yours.</h2>
315+
<p class="pit-copy">It is in your list below — change where it points, or release it.</p>
316+
</div>`;
317+
}
318+
319+
if (landing.kind === "taken") {
320+
return `<div class="pit-land">
321+
<p class="label">you asked for ${esc(landing.name)}</p>
322+
<h2>${name} is taken.</h2>
323+
<p class="pit-copy">Someone else holds ${tld} and has minted this name${
324+
landing.target ? `, pointing it at <span class="mono">${esc(landing.target)}</span>` : ""
325+
}. Claim an ending of your own below and you will never have to ask anyone for a name again.</p>
326+
</div>`;
327+
}
328+
329+
if (landing.kind === "buy") {
330+
const price = esc(String(landing.priceUsd));
331+
return `<div class="pit-land">
332+
<p class="label">you asked for ${esc(landing.name)}</p>
333+
<h2>${name} is free. ${tld} sells names at $${price}.</h2>
334+
<p class="pit-copy">Buy it and it is yours to point wherever you like — the operator of the ending
335+
keeps the money, and nobody can take the name back.</p>
336+
${req.user ? `
337+
<form method="post" action="/pit/${esc(landing.tld)}/buy" class="pit-row">
338+
${csrfInput(req)}
339+
<input type="hidden" name="label" value="${esc(landing.label)}">
340+
<span class="mono acid">${esc(landing.name)}</span>
341+
<button class="btn acid" type="submit">Buy for $${price}</button>
342+
</form>`
343+
: `<p class="pit-copy">Sign in with your moshcode account to buy it — the same login the CLI uses.</p>
344+
<p><a class="btn acid" href="/">Sign in →</a></p>`}
345+
</div>`;
346+
}
347+
348+
// not-for-sale: the name is free, but the operator has not put a price on
349+
// names under their ending, and `quoteName` refuses without one. Saying so
350+
// beats a checkout button that dead-ends.
351+
return `<div class="pit-land">
352+
<p class="label">you asked for ${esc(landing.name)}</p>
353+
<h2>${name} is free, but ${tld} is not selling.</h2>
354+
<p class="pit-copy">Whoever holds the ending has not put a price on names under it. Claim an ending
355+
of your own below — or take the same label under one that is selling.</p>
356+
</div>`;
357+
};
358+
267359
const PIT_CSS = `
268360
.pit-form{display:flex;gap:10px;flex-wrap:wrap;align-items:stretch;margin:18px 0 8px}
269361
.pit-field{display:flex;align-items:center;gap:2px;background:var(--surface);border:1px solid var(--line-2);
@@ -301,7 +393,11 @@ const PIT_CSS = `
301393
.pit-steps{margin:0;padding-left:20px;line-height:1.8;max-width:66ch}
302394
.pit-steps li{margin-bottom:10px}
303395
.pit-steps code,.pit-copy code{font-family:var(--mono);color:var(--acid);font-size:.86em}
304-
.pit-copy{max-width:66ch;color:var(--dim)}`;
396+
.pit-copy{max-width:66ch;color:var(--dim)}
397+
.pit-land{border:1px solid var(--line-2);border-left:3px solid var(--acid);border-radius:var(--r);
398+
background:linear-gradient(180deg,var(--surface),var(--bg-tint));padding:20px 22px;margin:0 0 26px}
399+
.pit-land h2{font-size:1.35rem;text-transform:none;margin:6px 0 10px}
400+
.pit-land .pit-form,.pit-land .pit-row{margin-bottom:0}`;
305401

306402
/**
307403
* The tab strip. `/pit` is the namespace itself and `/pit/dns` is how you
@@ -323,6 +419,24 @@ moshpitRouter.get("/pit", async (req, res) => {
323419
req.user ? balance(req.user.id) : 0,
324420
]);
325421

422+
// `?name=mosh.whatever` — somebody typed a Moshpit name and ended up here
423+
// instead of at a site. Work out what they can actually do about it.
424+
const asked = parseMoshpitName(req.query.name);
425+
let landing = { kind: "none" };
426+
if (asked) {
427+
// With the price: a stranger can buy a name under an ending that is listed
428+
// for sale (#127), so the card has to know whether this one is.
429+
const owner = await getTldWithPrice(asked.tld);
430+
const entry = owner ? await getName(asked.tld, asked.label) : null;
431+
landing = landingFor(req.query.name, {
432+
tldOwned: Boolean(owner),
433+
ownedByViewer: Boolean(owner && req.user && owner.user_id === req.user.id),
434+
nameRegistered: Boolean(entry),
435+
target: entry?.target ?? null,
436+
priceUsd: owner?.price_usd ?? null,
437+
});
438+
}
439+
326440
// Exemptions are only meaningful for a TLD that points somewhere, so only
327441
// those cost a query.
328442
const exemptions = new Map();
@@ -431,6 +545,7 @@ moshpitRouter.get("/pit", async (req, res) => {
431545
you exempt stays exactly where it is.
432546
</p>
433547
${pitTabs("namespace")}
548+
${landingCard(req, landing)}
434549
${msg}
435550
${req.user ? claimForm(req) : ""}
436551
<h2 style="margin-top:34px;font-size:1.2rem">Yours</h2>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Someone typed `mosh.whatever` and landed on the pit. What they are offered
2+
// has to match what the registry will actually let them do — an invitation to
3+
// register a name that `registerName` then refuses is worse than a plain no.
4+
import assert from "node:assert/strict";
5+
import test from "node:test";
6+
7+
import { landingFor } from "../src/lib/moshpit-landing.mjs";
8+
9+
test("an unclaimed ending is offered whole", () => {
10+
const landing = landingFor("mosh.whatever", { tldOwned: false });
11+
assert.equal(landing.kind, "claim-tld");
12+
assert.equal(landing.tld, "whatever");
13+
assert.equal(landing.label, "mosh");
14+
assert.equal(landing.name, "mosh.whatever");
15+
});
16+
17+
test("your own ending puts the name one form away", () => {
18+
assert.equal(
19+
landingFor("mosh.whatever", { tldOwned: true, ownedByViewer: true, nameRegistered: false }).kind,
20+
"mint-name",
21+
);
22+
assert.equal(
23+
landingFor("mosh.whatever", { tldOwned: true, ownedByViewer: true, nameRegistered: true }).kind,
24+
"yours",
25+
);
26+
});
27+
28+
test("someone else's minted name says so, and where it points", () => {
29+
const landing = landingFor("mosh.whatever", {
30+
tldOwned: true, ownedByViewer: false, nameRegistered: true, target: "203.0.113.9",
31+
});
32+
assert.equal(landing.kind, "taken");
33+
assert.equal(landing.target, "203.0.113.9");
34+
});
35+
36+
test("a free name under an ending that is listed for sale is offered at its price", () => {
37+
const landing = landingFor("mosh.whatever", {
38+
tldOwned: true, ownedByViewer: false, nameRegistered: false, priceUsd: 12.5,
39+
});
40+
assert.equal(landing.kind, "buy");
41+
assert.equal(landing.priceUsd, 12.5);
42+
});
43+
44+
test("a free name is priced at zero if that is what the operator set", () => {
45+
// 0 is a price, not the absence of one — `?? null` would be fine but `||`
46+
// would quietly turn a free ending into "not selling".
47+
const landing = landingFor("mosh.whatever", {
48+
tldOwned: true, ownedByViewer: false, nameRegistered: false, priceUsd: 0,
49+
});
50+
assert.equal(landing.kind, "buy");
51+
assert.equal(landing.priceUsd, 0);
52+
});
53+
54+
test("an unlisted ending says so rather than dead-ending in a checkout", () => {
55+
// quoteName() refuses when no price is set, so a Buy button here would take
56+
// someone to an error.
57+
const landing = landingFor("mosh.whatever", {
58+
tldOwned: true, ownedByViewer: false, nameRegistered: false, priceUsd: null,
59+
});
60+
assert.equal(landing.kind, "not-for-sale");
61+
});
62+
63+
test("anything that is not a Moshpit name lands on nothing at all", () => {
64+
for (const input of ["", null, "whatever", "a.b.c", "https://mosh.whatever", "mosh.whatever/path"]) {
65+
assert.equal(landingFor(input, { tldOwned: false }).kind, "none", `${JSON.stringify(input)}`);
66+
}
67+
});
68+
69+
test("the name is normalised the way the registry normalises it", () => {
70+
const landing = landingFor(" MOSH.Whatever. ", { tldOwned: false });
71+
assert.equal(landing.name, "mosh.whatever");
72+
});

0 commit comments

Comments
 (0)