@moshcoder/moshpit-name
Advanced tools
+46
-4
@@ -17,3 +17,5 @@ #!/usr/bin/env node | ||
| moshpit-name parse <name> [--json] split a name into its label and ending | ||
| moshpit-name list [-] [--json] parse a pasted list; - reads stdin | ||
| moshpit-name list [-] [--limit N] [--json] | ||
| parse up to N pasted entries; - reads stdin | ||
| moshpit-name reserved [--json] list endings that cannot be claimed | ||
| moshpit-name prices [--json] what an ending and a name cost | ||
@@ -25,3 +27,20 @@ | ||
| const json = rawRest.includes("--json"); | ||
| const rest = rawRest.filter((arg) => arg !== "--json"); | ||
| const rest = []; | ||
| let limit = MAX_BULK_TLDS; | ||
| let limitValue; | ||
| let limitFlags = 0; | ||
| for (let index = 0; index < rawRest.length; index++) { | ||
| const arg = rawRest[index]; | ||
| if (arg === "--json") continue; | ||
| if (arg === "--limit") { | ||
| limitFlags++; | ||
| const candidate = rawRest[index + 1]; | ||
| if (candidate !== undefined && !candidate.startsWith("--")) { | ||
| limitValue = candidate; | ||
| index++; | ||
| } | ||
| continue; | ||
| } | ||
| rest.push(arg); | ||
| } | ||
| const out = console.log; | ||
@@ -73,4 +92,20 @@ const outJson = (value) => out(JSON.stringify(value, null, 2)); | ||
| if (sub === "list") { | ||
| const parsedLimit = Number(limitValue); | ||
| const validLimit = limitFlags === 0 || ( | ||
| limitFlags === 1 | ||
| && /^\d+$/.test(limitValue ?? "") | ||
| && Number.isSafeInteger(parsedLimit) | ||
| && parsedLimit >= 1 | ||
| && parsedLimit <= MAX_BULK_TLDS | ||
| ); | ||
| if (!validLimit) { | ||
| const error = `--limit must be an integer from 1 to ${MAX_BULK_TLDS}`; | ||
| if (json) outJson({ error }); | ||
| else console.error(`moshpit-name: ${error}`); | ||
| process.exit(1); | ||
| } | ||
| if (limitFlags === 1) limit = parsedLimit; | ||
| const input = rest[0] === "-" || !rest.length ? await readStdin() : rest.join("\n"); | ||
| const parsed = parseTldList(input); | ||
| const parsed = parseTldList(input, limit); | ||
| if (json) { | ||
@@ -101,6 +136,13 @@ outJson(parsed); | ||
| if (names) counted.push(`${names} name${names === 1 ? "" : "s"}`); | ||
| out(`\n${counted.join(", ")}${skipped ? `, ${skipped} past the ${MAX_BULK_TLDS} limit` : ""}`); | ||
| out(`\n${counted.join(", ")}${skipped ? `, ${skipped} past the ${limit} limit` : ""}`); | ||
| process.exit(0); | ||
| } | ||
| if (sub === "reserved") { | ||
| const tlds = [...RESERVED_TLDS].sort(); | ||
| if (json) outJson({ count: tlds.length, tlds }); | ||
| else out(tlds.map((tld) => `.${tld}`).join("\n")); | ||
| process.exit(0); | ||
| } | ||
| if (sub === "prices") { | ||
@@ -107,0 +149,0 @@ if (json) { |
+23
-4
@@ -28,4 +28,18 @@ // Validation, policy and resolution precedence for Moshpit names. | ||
| /** A TLD label: lowercase letters, digits and dashes; no leading/trailing dash. */ | ||
| const LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/; | ||
| /** | ||
| * A label: lowercase letters and digits, up to 63 of them. No dashes. | ||
| * | ||
| * DNS would allow a dash, and dropping it costs real names — `lazy-loaded` | ||
| * cannot be an ending. It buys something worth more in this namespace: a dash | ||
| * is the cheapest way to mint a near-miss of an ending someone else holds. | ||
| * `.crypto` and `.cryp-to` read as the same thing at a glance and sort next to | ||
| * each other, so allowing dashes turns every claimed ending into a family of | ||
| * look-alikes worth squatting. | ||
| * | ||
| * That matters here more than it would elsewhere. The namespace is one level | ||
| * deep and first come first served: there is no second level to retreat to and | ||
| * no dispute process to appeal into, which makes the look-alike the whole | ||
| * attack rather than a nuisance. | ||
| */ | ||
| const LABEL = /^[a-z0-9]{1,63}$/; | ||
@@ -270,4 +284,9 @@ /** A hostname label. Unlike a TLD, an all-numeric label is valid. */ | ||
| let aliasOf = null; | ||
| for (const field of fields.slice(1)) { | ||
| const price = parsePriceToken(field); | ||
| for (let index = 1; index < fields.length; index++) { | ||
| const field = fields[index]; | ||
| let price = parsePriceToken(field); | ||
| if (price === null && /^usd$/i.test(field) && index + 1 < fields.length) { | ||
| price = parsePriceToken(`${field}${fields[index + 1]}`); | ||
| if (price !== null) index++; | ||
| } | ||
| // A price is unambiguous — it is the only field that can start with `$` | ||
@@ -274,0 +293,0 @@ // or be all digits, and an all-numeric ending is rejected anyway. So |
+1
-1
| { | ||
| "name": "@moshcoder/moshpit-name", | ||
| "version": "0.3.0", | ||
| "version": "0.4.0", | ||
| "type": "module", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+25
-1
@@ -39,3 +39,5 @@ # @moshcoder/moshpit-name | ||
| moshpit-name parse <name> [--json] split a name into its label and ending | ||
| moshpit-name list [-] [--json] parse a pasted list; - reads stdin | ||
| moshpit-name list [-] [--limit N] [--json] | ||
| parse up to N pasted entries; - reads stdin | ||
| moshpit-name reserved [--json] list endings that cannot be claimed | ||
| moshpit-name prices [--json] what an ending and a name cost | ||
@@ -55,2 +57,19 @@ ``` | ||
| .yeah | ||
| $ printf 'eggs\nyeah\noranges\n' | moshpit-name list - --limit 2 --json | ||
| { | ||
| "entries": [ | ||
| { "tld": "eggs", "label": null, "aliasOf": null, "priceUsd": null }, | ||
| { "tld": "yeah", "label": null, "aliasOf": null, "priceUsd": null } | ||
| ], | ||
| "tlds": ["eggs", "yeah"], | ||
| "names": [], | ||
| "skipped": 1 | ||
| } | ||
| $ moshpit-name reserved | ||
| .amazon | ||
| .amex | ||
| .anthropic | ||
| ... | ||
| ``` | ||
@@ -71,2 +90,7 @@ | ||
| `list --limit N` stops after `N` unique entries and reports the remainder in | ||
| `skipped`. `N` must be an integer from 1 through 1000, the package's bulk | ||
| ceiling. The option works with inline arguments or stdin and can appear before | ||
| or after `-`. | ||
| ## What it decides | ||
@@ -73,0 +97,0 @@ |
26554
11.99%460
14.71%117
25.81%