@shorten-dev/cli 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +12 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -68,7 +68,7 @@ function validateDestinationUrl(url) {
|
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
// src/
|
|
71
|
+
// ../shared/src/errors.ts
|
|
72
72
|
var ShortenApiError = class extends Error {
|
|
73
73
|
constructor(status, body) {
|
|
74
74
|
super(body.message);
|
|
@@ -85,6 +85,8 @@ var NetworkError = class extends Error {
|
|
|
85
85
|
this.name = "NetworkError";
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
|
+
|
|
89
|
+
// src/api/client.ts
|
|
88
90
|
var ApiClient = class {
|
|
89
91
|
apiKey;
|
|
90
92
|
baseUrl;
|
|
@@ -162,7 +164,7 @@ function resolveApiKey(flagKey) {
|
|
|
162
164
|
const key = flagKey ?? process.env["SHORTEN_API_KEY"] ?? loadConfig().api_key;
|
|
163
165
|
if (!key) {
|
|
164
166
|
console.error(
|
|
165
|
-
'No API key found. Set SHORTEN_API_KEY or pass --key.\nRun "shorten login" or visit https://shorten.dev/
|
|
167
|
+
'No API key found. Set SHORTEN_API_KEY or pass --key.\nRun "shorten login" or visit https://shorten.dev/api-keys'
|
|
166
168
|
);
|
|
167
169
|
process.exit(1);
|
|
168
170
|
}
|
|
@@ -233,7 +235,7 @@ function handleCommandError(err) {
|
|
|
233
235
|
switch (err.status) {
|
|
234
236
|
case 401:
|
|
235
237
|
error("Invalid API key. Check your key or generate a new one.");
|
|
236
|
-
info(' Run "shorten login" or visit https://shorten.dev/
|
|
238
|
+
info(' Run "shorten login" or visit https://shorten.dev/api-keys');
|
|
237
239
|
break;
|
|
238
240
|
case 403:
|
|
239
241
|
error("Forbidden \u2014 your API key doesn't have the required scope.");
|
|
@@ -278,7 +280,11 @@ function normalizeUrl(raw) {
|
|
|
278
280
|
return raw;
|
|
279
281
|
}
|
|
280
282
|
function registerShortenCommand(program2) {
|
|
281
|
-
const cmd = program2.command("create", { isDefault: true, hidden: true }).argument("
|
|
283
|
+
const cmd = program2.command("create", { isDefault: true, hidden: true }).argument("[url]", "URL to shorten").option("-s, --slug <slug>", "Custom slug").option("-t, --tag <tag>", "Add tags (repeatable)", (val, acc) => [...acc, val], []).option("-q, --quiet", "Output only the short URL").option("-j, --json", "Output as JSON").option("--no-copy", "Don't copy to clipboard").action(async (rawUrl, opts) => {
|
|
284
|
+
if (!rawUrl) {
|
|
285
|
+
program2.help();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
282
288
|
const { client, config } = resolveClient(cmd);
|
|
283
289
|
const format = resolveFormat({
|
|
284
290
|
json: opts.json,
|
|
@@ -406,7 +412,7 @@ function registerStatsCommand(program2) {
|
|
|
406
412
|
const period = res.period;
|
|
407
413
|
console.log(
|
|
408
414
|
`
|
|
409
|
-
${pc2.bold(`shorten.dev/${res.slug}`)} \u2014 ${pc2.cyan(formatNumber(res.total_clicks))} clicks (${period})`
|
|
415
|
+
${pc2.bold(`r.shorten.dev/${res.slug}`)} \u2014 ${pc2.cyan(formatNumber(res.total_clicks))} clicks (${period})`
|
|
410
416
|
);
|
|
411
417
|
console.log(
|
|
412
418
|
pc2.dim(
|
|
@@ -629,7 +635,7 @@ function registerConfigCommand(program2) {
|
|
|
629
635
|
// src/cli.ts
|
|
630
636
|
function createProgram() {
|
|
631
637
|
const program2 = new Command();
|
|
632
|
-
program2.name("shorten").description("Shorten URLs from your terminal").version("0.1.
|
|
638
|
+
program2.name("shorten").description("Shorten URLs from your terminal").version("0.1.2").option("-k, --key <key>", "API key (overrides SHORTEN_API_KEY)").option("--api-url <url>", "API base URL (overrides SHORTEN_API_URL)").option("--no-color", "Disable colored output").configureOutput({ writeErr: (str) => process.stderr.write(str) });
|
|
633
639
|
registerShortenCommand(program2);
|
|
634
640
|
registerListCommand(program2);
|
|
635
641
|
registerStatsCommand(program2);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../../shared/src/constants.ts","../src/api/client.ts","../src/config.ts","../src/utils/client-factory.ts","../src/utils/output.ts","../src/utils/errors.ts","../src/utils/clipboard.ts","../src/commands/shorten.ts","../src/commands/list.ts","../src/commands/stats.ts","../src/commands/whoami.ts","../src/commands/login.ts","../src/commands/config.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { registerShortenCommand } from \"./commands/shorten.js\";\nimport { registerListCommand } from \"./commands/list.js\";\nimport { registerStatsCommand } from \"./commands/stats.js\";\nimport { registerWhoamiCommand } from \"./commands/whoami.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerConfigCommand } from \"./commands/config.js\";\n\ndeclare const __CLI_VERSION__: string;\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"shorten\")\n .description(\"Shorten URLs from your terminal\")\n .version(__CLI_VERSION__)\n .option(\"-k, --key <key>\", \"API key (overrides SHORTEN_API_KEY)\")\n .option(\"--api-url <url>\", \"API base URL (overrides SHORTEN_API_URL)\")\n .option(\"--no-color\", \"Disable colored output\")\n .configureOutput({ writeErr: (str) => process.stderr.write(str) });\n\n registerShortenCommand(program);\n registerListCommand(program);\n registerStatsCommand(program);\n registerWhoamiCommand(program);\n registerLoginCommand(program);\n registerConfigCommand(program);\n\n return program;\n}\n","export const SLUG_LENGTH = 7;\nexport const SLUG_MIN_LENGTH = 3;\nexport const SLUG_MAX_LENGTH = 50;\nexport const SLUG_PATTERN = /^[a-zA-Z0-9_-]+$/;\nexport const BASE_URL = \"https://shorten.dev\";\n\nexport const MAX_TAGS_PER_LINK = 3;\nexport const MAX_TAG_LENGTH = 50;\n\n/**\n * Reserved slugs that cannot be used as custom short links.\n * Covers: app routes, common subdomains, platform terms, admin/system paths,\n * well-known paths, social/SEO, legal, and potentially confusing terms.\n *\n * All entries are lowercase — check with `.toLowerCase()` before comparing.\n */\nexport const RESERVED_SLUGS = new Set([\n // ── App routes & pages ──────────────────────────────────────────────\n \"app\",\n \"api\",\n \"auth\",\n \"login\",\n \"logout\",\n \"signup\",\n \"register\",\n \"signin\",\n \"signout\",\n \"sign-in\",\n \"sign-up\",\n \"sign-out\",\n \"log-in\",\n \"log-out\",\n \"callback\",\n \"dashboard\",\n \"settings\",\n \"account\",\n \"profile\",\n \"billing\",\n \"upgrade\",\n \"pricing\",\n \"plans\",\n \"onboarding\",\n \"welcome\",\n \"verify\",\n \"confirm\",\n \"reset\",\n \"password\",\n \"forgot\",\n \"forgot-password\",\n \"reset-password\",\n \"change-password\",\n \"invite\",\n \"join\",\n\n // ── Public pages ────────────────────────────────────────────────────\n \"about\",\n \"about-us\",\n \"donate\",\n \"docs\",\n \"documentation\",\n \"support\",\n \"help\",\n \"contact\",\n \"contact-us\",\n \"faq\",\n \"blog\",\n \"changelog\",\n \"updates\",\n \"news\",\n \"press\",\n \"careers\",\n \"jobs\",\n \"team\",\n \"partners\",\n \"affiliates\",\n \"referrals\",\n\n // ── Legal ───────────────────────────────────────────────────────────\n \"terms\",\n \"terms-of-service\",\n \"tos\",\n \"privacy\",\n \"privacy-policy\",\n \"cookies\",\n \"cookie-policy\",\n \"gdpr\",\n \"dmca\",\n \"legal\",\n \"compliance\",\n \"acceptable-use\",\n \"aup\",\n\n // ── Admin & system ──────────────────────────────────────────────────\n \"admin\",\n \"administrator\",\n \"root\",\n \"system\",\n \"sys\",\n \"internal\",\n \"debug\",\n \"dev\",\n \"staging\",\n \"test\",\n \"sandbox\",\n \"demo\",\n \"console\",\n \"panel\",\n \"manage\",\n \"manager\",\n \"config\",\n \"configuration\",\n \"setup\",\n \"install\",\n \"cron\",\n \"worker\",\n \"workers\",\n \"queue\",\n \"health\",\n \"healthcheck\",\n \"health-check\",\n \"status\",\n \"uptime\",\n \"metrics\",\n \"monitor\",\n \"monitoring\",\n \"logs\",\n \"trace\",\n\n // ── API & developer ─────────────────────────────────────────────────\n \"api-keys\",\n \"apikeys\",\n \"api-key\",\n \"tokens\",\n \"token\",\n \"oauth\",\n \"oauth2\",\n \"openid\",\n \"sso\",\n \"saml\",\n \"webhooks\",\n \"webhook\",\n \"graphql\",\n \"rest\",\n \"sdk\",\n \"cli\",\n \"developer\",\n \"developers\",\n \"playground\",\n \"explorer\",\n \"schema\",\n \"swagger\",\n \"openapi\",\n \"redoc\",\n\n // ── Analytics & data ────────────────────────────────────────────────\n \"analytics\",\n \"stats\",\n \"statistics\",\n \"reports\",\n \"report\",\n \"insights\",\n \"events\",\n \"clicks\",\n \"links\",\n \"link\",\n \"urls\",\n \"url\",\n \"domains\",\n \"domain\",\n\n // ── User & account ──────────────────────────────────────────────────\n \"user\",\n \"users\",\n \"me\",\n \"my\",\n \"you\",\n \"self\",\n \"notifications\",\n \"inbox\",\n \"messages\",\n \"mail\",\n \"email\",\n \"preferences\",\n \"subscription\",\n \"subscriptions\",\n\n // ── Social & SEO ────────────────────────────────────────────────────\n \"share\",\n \"embed\",\n \"widget\",\n \"follow\",\n \"like\",\n \"star\",\n \"bookmark\",\n \"feed\",\n \"rss\",\n \"atom\",\n \"sitemap\",\n \"robots\",\n \"manifest\",\n \"humans\",\n \"ads\",\n \"sponsors\",\n\n // ── Well-known paths ────────────────────────────────────────────────\n \"favicon\",\n \"wp-admin\",\n \"wp-login\",\n \"wp-content\",\n \"wordpress\",\n \"xmlrpc\",\n \"cgi-bin\",\n \"phpmyadmin\",\n \"env\",\n \"git\",\n \"svn\",\n \"htaccess\",\n \"htpasswd\",\n \"ssh\",\n \"ftp\",\n \"cpanel\",\n \"webmail\",\n \"autodiscover\",\n \"well-known\",\n \"_next\",\n\n // ── Common subdomains (if ever used as slugs) ───────────────────────\n \"www\",\n \"mail\",\n \"ftp\",\n \"cdn\",\n \"assets\",\n \"static\",\n \"media\",\n \"images\",\n \"img\",\n \"files\",\n \"download\",\n \"downloads\",\n \"upload\",\n \"uploads\",\n \"storage\",\n\n // ── Brand protection ────────────────────────────────────────────────\n \"shorten\",\n \"shorten-dev\",\n \"shortendev\",\n \"official\",\n \"verified\",\n\n // ── Potentially confusing / misleading ──────────────────────────────\n \"null\",\n \"undefined\",\n \"nil\",\n \"none\",\n \"true\",\n \"false\",\n \"nan\",\n \"infinity\",\n \"error\",\n \"404\",\n \"500\",\n \"403\",\n \"401\",\n \"new\",\n \"create\",\n \"edit\",\n \"delete\",\n \"remove\",\n \"update\",\n \"search\",\n \"home\",\n \"index\",\n \"default\",\n \"public\",\n \"private\",\n \"example\",\n \"test\",\n \"temp\",\n \"tmp\",\n\n // ── Payment & commerce ──────────────────────────────────────────────\n \"checkout\",\n \"payment\",\n \"payments\",\n \"pay\",\n \"invoice\",\n \"invoices\",\n \"receipt\",\n \"refund\",\n \"stripe\",\n \"paypal\",\n\n // ── Security-sensitive ──────────────────────────────────────────────\n \"security\",\n \"vulnerability\",\n \"report\",\n \"abuse\",\n \"spam\",\n \"phishing\",\n \"malware\",\n \"block\",\n \"blocked\",\n \"ban\",\n \"banned\",\n \"flag\",\n \"flagged\",\n \"suspend\",\n \"suspended\",\n \"deactivated\",\n \"disabled\",\n]);\n\n/**\n * Brand names that cannot appear anywhere inside a custom slug.\n * Checked via substring match (case-insensitive) to catch phishing patterns\n * like \"paypal-login\", \"apple-verify\", \"google-security-alert\", etc.\n *\n * All entries are lowercase — normalize with `.toLowerCase()` before checking.\n */\nexport const BRANDED_SLUG_TERMS = [\n // ── Finance & payments ──────────────────────────────────────────────\n \"paypal\",\n \"venmo\",\n \"cashapp\",\n \"cash-app\",\n \"zelle\",\n \"stripe\",\n \"square\",\n \"wise\",\n \"revolut\",\n \"coinbase\",\n \"binance\",\n \"kraken\",\n \"robinhood\",\n \"blockchain\",\n \"metamask\",\n \"opensea\",\n \"ledger\",\n\n // ── Banks ───────────────────────────────────────────────────────────\n \"chase\",\n \"wellsfargo\",\n \"wells-fargo\",\n \"bankofamerica\",\n \"bank-of-america\",\n \"citibank\",\n \"hsbc\",\n \"barclays\",\n \"capitalone\",\n \"capital-one\",\n \"usbank\",\n \"us-bank\",\n \"pnc\",\n \"truist\",\n \"schwab\",\n \"fidelity\",\n \"vanguard\",\n \"amex\",\n \"mastercard\",\n \"visa\",\n\n // ── Big tech ────────────────────────────────────────────────────────\n \"google\",\n \"gmail\",\n \"youtube\",\n \"apple\",\n \"icloud\",\n \"itunes\",\n \"microsoft\",\n \"outlook\",\n \"hotmail\",\n \"windows\",\n \"xbox\",\n \"linkedin\",\n \"amazon\",\n \"aws\",\n \"facebook\",\n \"instagram\",\n \"whatsapp\",\n \"messenger\",\n \"meta\",\n \"tiktok\",\n \"snapchat\",\n \"twitter\",\n \"x-com\",\n \"netflix\",\n \"spotify\",\n \"discord\",\n \"telegram\",\n \"signal\",\n \"slack\",\n \"zoom\",\n \"dropbox\",\n \"github\",\n \"gitlab\",\n \"bitbucket\",\n\n // ── E-commerce & delivery ───────────────────────────────────────────\n \"ebay\",\n \"walmart\",\n \"target\",\n \"bestbuy\",\n \"best-buy\",\n \"costco\",\n \"shopify\",\n \"etsy\",\n \"aliexpress\",\n \"alibaba\",\n \"fedex\",\n \"ups\",\n \"usps\",\n \"dhl\",\n\n // ── Telecom & ISP ──────────────────────────────────────────────────\n \"verizon\",\n \"att\",\n \"t-mobile\",\n \"tmobile\",\n \"comcast\",\n \"xfinity\",\n \"spectrum\",\n\n // ── Government & institutions ───────────────────────────────────────\n \"irs\",\n \"ssa\",\n \"medicare\",\n \"medicaid\",\n \"dmv\",\n \"usps\",\n \"fbi\",\n \"cia\",\n \"nsa\",\n \"dhs\",\n \"sec\",\n\n // ── Streaming & gaming ──────────────────────────────────────────────\n \"hulu\",\n \"disney\",\n \"hbomax\",\n \"hbo-max\",\n \"peacock\",\n \"paramount\",\n \"twitch\",\n \"steam\",\n \"playstation\",\n \"nintendo\",\n \"epicgames\",\n \"epic-games\",\n \"roblox\",\n \"fortnite\",\n\n // ── Security & identity ─────────────────────────────────────────────\n \"norton\",\n \"mcafee\",\n \"kaspersky\",\n \"lastpass\",\n \"onepassword\",\n \"1password\",\n \"okta\",\n \"auth0\",\n \"docusign\",\n\n // ── Travel & rideshare ──────────────────────────────────────────────\n \"uber\",\n \"lyft\",\n \"airbnb\",\n \"booking\",\n \"expedia\",\n \"delta\",\n \"united\",\n \"southwest\",\n \"american-airlines\",\n\n // ── Common phishing action words (combined with brand = high signal) ─\n // These are only blocked as part of the substring check, not alone.\n // e.g., \"verify\" alone is fine as a reserved slug; \"apple-verify\" is blocked.\n] as const;\n\n/**\n * Check if a slug contains a protected brand term.\n * Returns the matched brand term, or null if clean.\n */\nexport function findBrandTermInSlug(slug: string): string | null {\n const lower = slug.toLowerCase();\n for (const term of BRANDED_SLUG_TERMS) {\n if (lower.includes(term)) return term;\n }\n return null;\n}\n\n// ── Destination URL validation ─────────────────────────────────────────\n\n/** Private/reserved IP ranges that should never be redirect targets. */\nconst PRIVATE_IP_PATTERNS = [\n /^127\\./, // loopback\n /^10\\./, // class A private\n /^172\\.(1[6-9]|2\\d|3[01])\\./, // class B private\n /^192\\.168\\./, // class C private\n /^0\\./, // \"this\" network\n /^0\\.0\\.0\\.0$/,\n /^169\\.254\\./, // link-local\n /^::1$/, // IPv6 loopback\n /^\\[::1\\]$/,\n];\n\n/** Hosts that are non-routable or reserved for documentation. */\nconst BLOCKED_HOSTS = new Set([\n \"localhost\",\n \"example.com\",\n \"example.org\",\n \"example.net\",\n \"test.com\",\n \"test.org\",\n \"invalid\",\n \"local\",\n]);\n\n/**\n * Validate a destination URL for safety.\n * Expects a fully-formed URL (with protocol).\n * Returns an error message string, or null if valid.\n */\nexport function validateDestinationUrl(url: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return \"Invalid URL\";\n }\n\n // Must be http or https\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return \"Only http and https URLs are allowed\";\n }\n\n const hostname = parsed.hostname.toLowerCase();\n\n // Must contain a dot (blocks \"localhost\", single-label hosts, etc.)\n if (!hostname.includes(\".\")) {\n return \"URL must contain a valid domain name\";\n }\n\n // Minimum hostname length: \"x.xx\" = 4 chars\n if (hostname.length < 4) {\n return \"URL domain is too short\";\n }\n\n // Block private/internal IPs\n for (const pattern of PRIVATE_IP_PATTERNS) {\n if (pattern.test(hostname)) {\n return \"URLs pointing to private or internal addresses are not allowed\";\n }\n }\n\n // Block non-routable / reserved hosts\n if (BLOCKED_HOSTS.has(hostname)) {\n return `\"${hostname}\" is not allowed as a destination`;\n }\n\n // Block self-referencing URLs (redirect loops)\n if (hostname === \"shorten.dev\" || hostname.endsWith(\".shorten.dev\")) {\n return \"Cannot shorten URLs that point to shorten.dev\";\n }\n\n // Block data: and javascript: in the path (defense-in-depth, URL constructor\n // would already reject these but worth being explicit)\n if (parsed.href.toLowerCase().includes(\"javascript:\") || parsed.href.toLowerCase().includes(\"data:\")) {\n return \"URL contains a disallowed scheme\";\n }\n\n return null;\n}\n\nexport const RATE_LIMIT = {\n requests_per_hour: 300,\n} as const;\n\nexport const LINK_STATUSES = [\"active\", \"flagged\"] as const;\n\nexport const API_KEY_SCOPES = [\"read\", \"write\", \"admin\"] as const;\n\nexport const ANALYTICS_PERIODS = [\"7d\", \"30d\", \"90d\"] as const;\n\n\nexport const TIMEZONES = [\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"America/Anchorage\",\n \"Pacific/Honolulu\",\n \"Europe/London\",\n \"Europe/Paris\",\n \"Europe/Berlin\",\n \"Asia/Tokyo\",\n \"Asia/Shanghai\",\n \"Asia/Kolkata\",\n \"Australia/Sydney\",\n \"UTC\",\n] as const;\n","import type { ApiError } from \"@shorten/shared\";\n\nexport class ShortenApiError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: ApiError,\n ) {\n super(body.message);\n this.name = \"ShortenApiError\";\n }\n}\n\nexport class NetworkError extends Error {\n constructor(\n public readonly cause: Error,\n public readonly url: string,\n ) {\n super(`Network error: ${cause.message}`);\n this.name = \"NetworkError\";\n }\n}\n\nexport class ApiClient {\n private apiKey: string;\n private baseUrl: string;\n\n constructor(apiKey: string, baseUrl: string) {\n this.apiKey = apiKey;\n this.baseUrl = baseUrl.replace(/\\/+$/, \"\");\n }\n\n async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== \"\") url.searchParams.set(k, v);\n }\n }\n return this.request(url, { method: \"GET\" });\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request(new URL(`${this.baseUrl}${path}`), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: body ? JSON.stringify(body) : undefined,\n });\n }\n\n private async request<T>(url: URL, init: RequestInit): Promise<T> {\n let res: Response;\n try {\n res = await fetch(url, {\n ...init,\n headers: {\n ...((init.headers as Record<string, string>) ?? {}),\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n } catch (err) {\n throw new NetworkError(\n err instanceof Error ? err : new Error(String(err)),\n url.toString(),\n );\n }\n\n if (!res.ok) {\n let body: ApiError;\n try {\n body = (await res.json()) as ApiError;\n } catch {\n body = {\n error: `HTTP ${res.status}`,\n message: res.statusText || `Request failed with status ${res.status}`,\n status: res.status,\n };\n }\n throw new ShortenApiError(res.status, body);\n }\n\n return (await res.json()) as T;\n }\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport interface ShortenConfig {\n api_key?: string;\n api_url?: string;\n default_format?: \"pretty\" | \"short\" | \"json\";\n copy_to_clipboard?: boolean;\n}\n\nexport const DEFAULT_API_URL = \"https://shorten.dev/api/v1\";\n\nexport const CONFIG_PATH = join(homedir(), \".shorten.json\");\n\nexport function loadConfig(): ShortenConfig {\n try {\n const raw = readFileSync(CONFIG_PATH, \"utf-8\");\n return JSON.parse(raw) as ShortenConfig;\n } catch {\n return {};\n }\n}\n\nexport function saveConfig(config: ShortenConfig): void {\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n}\n\nexport function resolveApiKey(flagKey?: string): string {\n const key = flagKey ?? process.env[\"SHORTEN_API_KEY\"] ?? loadConfig().api_key;\n\n if (!key) {\n console.error(\n \"No API key found. Set SHORTEN_API_KEY or pass --key.\\n\" +\n 'Run \"shorten login\" or visit https://shorten.dev/app/api-keys',\n );\n process.exit(1);\n }\n\n return key;\n}\n\nexport function resolveApiUrl(flagUrl?: string): string {\n return (\n flagUrl ??\n process.env[\"SHORTEN_API_URL\"] ??\n loadConfig().api_url ??\n DEFAULT_API_URL\n );\n}\n","import type { Command } from \"commander\";\nimport { ApiClient } from \"../api/client.js\";\nimport { loadConfig, resolveApiKey, resolveApiUrl } from \"../config.js\";\nimport type { ShortenConfig } from \"../config.js\";\n\nexport interface ResolvedClient {\n client: ApiClient;\n config: ShortenConfig;\n apiKey: string;\n}\n\nexport function resolveClient(cmd: Command): ResolvedClient {\n const globals = cmd.optsWithGlobals() as Record<string, string | undefined>;\n const config = loadConfig();\n const apiKey = resolveApiKey(globals[\"key\"]);\n const apiUrl = resolveApiUrl(globals[\"apiUrl\"]);\n const client = new ApiClient(apiKey, apiUrl);\n return { client, config, apiKey };\n}\n","import pc from \"picocolors\";\n\nexport type OutputFormat = \"pretty\" | \"json\" | \"quiet\";\n\nexport function resolveFormat(opts: {\n json?: boolean;\n quiet?: boolean;\n configDefault?: string;\n}): OutputFormat {\n if (opts.json) return \"json\";\n if (opts.quiet) return \"quiet\";\n if (opts.configDefault === \"json\") return \"json\";\n if (opts.configDefault === \"short\") return \"quiet\";\n return \"pretty\";\n}\n\nexport function success(msg: string): void {\n console.log(pc.green(\"✓\") + \" \" + msg);\n}\n\nexport function error(msg: string): void {\n console.error(pc.red(\"✗\") + \" \" + msg);\n}\n\nexport function info(msg: string): void {\n console.log(pc.dim(msg));\n}\n\nexport function json(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\nexport function table(\n headers: string[],\n rows: string[][],\n columnWidths?: number[],\n): void {\n const widths =\n columnWidths ??\n headers.map((h, i) =>\n Math.max(h.length, ...rows.map((r) => (r[i] ?? \"\").length)),\n );\n\n const header = headers\n .map((h, i) => h.padEnd(widths[i] ?? h.length))\n .join(\" \");\n console.log(pc.bold(header));\n console.log(pc.dim(\"─\".repeat(header.length)));\n\n for (const row of rows) {\n console.log(\n row.map((c, i) => c.padEnd(widths[i] ?? c.length)).join(\" \"),\n );\n }\n}\n\nexport function formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n\nexport function formatDate(iso: string): string {\n return new Date(iso).toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n}\n\nexport function percentBar(pct: number, width = 10): string {\n const filled = Math.round((pct / 100) * width);\n return pc.cyan(\"█\".repeat(filled)) + pc.dim(\"░\".repeat(width - filled));\n}\n","import { ShortenApiError, NetworkError } from \"../api/client.js\";\nimport * as out from \"./output.js\";\n\nexport function handleCommandError(err: unknown): never {\n if (err instanceof ShortenApiError) {\n switch (err.status) {\n case 401:\n out.error(\"Invalid API key. Check your key or generate a new one.\");\n out.info(' Run \"shorten login\" or visit https://shorten.dev/app/api-keys');\n break;\n case 403:\n out.error(\"Forbidden — your API key doesn't have the required scope.\");\n break;\n case 404:\n out.error(err.message || \"Not found.\");\n break;\n case 429:\n out.error(\"Rate limit exceeded. Try again later.\");\n break;\n default:\n out.error(err.message);\n }\n process.exit(1);\n }\n\n if (err instanceof NetworkError) {\n out.error(`Could not connect to the API (${err.url}).`);\n out.info(\" Check your internet connection or use --api-url to set the correct endpoint.\");\n process.exit(1);\n }\n\n const message = err instanceof Error ? err.message : String(err);\n out.error(`Unexpected error: ${message}`);\n process.exit(1);\n}\n","export async function copyToClipboard(text: string): Promise<boolean> {\n try {\n const { default: clipboardy } = await import(\"clipboardy\");\n await clipboardy.write(text);\n return true;\n } catch {\n return false;\n }\n}\n","import type { Command } from \"commander\";\nimport {\n MAX_TAGS_PER_LINK,\n MAX_TAG_LENGTH,\n validateDestinationUrl,\n} from \"@shorten/shared\";\nimport type { CreateLinkRequest, CreateLinkResponse } from \"@shorten/shared\";\nimport { resolveClient } from \"../utils/client-factory.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport { copyToClipboard } from \"../utils/clipboard.js\";\nimport * as out from \"../utils/output.js\";\n\ninterface ShortenOptions {\n slug?: string;\n tag?: string[];\n quiet?: boolean;\n json?: boolean;\n copy?: boolean;\n}\n\nfunction normalizeUrl(raw: string): string {\n if (!/^https?:\\/\\//i.test(raw)) {\n return `https://${raw}`;\n }\n return raw;\n}\n\nexport function registerShortenCommand(program: Command): void {\n const cmd = program\n .command(\"create\", { isDefault: true, hidden: true })\n .argument(\"<url>\", \"URL to shorten\")\n .option(\"-s, --slug <slug>\", \"Custom slug\")\n .option(\"-t, --tag <tag>\", \"Add tags (repeatable)\", (val: string, acc: string[]) => [...acc, val], [] as string[])\n .option(\"-q, --quiet\", \"Output only the short URL\")\n .option(\"-j, --json\", \"Output as JSON\")\n .option(\"--no-copy\", \"Don't copy to clipboard\")\n .action(async (rawUrl: string, opts: ShortenOptions) => {\n const { client, config } = resolveClient(cmd);\n const format = out.resolveFormat({\n json: opts.json,\n quiet: opts.quiet,\n configDefault: config.default_format,\n });\n\n // Normalize and validate URL client-side\n const url = normalizeUrl(rawUrl);\n const validationError = validateDestinationUrl(url);\n if (validationError) {\n out.error(validationError);\n process.exit(1);\n return;\n }\n\n const tags = opts.tag;\n if (tags && tags.length > MAX_TAGS_PER_LINK) {\n out.error(`Too many tags: got ${tags.length}, max is ${MAX_TAGS_PER_LINK}`);\n process.exit(1);\n return;\n }\n const longTag = tags?.find((t) => t.length > MAX_TAG_LENGTH);\n if (longTag) {\n out.error(`Tag \"${longTag}\" exceeds ${MAX_TAG_LENGTH} characters`);\n process.exit(1);\n return;\n }\n\n const body: CreateLinkRequest = {\n destination_url: url,\n custom_slug: opts.slug,\n tags,\n };\n\n try {\n const res = await client.post<CreateLinkResponse>(\"/links\", body);\n\n if (format === \"json\") {\n out.json(res);\n return;\n }\n\n if (format === \"quiet\") {\n console.log(res.short_url);\n return;\n }\n\n const shouldCopy =\n opts.copy !== false && (config.copy_to_clipboard ?? true);\n\n let suffix = \"\";\n if (shouldCopy) {\n const copied = await copyToClipboard(res.short_url);\n if (copied) suffix = \" (copied to clipboard)\";\n }\n\n out.success(`${res.short_url}${suffix}`);\n\n if (res.link.tags.length > 0) {\n out.info(` tags: ${res.link.tags.join(\", \")}`);\n }\n } catch (err) {\n handleCommandError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport type { Link, PaginatedResponse } from \"@shorten/shared\";\nimport { resolveClient } from \"../utils/client-factory.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport * as out from \"../utils/output.js\";\n\ninterface ListOptions {\n limit?: string;\n status?: string;\n search?: string;\n sort?: string;\n order?: string;\n json?: boolean;\n}\n\nexport function registerListCommand(program: Command): void {\n const cmd = program\n .command(\"list\")\n .description(\"List your links\")\n .option(\"-n, --limit <n>\", \"Number of results (default: 10, max: 100)\")\n .option(\"--status <status>\", \"Filter by active or flagged\")\n .option(\"--search <query>\", \"Search in slug, URL, and tags\")\n .option(\n \"--sort <field>\",\n \"Sort by created_at or slug\",\n )\n .option(\"--order <dir>\", \"asc or desc (default: desc)\")\n .option(\"-j, --json\", \"Output as JSON\")\n .action(async (opts: ListOptions) => {\n const { client } = resolveClient(cmd);\n\n const params: Record<string, string> = {};\n if (opts.limit) params[\"limit\"] = opts.limit;\n if (opts.status) params[\"status\"] = opts.status;\n if (opts.search) params[\"search\"] = opts.search;\n if (opts.sort) params[\"sort\"] = opts.sort;\n if (opts.order) params[\"order\"] = opts.order;\n\n try {\n const res = await client.get<PaginatedResponse<Link>>(\n \"/links\",\n params,\n );\n\n if (opts.json) {\n out.json(res);\n return;\n }\n\n if (res.data.length === 0) {\n out.info(\"No links found.\");\n return;\n }\n\n out.table(\n [\"Slug\", \"Destination\", \"Status\", \"Created\"],\n res.data.map((link) => [\n link.slug,\n truncate(link.destination_url, 40),\n link.status,\n out.formatDate(link.created_at),\n ]),\n );\n\n out.info(\n `\\nShowing ${res.data.length} of ${res.total} links (page ${res.page}/${res.total_pages})`,\n );\n } catch (err) {\n handleCommandError(err);\n }\n });\n}\n\nfunction truncate(str: string, maxLen: number): string {\n if (str.length <= maxLen) return str;\n return str.slice(0, maxLen - 1) + \"…\";\n}\n","import type { Command } from \"commander\";\nimport type { AnalyticsResponse, DeviceEntry } from \"@shorten/shared\";\nimport { resolveClient } from \"../utils/client-factory.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport * as out from \"../utils/output.js\";\nimport pc from \"picocolors\";\n\ninterface StatsOptions {\n period?: string;\n json?: boolean;\n}\n\nexport function registerStatsCommand(program: Command): void {\n const cmd = program\n .command(\"stats <slug>\")\n .description(\"View click analytics for a link\")\n .option(\n \"-p, --period <period>\",\n \"Time window: 7d, 30d, or 90d (default: 7d)\",\n )\n .option(\"-j, --json\", \"Output as JSON\")\n .action(async (slug: string, opts: StatsOptions) => {\n const { client } = resolveClient(cmd);\n\n const params: Record<string, string> = {};\n if (opts.period) params[\"period\"] = opts.period;\n\n try {\n const res = await client.get<AnalyticsResponse>(\n `/links/${slug}/analytics`,\n params,\n );\n\n if (opts.json) {\n out.json(res);\n return;\n }\n\n const period = res.period;\n console.log(\n `\\n${pc.bold(`shorten.dev/${res.slug}`)} — ${pc.cyan(out.formatNumber(res.total_clicks))} clicks (${period})`,\n );\n console.log(\n pc.dim(\n ` ${out.formatNumber(res.unique_visitors)} unique visitors`,\n ),\n );\n\n console.log();\n\n const colWidth = 24;\n const maxRows = 5;\n\n // Build columns\n const countries = res.top_countries.slice(0, maxRows);\n const referrers = res.top_referrers.slice(0, maxRows);\n\n const totalForPct = res.total_clicks || 1;\n\n // Header\n console.log(\n pc.bold(\" Top countries\".padEnd(colWidth)) +\n pc.bold(\"Top referrers\".padEnd(colWidth)) +\n pc.bold(\"Devices\"),\n );\n\n // Rows\n const deviceEntries = res.top_devices.slice(0, maxRows).map((d: DeviceEntry) => [\n d.device.charAt(0).toUpperCase() + d.device.slice(1),\n d.count,\n ] as const);\n\n for (let i = 0; i < maxRows; i++) {\n let line = \" \";\n\n // Country column\n const country = countries[i];\n if (country) {\n const pct = Math.round(\n (country.count / totalForPct) * 100,\n );\n line += `${country.country.padEnd(6)}${String(pct).padStart(3)}%`.padEnd(\n colWidth,\n );\n } else {\n line += \"\".padEnd(colWidth);\n }\n\n // Referrer column\n const referrer = referrers[i];\n if (referrer) {\n const pct = Math.round(\n (referrer.count / totalForPct) * 100,\n );\n line += `${referrer.referrer.padEnd(16)}${String(pct).padStart(3)}%`.padEnd(\n colWidth,\n );\n } else {\n line += \"\".padEnd(colWidth);\n }\n\n // Device column\n const device = deviceEntries[i];\n if (device) {\n const pct = Math.round(\n (device[1] / totalForPct) * 100,\n );\n line += `${device[0].padEnd(10)}${String(pct).padStart(3)}%`;\n }\n\n console.log(line);\n }\n\n console.log();\n } catch (err) {\n handleCommandError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport type { UsageResponse } from \"@shorten/shared\";\nimport { resolveClient } from \"../utils/client-factory.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport * as out from \"../utils/output.js\";\nimport pc from \"picocolors\";\n\ninterface WhoamiOptions {\n json?: boolean;\n}\n\nexport function registerWhoamiCommand(program: Command): void {\n const cmd = program\n .command(\"whoami\")\n .description(\"Display authenticated user and API key info\")\n .option(\"-j, --json\", \"Output as JSON\")\n .action(async (opts: WhoamiOptions) => {\n const { client, apiKey } = resolveClient(cmd);\n\n try {\n const usage = await client.get<UsageResponse>(\"/usage\");\n\n if (opts.json) {\n out.json({\n key_prefix: maskKey(apiKey),\n rate_limit: usage,\n });\n return;\n }\n\n console.log(\n ` Key: ${pc.cyan(maskKey(apiKey))}`,\n );\n console.log(\n ` Rate limit: ${pc.bold(String(usage.remaining))}/${usage.limit} remaining`,\n );\n console.log(\n ` Resets: ${out.formatDate(usage.reset_at)}`,\n );\n } catch (err) {\n handleCommandError(err);\n }\n });\n}\n\nfunction maskKey(key: string): string {\n if (key.length <= 8) return key;\n return key.slice(0, 3) + \"…\" + key.slice(-4);\n}\n","import type { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { ApiClient } from \"../api/client.js\";\nimport { loadConfig, saveConfig, resolveApiUrl } from \"../config.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport * as out from \"../utils/output.js\";\n\nexport function registerLoginCommand(program: Command): void {\n const cmd = program\n .command(\"login\")\n .description(\"Authenticate with your API key\")\n .action(async () => {\n const globals = cmd.optsWithGlobals() as Record<string, string | undefined>;\n const apiUrl = resolveApiUrl(globals[\"apiUrl\"]);\n\n // Derive the web app URL from the API URL\n const appUrl = apiUrl.replace(/\\/api\\/v1\\/?$/, \"/app/api-keys\");\n\n if (!process.stdin.isTTY) {\n out.error(\n \"Non-interactive terminal detected. Use --key flag instead:\\n\" +\n \" shorten --key sk_your_key whoami\",\n );\n process.exit(1);\n return;\n }\n\n console.log(`\\nOpen your browser to get an API key:\\n ${appUrl}\\n`);\n\n // Try to open browser automatically (best-effort)\n try {\n const { exec } = await import(\"node:child_process\");\n const openCmd =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"start\"\n : \"xdg-open\";\n exec(`${openCmd} ${appUrl}`);\n } catch {\n // Ignore — user can open manually\n }\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const key = await new Promise<string>((resolve) => {\n rl.question(\"Paste your API key: \", (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n\n if (!key) {\n out.error(\"No key provided.\");\n process.exit(1);\n return;\n }\n\n if (!key.startsWith(\"sk_\")) {\n out.error('Invalid API key format. Keys start with \"sk_\".');\n process.exit(1);\n return;\n }\n\n // Validate the key by calling /usage, then save on success\n try {\n const client = new ApiClient(key, apiUrl);\n await client.get(\"/usage\");\n\n const config = loadConfig();\n config.api_key = key;\n saveConfig(config);\n\n out.success(\"Logged in successfully. API key saved to ~/.shorten.json\");\n } catch (err) {\n out.error(\"Could not verify the API key.\");\n handleCommandError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { unlinkSync } from \"node:fs\";\nimport {\n loadConfig,\n saveConfig,\n CONFIG_PATH,\n type ShortenConfig,\n} from \"../config.js\";\nimport * as out from \"../utils/output.js\";\n\nconst VALID_KEYS: (keyof ShortenConfig)[] = [\n \"api_key\",\n \"api_url\",\n \"default_format\",\n \"copy_to_clipboard\",\n];\n\nfunction maskValue(key: string, value: unknown): string {\n if (key === \"api_key\" && typeof value === \"string\" && value.length > 8) {\n return value.slice(0, 3) + \"…\" + value.slice(-4);\n }\n return String(value);\n}\n\nexport function registerConfigCommand(program: Command): void {\n const configCmd = program\n .command(\"config\")\n .description(\"Manage CLI configuration\");\n\n configCmd\n .command(\"list\")\n .description(\"Show all configuration values\")\n .action(() => {\n const config = loadConfig();\n const entries = Object.entries(config);\n\n if (entries.length === 0) {\n out.info(`No configuration found. Config file: ${CONFIG_PATH}`);\n return;\n }\n\n for (const [key, value] of entries) {\n console.log(`${key} = ${maskValue(key, value)}`);\n }\n });\n\n configCmd\n .command(\"get <key>\")\n .description(\"Get a configuration value\")\n .action((key: string) => {\n if (!VALID_KEYS.includes(key as keyof ShortenConfig)) {\n out.error(`Unknown config key: \"${key}\". Valid keys: ${VALID_KEYS.join(\", \")}`);\n process.exit(1);\n return;\n }\n\n const config = loadConfig();\n const value = config[key as keyof ShortenConfig];\n\n if (value === undefined) {\n out.info(`\"${key}\" is not set.`);\n return;\n }\n\n console.log(maskValue(key, value));\n });\n\n configCmd\n .command(\"set <key> <value>\")\n .description(\"Set a configuration value\")\n .action((key: string, value: string) => {\n if (!VALID_KEYS.includes(key as keyof ShortenConfig)) {\n out.error(`Unknown config key: \"${key}\". Valid keys: ${VALID_KEYS.join(\", \")}`);\n process.exit(1);\n return;\n }\n\n const config = loadConfig();\n\n if (key === \"copy_to_clipboard\") {\n (config as Record<string, unknown>)[key] = value === \"true\";\n } else {\n (config as Record<string, unknown>)[key] = value;\n }\n\n saveConfig(config);\n out.success(`Set ${key} = ${maskValue(key, value)}`);\n });\n\n configCmd\n .command(\"reset\")\n .description(\"Delete the configuration file\")\n .action(() => {\n try {\n unlinkSync(CONFIG_PATH);\n out.success(`Deleted ${CONFIG_PATH}`);\n } catch {\n out.info(\"No configuration file to delete.\");\n }\n });\n\n configCmd\n .command(\"path\")\n .description(\"Print the configuration file path\")\n .action(() => {\n console.log(CONFIG_PATH);\n });\n}\n","import { createProgram } from \"./cli.js\";\n\nconst program = createProgram();\nprogram.parseAsync(process.argv);\n"],"mappings":"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","names":["program","program","pc","program","pc","program","program","program","program"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../../shared/src/constants.ts","../../shared/src/errors.ts","../src/api/client.ts","../src/config.ts","../src/utils/client-factory.ts","../src/utils/output.ts","../src/utils/errors.ts","../src/utils/clipboard.ts","../src/commands/shorten.ts","../src/commands/list.ts","../src/commands/stats.ts","../src/commands/whoami.ts","../src/commands/login.ts","../src/commands/config.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { registerShortenCommand } from \"./commands/shorten.js\";\nimport { registerListCommand } from \"./commands/list.js\";\nimport { registerStatsCommand } from \"./commands/stats.js\";\nimport { registerWhoamiCommand } from \"./commands/whoami.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerConfigCommand } from \"./commands/config.js\";\n\ndeclare const __CLI_VERSION__: string;\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"shorten\")\n .description(\"Shorten URLs from your terminal\")\n .version(__CLI_VERSION__)\n .option(\"-k, --key <key>\", \"API key (overrides SHORTEN_API_KEY)\")\n .option(\"--api-url <url>\", \"API base URL (overrides SHORTEN_API_URL)\")\n .option(\"--no-color\", \"Disable colored output\")\n .configureOutput({ writeErr: (str) => process.stderr.write(str) });\n\n registerShortenCommand(program);\n registerListCommand(program);\n registerStatsCommand(program);\n registerWhoamiCommand(program);\n registerLoginCommand(program);\n registerConfigCommand(program);\n\n return program;\n}\n","export const SLUG_LENGTH = 7;\nexport const SLUG_MIN_LENGTH = 3;\nexport const SLUG_MAX_LENGTH = 50;\nexport const SLUG_PATTERN = /^[a-zA-Z0-9_-]+$/;\nexport const BASE_URL = \"https://r.shorten.dev\";\n\nexport const MAX_TAGS_PER_LINK = 3;\nexport const MAX_TAG_LENGTH = 50;\n\n/**\n * Reserved slugs that cannot be used as custom short links.\n * Covers: app routes, common subdomains, platform terms, admin/system paths,\n * well-known paths, social/SEO, legal, and potentially confusing terms.\n *\n * All entries are lowercase — check with `.toLowerCase()` before comparing.\n */\nexport const RESERVED_SLUGS = new Set([\n // ── App routes & pages ──────────────────────────────────────────────\n \"app\",\n \"api\",\n \"auth\",\n \"login\",\n \"logout\",\n \"signup\",\n \"register\",\n \"signin\",\n \"signout\",\n \"sign-in\",\n \"sign-up\",\n \"sign-out\",\n \"log-in\",\n \"log-out\",\n \"callback\",\n \"dashboard\",\n \"settings\",\n \"account\",\n \"profile\",\n \"billing\",\n \"upgrade\",\n \"pricing\",\n \"plans\",\n \"onboarding\",\n \"welcome\",\n \"verify\",\n \"confirm\",\n \"reset\",\n \"password\",\n \"forgot\",\n \"forgot-password\",\n \"reset-password\",\n \"change-password\",\n \"invite\",\n \"join\",\n\n // ── Public pages ────────────────────────────────────────────────────\n \"about\",\n \"about-us\",\n \"donate\",\n \"docs\",\n \"documentation\",\n \"support\",\n \"help\",\n \"contact\",\n \"contact-us\",\n \"faq\",\n \"blog\",\n \"changelog\",\n \"updates\",\n \"news\",\n \"press\",\n \"careers\",\n \"jobs\",\n \"team\",\n \"partners\",\n \"affiliates\",\n \"referrals\",\n\n // ── Legal ───────────────────────────────────────────────────────────\n \"terms\",\n \"terms-of-service\",\n \"tos\",\n \"privacy\",\n \"privacy-policy\",\n \"cookies\",\n \"cookie-policy\",\n \"gdpr\",\n \"dmca\",\n \"legal\",\n \"compliance\",\n \"acceptable-use\",\n \"aup\",\n\n // ── Admin & system ──────────────────────────────────────────────────\n \"admin\",\n \"administrator\",\n \"root\",\n \"system\",\n \"sys\",\n \"internal\",\n \"debug\",\n \"dev\",\n \"staging\",\n \"test\",\n \"sandbox\",\n \"demo\",\n \"console\",\n \"panel\",\n \"manage\",\n \"manager\",\n \"config\",\n \"configuration\",\n \"setup\",\n \"install\",\n \"cron\",\n \"worker\",\n \"workers\",\n \"queue\",\n \"health\",\n \"healthcheck\",\n \"health-check\",\n \"status\",\n \"uptime\",\n \"metrics\",\n \"monitor\",\n \"monitoring\",\n \"logs\",\n \"trace\",\n\n // ── API & developer ─────────────────────────────────────────────────\n \"api-keys\",\n \"apikeys\",\n \"api-key\",\n \"tokens\",\n \"token\",\n \"oauth\",\n \"oauth2\",\n \"openid\",\n \"sso\",\n \"saml\",\n \"webhooks\",\n \"webhook\",\n \"graphql\",\n \"rest\",\n \"sdk\",\n \"cli\",\n \"developer\",\n \"developers\",\n \"playground\",\n \"explorer\",\n \"schema\",\n \"swagger\",\n \"openapi\",\n \"redoc\",\n\n // ── Analytics & data ────────────────────────────────────────────────\n \"analytics\",\n \"stats\",\n \"statistics\",\n \"reports\",\n \"report\",\n \"insights\",\n \"events\",\n \"clicks\",\n \"links\",\n \"link\",\n \"urls\",\n \"url\",\n \"domains\",\n \"domain\",\n\n // ── User & account ──────────────────────────────────────────────────\n \"user\",\n \"users\",\n \"me\",\n \"my\",\n \"you\",\n \"self\",\n \"notifications\",\n \"inbox\",\n \"messages\",\n \"mail\",\n \"email\",\n \"preferences\",\n \"subscription\",\n \"subscriptions\",\n\n // ── Social & SEO ────────────────────────────────────────────────────\n \"share\",\n \"embed\",\n \"widget\",\n \"follow\",\n \"like\",\n \"star\",\n \"bookmark\",\n \"feed\",\n \"rss\",\n \"atom\",\n \"sitemap\",\n \"robots\",\n \"manifest\",\n \"humans\",\n \"ads\",\n \"sponsors\",\n\n // ── Well-known paths ────────────────────────────────────────────────\n \"favicon\",\n \"wp-admin\",\n \"wp-login\",\n \"wp-content\",\n \"wordpress\",\n \"xmlrpc\",\n \"cgi-bin\",\n \"phpmyadmin\",\n \"env\",\n \"git\",\n \"svn\",\n \"htaccess\",\n \"htpasswd\",\n \"ssh\",\n \"ftp\",\n \"cpanel\",\n \"webmail\",\n \"autodiscover\",\n \"well-known\",\n \"_next\",\n\n // ── Common subdomains (if ever used as slugs) ───────────────────────\n \"www\",\n \"mail\",\n \"ftp\",\n \"cdn\",\n \"assets\",\n \"static\",\n \"media\",\n \"images\",\n \"img\",\n \"files\",\n \"download\",\n \"downloads\",\n \"upload\",\n \"uploads\",\n \"storage\",\n\n // ── Brand protection ────────────────────────────────────────────────\n \"shorten\",\n \"shorten-dev\",\n \"shortendev\",\n \"official\",\n \"verified\",\n\n // ── Potentially confusing / misleading ──────────────────────────────\n \"null\",\n \"undefined\",\n \"nil\",\n \"none\",\n \"true\",\n \"false\",\n \"nan\",\n \"infinity\",\n \"error\",\n \"404\",\n \"500\",\n \"403\",\n \"401\",\n \"new\",\n \"create\",\n \"edit\",\n \"delete\",\n \"remove\",\n \"update\",\n \"search\",\n \"home\",\n \"index\",\n \"default\",\n \"public\",\n \"private\",\n \"example\",\n \"test\",\n \"temp\",\n \"tmp\",\n\n // ── Payment & commerce ──────────────────────────────────────────────\n \"checkout\",\n \"payment\",\n \"payments\",\n \"pay\",\n \"invoice\",\n \"invoices\",\n \"receipt\",\n \"refund\",\n \"stripe\",\n \"paypal\",\n\n // ── Security-sensitive ──────────────────────────────────────────────\n \"security\",\n \"vulnerability\",\n \"report\",\n \"abuse\",\n \"spam\",\n \"phishing\",\n \"malware\",\n \"block\",\n \"blocked\",\n \"ban\",\n \"banned\",\n \"flag\",\n \"flagged\",\n \"suspend\",\n \"suspended\",\n \"deactivated\",\n \"disabled\",\n]);\n\n/**\n * Brand names that cannot appear anywhere inside a custom slug.\n * Checked via substring match (case-insensitive) to catch phishing patterns\n * like \"paypal-login\", \"apple-verify\", \"google-security-alert\", etc.\n *\n * All entries are lowercase — normalize with `.toLowerCase()` before checking.\n */\nexport const BRANDED_SLUG_TERMS = [\n // ── Finance & payments ──────────────────────────────────────────────\n \"paypal\",\n \"venmo\",\n \"cashapp\",\n \"cash-app\",\n \"zelle\",\n \"stripe\",\n \"square\",\n \"wise\",\n \"revolut\",\n \"coinbase\",\n \"binance\",\n \"kraken\",\n \"robinhood\",\n \"blockchain\",\n \"metamask\",\n \"opensea\",\n \"ledger\",\n\n // ── Banks ───────────────────────────────────────────────────────────\n \"chase\",\n \"wellsfargo\",\n \"wells-fargo\",\n \"bankofamerica\",\n \"bank-of-america\",\n \"citibank\",\n \"hsbc\",\n \"barclays\",\n \"capitalone\",\n \"capital-one\",\n \"usbank\",\n \"us-bank\",\n \"pnc\",\n \"truist\",\n \"schwab\",\n \"fidelity\",\n \"vanguard\",\n \"amex\",\n \"mastercard\",\n \"visa\",\n\n // ── Big tech ────────────────────────────────────────────────────────\n \"google\",\n \"gmail\",\n \"youtube\",\n \"apple\",\n \"icloud\",\n \"itunes\",\n \"microsoft\",\n \"outlook\",\n \"hotmail\",\n \"windows\",\n \"xbox\",\n \"linkedin\",\n \"amazon\",\n \"aws\",\n \"facebook\",\n \"instagram\",\n \"whatsapp\",\n \"messenger\",\n \"meta\",\n \"tiktok\",\n \"snapchat\",\n \"twitter\",\n \"x-com\",\n \"netflix\",\n \"spotify\",\n \"discord\",\n \"telegram\",\n \"signal\",\n \"slack\",\n \"zoom\",\n \"dropbox\",\n \"github\",\n \"gitlab\",\n \"bitbucket\",\n\n // ── E-commerce & delivery ───────────────────────────────────────────\n \"ebay\",\n \"walmart\",\n \"target\",\n \"bestbuy\",\n \"best-buy\",\n \"costco\",\n \"shopify\",\n \"etsy\",\n \"aliexpress\",\n \"alibaba\",\n \"fedex\",\n \"ups\",\n \"usps\",\n \"dhl\",\n\n // ── Telecom & ISP ──────────────────────────────────────────────────\n \"verizon\",\n \"att\",\n \"t-mobile\",\n \"tmobile\",\n \"comcast\",\n \"xfinity\",\n \"spectrum\",\n\n // ── Government & institutions ───────────────────────────────────────\n \"irs\",\n \"ssa\",\n \"medicare\",\n \"medicaid\",\n \"dmv\",\n \"usps\",\n \"fbi\",\n \"cia\",\n \"nsa\",\n \"dhs\",\n \"sec\",\n\n // ── Streaming & gaming ──────────────────────────────────────────────\n \"hulu\",\n \"disney\",\n \"hbomax\",\n \"hbo-max\",\n \"peacock\",\n \"paramount\",\n \"twitch\",\n \"steam\",\n \"playstation\",\n \"nintendo\",\n \"epicgames\",\n \"epic-games\",\n \"roblox\",\n \"fortnite\",\n\n // ── Security & identity ─────────────────────────────────────────────\n \"norton\",\n \"mcafee\",\n \"kaspersky\",\n \"lastpass\",\n \"onepassword\",\n \"1password\",\n \"okta\",\n \"auth0\",\n \"docusign\",\n\n // ── Travel & rideshare ──────────────────────────────────────────────\n \"uber\",\n \"lyft\",\n \"airbnb\",\n \"booking\",\n \"expedia\",\n \"delta\",\n \"united\",\n \"southwest\",\n \"american-airlines\",\n\n // ── Common phishing action words (combined with brand = high signal) ─\n // These are only blocked as part of the substring check, not alone.\n // e.g., \"verify\" alone is fine as a reserved slug; \"apple-verify\" is blocked.\n] as const;\n\n/**\n * Check if a slug contains a protected brand term.\n * Returns the matched brand term, or null if clean.\n */\nexport function findBrandTermInSlug(slug: string): string | null {\n const lower = slug.toLowerCase();\n for (const term of BRANDED_SLUG_TERMS) {\n if (lower.includes(term)) return term;\n }\n return null;\n}\n\n// ── Destination URL validation ─────────────────────────────────────────\n\n/** Private/reserved IP ranges that should never be redirect targets. */\nconst PRIVATE_IP_PATTERNS = [\n /^127\\./, // loopback\n /^10\\./, // class A private\n /^172\\.(1[6-9]|2\\d|3[01])\\./, // class B private\n /^192\\.168\\./, // class C private\n /^0\\./, // \"this\" network\n /^0\\.0\\.0\\.0$/,\n /^169\\.254\\./, // link-local\n /^::1$/, // IPv6 loopback\n /^\\[::1\\]$/,\n];\n\n/** Hosts that are non-routable or reserved for documentation. */\nconst BLOCKED_HOSTS = new Set([\n \"localhost\",\n \"example.com\",\n \"example.org\",\n \"example.net\",\n \"test.com\",\n \"test.org\",\n \"invalid\",\n \"local\",\n]);\n\n/**\n * Validate a destination URL for safety.\n * Expects a fully-formed URL (with protocol).\n * Returns an error message string, or null if valid.\n */\nexport function validateDestinationUrl(url: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return \"Invalid URL\";\n }\n\n // Must be http or https\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return \"Only http and https URLs are allowed\";\n }\n\n const hostname = parsed.hostname.toLowerCase();\n\n // Must contain a dot (blocks \"localhost\", single-label hosts, etc.)\n if (!hostname.includes(\".\")) {\n return \"URL must contain a valid domain name\";\n }\n\n // Minimum hostname length: \"x.xx\" = 4 chars\n if (hostname.length < 4) {\n return \"URL domain is too short\";\n }\n\n // Block private/internal IPs\n for (const pattern of PRIVATE_IP_PATTERNS) {\n if (pattern.test(hostname)) {\n return \"URLs pointing to private or internal addresses are not allowed\";\n }\n }\n\n // Block non-routable / reserved hosts\n if (BLOCKED_HOSTS.has(hostname)) {\n return `\"${hostname}\" is not allowed as a destination`;\n }\n\n // Block self-referencing URLs (redirect loops)\n if (hostname === \"shorten.dev\" || hostname.endsWith(\".shorten.dev\")) {\n return \"Cannot shorten URLs that point to shorten.dev\";\n }\n\n // Block data: and javascript: in the path (defense-in-depth, URL constructor\n // would already reject these but worth being explicit)\n if (parsed.href.toLowerCase().includes(\"javascript:\") || parsed.href.toLowerCase().includes(\"data:\")) {\n return \"URL contains a disallowed scheme\";\n }\n\n return null;\n}\n\nexport const RATE_LIMIT = {\n requests_per_hour: 300,\n} as const;\n\nexport const LINK_STATUSES = [\"active\", \"flagged\"] as const;\n\nexport const API_KEY_SCOPES = [\"read\", \"write\", \"admin\"] as const;\n\nexport const ANALYTICS_PERIODS = [\"7d\", \"30d\", \"90d\"] as const;\n\n// ── KV cache ──────────────────────────────────────────────────────────\nexport const KV_CACHE_TTL_SECONDS = 86400; // 24 hours\n\n// ── API key format ────────────────────────────────────────────────────\nexport const API_KEY_PREFIX = \"sk_\";\nexport const API_KEY_BYTE_LENGTH = 32;\nexport const API_KEY_PREFIX_DISPLAY_LENGTH = 8;\n\n// ── Click events ──────────────────────────────────────────────────────\nexport const COUNTRY_UNKNOWN = \"XX\";\n\n// ── Pagination ────────────────────────────────────────────────────────\nexport const PAGE_LIMIT_DEFAULT = 10;\nexport const PAGE_LIMIT_MAX = 50;\n\n// ── Postgres / PostgREST error codes ──────────────────────────────────\nexport const PG_UNIQUE_VIOLATION = \"23505\";\nexport const PGRST_NO_ROWS = \"PGRST116\";\n\nexport const TIMEZONES = [\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"America/Anchorage\",\n \"Pacific/Honolulu\",\n \"Europe/London\",\n \"Europe/Paris\",\n \"Europe/Berlin\",\n \"Asia/Tokyo\",\n \"Asia/Shanghai\",\n \"Asia/Kolkata\",\n \"Australia/Sydney\",\n \"UTC\",\n] as const;\n\n// ── Slug path validation ────────────────────────────────────────────────\n\nconst STATIC_EXTENSION = /\\.\\w{1,10}$/;\n\n/**\n * Validate whether a string is a valid short link slug.\n * Accepts both \"/my-slug\" (pathname) and \"my-slug\" (raw slug).\n * Returns the slug if valid, null if reserved/invalid/nested/static.\n *\n * Used by API route handlers for custom slug validation during link creation.\n */\nexport function isSlugPath(input: string): string | null {\n if (input === \"/\") return null;\n\n const segment = input.startsWith(\"/\") ? input.slice(1) : input;\n\n // Nested paths (e.g., /api/v1/links) are never slugs\n if (segment.includes(\"/\")) return null;\n\n // Static assets (e.g., favicon.ico)\n if (STATIC_EXTENSION.test(segment)) return null;\n\n // Must match slug character pattern and length\n if (!SLUG_PATTERN.test(segment)) return null;\n if (segment.length < SLUG_MIN_LENGTH || segment.length > SLUG_MAX_LENGTH) return null;\n\n // Reserved slugs\n if (RESERVED_SLUGS.has(segment.toLowerCase())) return null;\n\n // Brand protection\n if (findBrandTermInSlug(segment)) return null;\n\n return segment;\n}\n","import type { ApiError } from \"./types/api\";\n\nexport class ShortenApiError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: ApiError,\n ) {\n super(body.message);\n this.name = \"ShortenApiError\";\n }\n}\n\nexport class NetworkError extends Error {\n constructor(\n public readonly cause: Error,\n public readonly url: string,\n ) {\n super(`Network error: ${cause.message}`);\n this.name = \"NetworkError\";\n }\n}\n","import type { ApiError } from \"@shorten/shared\";\nimport { ShortenApiError, NetworkError } from \"@shorten/shared\";\n\nexport { ShortenApiError, NetworkError };\n\nexport class ApiClient {\n private apiKey: string;\n private baseUrl: string;\n\n constructor(apiKey: string, baseUrl: string) {\n this.apiKey = apiKey;\n this.baseUrl = baseUrl.replace(/\\/+$/, \"\");\n }\n\n async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== \"\") url.searchParams.set(k, v);\n }\n }\n return this.request(url, { method: \"GET\" });\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request(new URL(`${this.baseUrl}${path}`), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: body ? JSON.stringify(body) : undefined,\n });\n }\n\n private async request<T>(url: URL, init: RequestInit): Promise<T> {\n let res: Response;\n try {\n res = await fetch(url, {\n ...init,\n headers: {\n ...((init.headers as Record<string, string>) ?? {}),\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n } catch (err) {\n throw new NetworkError(\n err instanceof Error ? err : new Error(String(err)),\n url.toString(),\n );\n }\n\n if (!res.ok) {\n let body: ApiError;\n try {\n body = (await res.json()) as ApiError;\n } catch {\n body = {\n error: `HTTP ${res.status}`,\n message: res.statusText || `Request failed with status ${res.status}`,\n status: res.status,\n };\n }\n throw new ShortenApiError(res.status, body);\n }\n\n return (await res.json()) as T;\n }\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport interface ShortenConfig {\n api_key?: string;\n api_url?: string;\n default_format?: \"pretty\" | \"short\" | \"json\";\n copy_to_clipboard?: boolean;\n}\n\nexport const DEFAULT_API_URL = \"https://shorten.dev/api/v1\";\n\nexport const CONFIG_PATH = join(homedir(), \".shorten.json\");\n\nexport function loadConfig(): ShortenConfig {\n try {\n const raw = readFileSync(CONFIG_PATH, \"utf-8\");\n return JSON.parse(raw) as ShortenConfig;\n } catch {\n return {};\n }\n}\n\nexport function saveConfig(config: ShortenConfig): void {\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n}\n\nexport function resolveApiKey(flagKey?: string): string {\n const key = flagKey ?? process.env[\"SHORTEN_API_KEY\"] ?? loadConfig().api_key;\n\n if (!key) {\n console.error(\n \"No API key found. Set SHORTEN_API_KEY or pass --key.\\n\" +\n 'Run \"shorten login\" or visit https://shorten.dev/api-keys',\n );\n process.exit(1);\n }\n\n return key;\n}\n\nexport function resolveApiUrl(flagUrl?: string): string {\n return (\n flagUrl ??\n process.env[\"SHORTEN_API_URL\"] ??\n loadConfig().api_url ??\n DEFAULT_API_URL\n );\n}\n","import type { Command } from \"commander\";\nimport { ApiClient } from \"../api/client.js\";\nimport { loadConfig, resolveApiKey, resolveApiUrl } from \"../config.js\";\nimport type { ShortenConfig } from \"../config.js\";\n\nexport interface ResolvedClient {\n client: ApiClient;\n config: ShortenConfig;\n apiKey: string;\n}\n\nexport function resolveClient(cmd: Command): ResolvedClient {\n const globals = cmd.optsWithGlobals() as Record<string, string | undefined>;\n const config = loadConfig();\n const apiKey = resolveApiKey(globals[\"key\"]);\n const apiUrl = resolveApiUrl(globals[\"apiUrl\"]);\n const client = new ApiClient(apiKey, apiUrl);\n return { client, config, apiKey };\n}\n","import pc from \"picocolors\";\n\nexport type OutputFormat = \"pretty\" | \"json\" | \"quiet\";\n\nexport function resolveFormat(opts: {\n json?: boolean;\n quiet?: boolean;\n configDefault?: string;\n}): OutputFormat {\n if (opts.json) return \"json\";\n if (opts.quiet) return \"quiet\";\n if (opts.configDefault === \"json\") return \"json\";\n if (opts.configDefault === \"short\") return \"quiet\";\n return \"pretty\";\n}\n\nexport function success(msg: string): void {\n console.log(pc.green(\"✓\") + \" \" + msg);\n}\n\nexport function error(msg: string): void {\n console.error(pc.red(\"✗\") + \" \" + msg);\n}\n\nexport function info(msg: string): void {\n console.log(pc.dim(msg));\n}\n\nexport function json(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\nexport function table(\n headers: string[],\n rows: string[][],\n columnWidths?: number[],\n): void {\n const widths =\n columnWidths ??\n headers.map((h, i) =>\n Math.max(h.length, ...rows.map((r) => (r[i] ?? \"\").length)),\n );\n\n const header = headers\n .map((h, i) => h.padEnd(widths[i] ?? h.length))\n .join(\" \");\n console.log(pc.bold(header));\n console.log(pc.dim(\"─\".repeat(header.length)));\n\n for (const row of rows) {\n console.log(\n row.map((c, i) => c.padEnd(widths[i] ?? c.length)).join(\" \"),\n );\n }\n}\n\nexport function formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n\nexport function formatDate(iso: string): string {\n return new Date(iso).toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n}\n\nexport function percentBar(pct: number, width = 10): string {\n const filled = Math.round((pct / 100) * width);\n return pc.cyan(\"█\".repeat(filled)) + pc.dim(\"░\".repeat(width - filled));\n}\n","import { ShortenApiError, NetworkError } from \"../api/client.js\";\nimport * as out from \"./output.js\";\n\nexport function handleCommandError(err: unknown): never {\n if (err instanceof ShortenApiError) {\n switch (err.status) {\n case 401:\n out.error(\"Invalid API key. Check your key or generate a new one.\");\n out.info(' Run \"shorten login\" or visit https://shorten.dev/api-keys');\n break;\n case 403:\n out.error(\"Forbidden — your API key doesn't have the required scope.\");\n break;\n case 404:\n out.error(err.message || \"Not found.\");\n break;\n case 429:\n out.error(\"Rate limit exceeded. Try again later.\");\n break;\n default:\n out.error(err.message);\n }\n process.exit(1);\n }\n\n if (err instanceof NetworkError) {\n out.error(`Could not connect to the API (${err.url}).`);\n out.info(\" Check your internet connection or use --api-url to set the correct endpoint.\");\n process.exit(1);\n }\n\n const message = err instanceof Error ? err.message : String(err);\n out.error(`Unexpected error: ${message}`);\n process.exit(1);\n}\n","export async function copyToClipboard(text: string): Promise<boolean> {\n try {\n const { default: clipboardy } = await import(\"clipboardy\");\n await clipboardy.write(text);\n return true;\n } catch {\n return false;\n }\n}\n","import type { Command } from \"commander\";\nimport {\n MAX_TAGS_PER_LINK,\n MAX_TAG_LENGTH,\n validateDestinationUrl,\n} from \"@shorten/shared\";\nimport type { CreateLinkRequest, CreateLinkResponse } from \"@shorten/shared\";\nimport { resolveClient } from \"../utils/client-factory.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport { copyToClipboard } from \"../utils/clipboard.js\";\nimport * as out from \"../utils/output.js\";\n\ninterface ShortenOptions {\n slug?: string;\n tag?: string[];\n quiet?: boolean;\n json?: boolean;\n copy?: boolean;\n}\n\nfunction normalizeUrl(raw: string): string {\n if (!/^https?:\\/\\//i.test(raw)) {\n return `https://${raw}`;\n }\n return raw;\n}\n\nexport function registerShortenCommand(program: Command): void {\n const cmd = program\n .command(\"create\", { isDefault: true, hidden: true })\n .argument(\"[url]\", \"URL to shorten\")\n .option(\"-s, --slug <slug>\", \"Custom slug\")\n .option(\"-t, --tag <tag>\", \"Add tags (repeatable)\", (val: string, acc: string[]) => [...acc, val], [] as string[])\n .option(\"-q, --quiet\", \"Output only the short URL\")\n .option(\"-j, --json\", \"Output as JSON\")\n .option(\"--no-copy\", \"Don't copy to clipboard\")\n .action(async (rawUrl: string | undefined, opts: ShortenOptions) => {\n if (!rawUrl) {\n program.help();\n return;\n }\n\n const { client, config } = resolveClient(cmd);\n const format = out.resolveFormat({\n json: opts.json,\n quiet: opts.quiet,\n configDefault: config.default_format,\n });\n\n // Normalize and validate URL client-side\n const url = normalizeUrl(rawUrl);\n const validationError = validateDestinationUrl(url);\n if (validationError) {\n out.error(validationError);\n process.exit(1);\n return;\n }\n\n const tags = opts.tag;\n if (tags && tags.length > MAX_TAGS_PER_LINK) {\n out.error(`Too many tags: got ${tags.length}, max is ${MAX_TAGS_PER_LINK}`);\n process.exit(1);\n return;\n }\n const longTag = tags?.find((t) => t.length > MAX_TAG_LENGTH);\n if (longTag) {\n out.error(`Tag \"${longTag}\" exceeds ${MAX_TAG_LENGTH} characters`);\n process.exit(1);\n return;\n }\n\n const body: CreateLinkRequest = {\n destination_url: url,\n custom_slug: opts.slug,\n tags,\n };\n\n try {\n const res = await client.post<CreateLinkResponse>(\"/links\", body);\n\n if (format === \"json\") {\n out.json(res);\n return;\n }\n\n if (format === \"quiet\") {\n console.log(res.short_url);\n return;\n }\n\n const shouldCopy =\n opts.copy !== false && (config.copy_to_clipboard ?? true);\n\n let suffix = \"\";\n if (shouldCopy) {\n const copied = await copyToClipboard(res.short_url);\n if (copied) suffix = \" (copied to clipboard)\";\n }\n\n out.success(`${res.short_url}${suffix}`);\n\n if (res.link.tags.length > 0) {\n out.info(` tags: ${res.link.tags.join(\", \")}`);\n }\n } catch (err) {\n handleCommandError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport type { Link, PaginatedResponse } from \"@shorten/shared\";\nimport { resolveClient } from \"../utils/client-factory.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport * as out from \"../utils/output.js\";\n\ninterface ListOptions {\n limit?: string;\n status?: string;\n search?: string;\n sort?: string;\n order?: string;\n json?: boolean;\n}\n\nexport function registerListCommand(program: Command): void {\n const cmd = program\n .command(\"list\")\n .description(\"List your links\")\n .option(\"-n, --limit <n>\", \"Number of results (default: 10, max: 100)\")\n .option(\"--status <status>\", \"Filter by active or flagged\")\n .option(\"--search <query>\", \"Search in slug, URL, and tags\")\n .option(\n \"--sort <field>\",\n \"Sort by created_at or slug\",\n )\n .option(\"--order <dir>\", \"asc or desc (default: desc)\")\n .option(\"-j, --json\", \"Output as JSON\")\n .action(async (opts: ListOptions) => {\n const { client } = resolveClient(cmd);\n\n const params: Record<string, string> = {};\n if (opts.limit) params[\"limit\"] = opts.limit;\n if (opts.status) params[\"status\"] = opts.status;\n if (opts.search) params[\"search\"] = opts.search;\n if (opts.sort) params[\"sort\"] = opts.sort;\n if (opts.order) params[\"order\"] = opts.order;\n\n try {\n const res = await client.get<PaginatedResponse<Link>>(\n \"/links\",\n params,\n );\n\n if (opts.json) {\n out.json(res);\n return;\n }\n\n if (res.data.length === 0) {\n out.info(\"No links found.\");\n return;\n }\n\n out.table(\n [\"Slug\", \"Destination\", \"Status\", \"Created\"],\n res.data.map((link) => [\n link.slug,\n truncate(link.destination_url, 40),\n link.status,\n out.formatDate(link.created_at),\n ]),\n );\n\n out.info(\n `\\nShowing ${res.data.length} of ${res.total} links (page ${res.page}/${res.total_pages})`,\n );\n } catch (err) {\n handleCommandError(err);\n }\n });\n}\n\nfunction truncate(str: string, maxLen: number): string {\n if (str.length <= maxLen) return str;\n return str.slice(0, maxLen - 1) + \"…\";\n}\n","import type { Command } from \"commander\";\nimport type { AnalyticsResponse, DeviceEntry } from \"@shorten/shared\";\nimport { resolveClient } from \"../utils/client-factory.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport * as out from \"../utils/output.js\";\nimport pc from \"picocolors\";\n\ninterface StatsOptions {\n period?: string;\n json?: boolean;\n}\n\nexport function registerStatsCommand(program: Command): void {\n const cmd = program\n .command(\"stats <slug>\")\n .description(\"View click analytics for a link\")\n .option(\n \"-p, --period <period>\",\n \"Time window: 7d, 30d, or 90d (default: 7d)\",\n )\n .option(\"-j, --json\", \"Output as JSON\")\n .action(async (slug: string, opts: StatsOptions) => {\n const { client } = resolveClient(cmd);\n\n const params: Record<string, string> = {};\n if (opts.period) params[\"period\"] = opts.period;\n\n try {\n const res = await client.get<AnalyticsResponse>(\n `/links/${slug}/analytics`,\n params,\n );\n\n if (opts.json) {\n out.json(res);\n return;\n }\n\n const period = res.period;\n console.log(\n `\\n${pc.bold(`r.shorten.dev/${res.slug}`)} — ${pc.cyan(out.formatNumber(res.total_clicks))} clicks (${period})`,\n );\n console.log(\n pc.dim(\n ` ${out.formatNumber(res.unique_visitors)} unique visitors`,\n ),\n );\n\n console.log();\n\n const colWidth = 24;\n const maxRows = 5;\n\n // Build columns\n const countries = res.top_countries.slice(0, maxRows);\n const referrers = res.top_referrers.slice(0, maxRows);\n\n const totalForPct = res.total_clicks || 1;\n\n // Header\n console.log(\n pc.bold(\" Top countries\".padEnd(colWidth)) +\n pc.bold(\"Top referrers\".padEnd(colWidth)) +\n pc.bold(\"Devices\"),\n );\n\n // Rows\n const deviceEntries = res.top_devices.slice(0, maxRows).map((d: DeviceEntry) => [\n d.device.charAt(0).toUpperCase() + d.device.slice(1),\n d.count,\n ] as const);\n\n for (let i = 0; i < maxRows; i++) {\n let line = \" \";\n\n // Country column\n const country = countries[i];\n if (country) {\n const pct = Math.round(\n (country.count / totalForPct) * 100,\n );\n line += `${country.country.padEnd(6)}${String(pct).padStart(3)}%`.padEnd(\n colWidth,\n );\n } else {\n line += \"\".padEnd(colWidth);\n }\n\n // Referrer column\n const referrer = referrers[i];\n if (referrer) {\n const pct = Math.round(\n (referrer.count / totalForPct) * 100,\n );\n line += `${referrer.referrer.padEnd(16)}${String(pct).padStart(3)}%`.padEnd(\n colWidth,\n );\n } else {\n line += \"\".padEnd(colWidth);\n }\n\n // Device column\n const device = deviceEntries[i];\n if (device) {\n const pct = Math.round(\n (device[1] / totalForPct) * 100,\n );\n line += `${device[0].padEnd(10)}${String(pct).padStart(3)}%`;\n }\n\n console.log(line);\n }\n\n console.log();\n } catch (err) {\n handleCommandError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport type { UsageResponse } from \"@shorten/shared\";\nimport { resolveClient } from \"../utils/client-factory.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport * as out from \"../utils/output.js\";\nimport pc from \"picocolors\";\n\ninterface WhoamiOptions {\n json?: boolean;\n}\n\nexport function registerWhoamiCommand(program: Command): void {\n const cmd = program\n .command(\"whoami\")\n .description(\"Display authenticated user and API key info\")\n .option(\"-j, --json\", \"Output as JSON\")\n .action(async (opts: WhoamiOptions) => {\n const { client, apiKey } = resolveClient(cmd);\n\n try {\n const usage = await client.get<UsageResponse>(\"/usage\");\n\n if (opts.json) {\n out.json({\n key_prefix: maskKey(apiKey),\n rate_limit: usage,\n });\n return;\n }\n\n console.log(\n ` Key: ${pc.cyan(maskKey(apiKey))}`,\n );\n console.log(\n ` Rate limit: ${pc.bold(String(usage.remaining))}/${usage.limit} remaining`,\n );\n console.log(\n ` Resets: ${out.formatDate(usage.reset_at)}`,\n );\n } catch (err) {\n handleCommandError(err);\n }\n });\n}\n\nfunction maskKey(key: string): string {\n if (key.length <= 8) return key;\n return key.slice(0, 3) + \"…\" + key.slice(-4);\n}\n","import type { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { ApiClient } from \"../api/client.js\";\nimport { loadConfig, saveConfig, resolveApiUrl } from \"../config.js\";\nimport { handleCommandError } from \"../utils/errors.js\";\nimport * as out from \"../utils/output.js\";\n\nexport function registerLoginCommand(program: Command): void {\n const cmd = program\n .command(\"login\")\n .description(\"Authenticate with your API key\")\n .action(async () => {\n const globals = cmd.optsWithGlobals() as Record<string, string | undefined>;\n const apiUrl = resolveApiUrl(globals[\"apiUrl\"]);\n\n // Derive the web app URL from the API URL\n const appUrl = apiUrl.replace(/\\/api\\/v1\\/?$/, \"/app/api-keys\");\n\n if (!process.stdin.isTTY) {\n out.error(\n \"Non-interactive terminal detected. Use --key flag instead:\\n\" +\n \" shorten --key sk_your_key whoami\",\n );\n process.exit(1);\n return;\n }\n\n console.log(`\\nOpen your browser to get an API key:\\n ${appUrl}\\n`);\n\n // Try to open browser automatically (best-effort)\n try {\n const { exec } = await import(\"node:child_process\");\n const openCmd =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"start\"\n : \"xdg-open\";\n exec(`${openCmd} ${appUrl}`);\n } catch {\n // Ignore — user can open manually\n }\n\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const key = await new Promise<string>((resolve) => {\n rl.question(\"Paste your API key: \", (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n\n if (!key) {\n out.error(\"No key provided.\");\n process.exit(1);\n return;\n }\n\n if (!key.startsWith(\"sk_\")) {\n out.error('Invalid API key format. Keys start with \"sk_\".');\n process.exit(1);\n return;\n }\n\n // Validate the key by calling /usage, then save on success\n try {\n const client = new ApiClient(key, apiUrl);\n await client.get(\"/usage\");\n\n const config = loadConfig();\n config.api_key = key;\n saveConfig(config);\n\n out.success(\"Logged in successfully. API key saved to ~/.shorten.json\");\n } catch (err) {\n out.error(\"Could not verify the API key.\");\n handleCommandError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { unlinkSync } from \"node:fs\";\nimport {\n loadConfig,\n saveConfig,\n CONFIG_PATH,\n type ShortenConfig,\n} from \"../config.js\";\nimport * as out from \"../utils/output.js\";\n\nconst VALID_KEYS: (keyof ShortenConfig)[] = [\n \"api_key\",\n \"api_url\",\n \"default_format\",\n \"copy_to_clipboard\",\n];\n\nfunction maskValue(key: string, value: unknown): string {\n if (key === \"api_key\" && typeof value === \"string\" && value.length > 8) {\n return value.slice(0, 3) + \"…\" + value.slice(-4);\n }\n return String(value);\n}\n\nexport function registerConfigCommand(program: Command): void {\n const configCmd = program\n .command(\"config\")\n .description(\"Manage CLI configuration\");\n\n configCmd\n .command(\"list\")\n .description(\"Show all configuration values\")\n .action(() => {\n const config = loadConfig();\n const entries = Object.entries(config);\n\n if (entries.length === 0) {\n out.info(`No configuration found. Config file: ${CONFIG_PATH}`);\n return;\n }\n\n for (const [key, value] of entries) {\n console.log(`${key} = ${maskValue(key, value)}`);\n }\n });\n\n configCmd\n .command(\"get <key>\")\n .description(\"Get a configuration value\")\n .action((key: string) => {\n if (!VALID_KEYS.includes(key as keyof ShortenConfig)) {\n out.error(`Unknown config key: \"${key}\". Valid keys: ${VALID_KEYS.join(\", \")}`);\n process.exit(1);\n return;\n }\n\n const config = loadConfig();\n const value = config[key as keyof ShortenConfig];\n\n if (value === undefined) {\n out.info(`\"${key}\" is not set.`);\n return;\n }\n\n console.log(maskValue(key, value));\n });\n\n configCmd\n .command(\"set <key> <value>\")\n .description(\"Set a configuration value\")\n .action((key: string, value: string) => {\n if (!VALID_KEYS.includes(key as keyof ShortenConfig)) {\n out.error(`Unknown config key: \"${key}\". Valid keys: ${VALID_KEYS.join(\", \")}`);\n process.exit(1);\n return;\n }\n\n const config = loadConfig();\n\n if (key === \"copy_to_clipboard\") {\n (config as Record<string, unknown>)[key] = value === \"true\";\n } else {\n (config as Record<string, unknown>)[key] = value;\n }\n\n saveConfig(config);\n out.success(`Set ${key} = ${maskValue(key, value)}`);\n });\n\n configCmd\n .command(\"reset\")\n .description(\"Delete the configuration file\")\n .action(() => {\n try {\n unlinkSync(CONFIG_PATH);\n out.success(`Deleted ${CONFIG_PATH}`);\n } catch {\n out.info(\"No configuration file to delete.\");\n }\n });\n\n configCmd\n .command(\"path\")\n .description(\"Print the configuration file path\")\n .action(() => {\n console.log(CONFIG_PATH);\n });\n}\n","import { createProgram } from \"./cli.js\";\n\nconst program = createProgram();\nprogram.parseAsync(process.argv);\n"],"mappings":"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","names":["program","program","pc","program","pc","program","program","program","program"]}
|