@moshcoder/moshpit-name
Advanced tools
+15
-3
@@ -64,10 +64,22 @@ #!/usr/bin/env node | ||
| for (const e of entries) { | ||
| const bits = [`.${e.tld}`]; | ||
| // A name is printed as written. Prefixing it with a dot would spell it as | ||
| // an ending, which is the one thing it is not. | ||
| const bits = [e.label ? `${e.label}.${e.tld}` : `.${e.tld}`]; | ||
| if (e.aliasOf) bits.push(`→ .${e.aliasOf}`); | ||
| if (e.priceUsd !== null) bits.push(`$${e.priceUsd}`); | ||
| const why = tldRejection(e.tld); | ||
| // A token that is neither an ending nor a name arrives here carrying its | ||
| // own bad text, and tldRejection only speaks about things that are already | ||
| // shaped like an ending — so the malformed case is named here rather than | ||
| // printed clean as though the registry would take it. | ||
| const why = normalizeTld(e.tld) | ||
| ? tldRejection(e.tld) | ||
| : "not a valid ending — letters, digits and dashes only, no dots"; | ||
| if (why) bits.push(`[${why}]`); | ||
| out(bits.join(" ")); | ||
| } | ||
| out(`\n${entries.length} endings${skipped ? `, ${skipped} past the ${MAX_BULK_TLDS} limit` : ""}`); | ||
| const endings = entries.filter((e) => !e.label).length; | ||
| const names = entries.length - endings; | ||
| const counted = [`${endings} ending${endings === 1 ? "" : "s"}`]; | ||
| if (names) counted.push(`${names} name${names === 1 ? "" : "s"}`); | ||
| out(`\n${counted.join(", ")}${skipped ? `, ${skipped} past the ${MAX_BULK_TLDS} limit` : ""}`); | ||
| process.exit(0); | ||
@@ -74,0 +86,0 @@ } |
+41
-6
@@ -227,2 +227,10 @@ // Validation, policy and resolution precedence for Moshpit names. | ||
| * against yourself. | ||
| * | ||
| * Both halves of the namespace come through the same field. `eggs` and `.eggs` | ||
| * are the ending; `blue.eggs` and `.blue.eggs` are a name under it. The leading | ||
| * dot is decoration in either case — people type the shape they saw written | ||
| * down, and refusing one spelling of the thing they already own would be a | ||
| * distinction without a difference. Which one a line meant is recorded on the | ||
| * entry rather than resolved here, because parsing is not the place that knows | ||
| * who holds `.eggs`. | ||
| */ | ||
@@ -247,5 +255,9 @@ export function parseTldList(input, limit = MAX_BULK_TLDS) { | ||
| const fields = record.split(/\s+/).filter(Boolean); | ||
| const tld = normalizeToken(fields[0]); | ||
| if (!tld || seen.has(tld)) continue; | ||
| seen.add(tld); | ||
| const target = parseListToken(fields[0]); | ||
| if (!target) continue; | ||
| // Keyed on the whole thing, so `.eggs` and `blue.eggs` in one paste are two | ||
| // entries rather than one swallowing the other. | ||
| const key = target.label ? `${target.label}.${target.tld}` : target.tld; | ||
| if (seen.has(key)) continue; | ||
| seen.add(key); | ||
@@ -267,10 +279,33 @@ // Counted rather than silently dropped: "I pasted 300 and got 200" needs to | ||
| } | ||
| entries.push({ tld, aliasOf, priceUsd }); | ||
| entries.push({ tld: target.tld, label: target.label, aliasOf, priceUsd }); | ||
| } | ||
| // `tlds` alongside `entries` because most callers only want the names, and | ||
| // making every one of them map over the records would be noise. | ||
| return { entries, tlds: entries.map((e) => e.tld), skipped }; | ||
| // making every one of them map over the records would be noise. `names` is | ||
| // the same courtesy for the other half, and the two are disjoint: an entry is | ||
| // an ending or a name under one, never both. | ||
| return { | ||
| entries, | ||
| tlds: entries.filter((e) => !e.label).map((e) => e.tld), | ||
| names: entries.filter((e) => e.label).map((e) => ({ tld: e.tld, label: e.label })), | ||
| skipped, | ||
| }; | ||
| } | ||
| /** | ||
| * One pasted token, as the ending or the name it was written as. | ||
| * | ||
| * A token that reads as neither comes back as an ending carrying its own bad | ||
| * text — `a.b.c` is not silently dropped, it is handed on so the caller can | ||
| * reject it by name and say why. Losing it here would turn "you pasted | ||
| * something wrong" into a line that never appears in the report at all. | ||
| */ | ||
| function parseListToken(value) { | ||
| const raw = normalizeToken(value); | ||
| if (!raw) return null; | ||
| if (!raw.includes(".")) return { tld: raw, label: null }; | ||
| const name = parseMoshpitName(raw); | ||
| return name ? { tld: name.tld, label: name.label } : { tld: raw, label: null }; | ||
| } | ||
| function normalizeToken(value) { | ||
@@ -277,0 +312,0 @@ return String(value ?? "").trim().toLowerCase().replace(/^\.+/, "") || null; |
+1
-1
| { | ||
| "name": "@moshcoder/moshpit-name", | ||
| "version": "0.1.1", | ||
| "version": "0.2.0", | ||
| "type": "module", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+6
-0
@@ -19,2 +19,8 @@ # @moshcoder/moshpit-name | ||
| tldRejection("bank"); // "that name is reserved" | ||
| // A pasted list holds both halves of the namespace, in whichever shape the | ||
| // person typed. The leading dot is decoration; two labels is a name. | ||
| parseTldList("eggs\n.whatever\nblue.eggs\n.me.whatever"); | ||
| // tlds: ["eggs", "whatever"] | ||
| // names: [{ tld: "eggs", label: "blue" }, { tld: "whatever", label: "me" }] | ||
| ``` | ||
@@ -21,0 +27,0 @@ |
22548
14.18%378
13.86%80
8.11%