clipr-cli 0.0.5

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/build.ts","../src/utils/output.ts","../src/commands/config.ts","../src/commands/delete.ts","../src/commands/deploy.ts","../src/commands/edit.ts","../src/commands/export.ts","../src/commands/import.ts","../src/commands/info.ts","../src/commands/init.ts","../src/commands/list.ts","../src/commands/qr.ts","../src/utils/qr.ts","../src/commands/shorten.ts","../src/commands/stats.ts","../src/factory.ts","../src/utils/context.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport type { UrlBackend } from '@clipr/core';\nimport { DEFAULT_DB_PATH, loadConfig } from '@clipr/core';\nimport { Command } from 'commander';\nimport { build } from './commands/build.js';\nimport { config } from './commands/config.js';\nimport { del } from './commands/delete.js';\nimport { deploy } from './commands/deploy.js';\nimport { edit } from './commands/edit.js';\nimport { exportUrls } from './commands/export.js';\nimport { importUrls } from './commands/import.js';\nimport { info } from './commands/info.js';\nimport { init } from './commands/init.js';\nimport { list } from './commands/list.js';\nimport { qr } from './commands/qr.js';\nimport { shorten } from './commands/shorten.js';\nimport { stats } from './commands/stats.js';\nimport { createBackend as createFactoryBackend } from './factory.js';\nimport { createBackend as createJsonBackend } from './utils/context.js';\nimport { error } from './utils/output.js';\n\nconst program = new Command();\n\n/**\n * Resolve the backend for the current command.\n * If --db is passed, use the legacy JsonBackend directly.\n * Otherwise, load config and use the factory pattern.\n */\nasync function resolveBackend(globalOpts: { db?: string }): Promise<UrlBackend> {\n if (globalOpts.db) {\n // Legacy mode: direct JsonBackend with explicit path\n // Wrap in the adapter to satisfy UrlBackend interface\n const { JsonBackendAdapter } = await import('./backends/json-adapter.js');\n const dbPath = resolve(globalOpts.db);\n return new JsonBackendAdapter(dbPath, '');\n }\n\n const config = loadConfig();\n return createFactoryBackend(config);\n}\n\nprogram\n .name('clipr')\n .description('Shorten, manage, and track URLs')\n .version('0.0.1')\n .option('--db <path>', 'path to urls.json database file');\n\nprogram\n .command('shorten')\n .description('Shorten a URL')\n .argument('<url>', 'URL to shorten')\n .option('-s, --slug <slug>', 'custom slug (random if omitted)')\n .option('-d, --description <text>', 'description for the link')\n .option('--utm-source <value>', 'UTM source')\n .option('--utm-medium <value>', 'UTM medium')\n .option('--utm-campaign <value>', 'UTM campaign')\n .option('--utm-term <value>', 'UTM term')\n .option('--utm-content <value>', 'UTM content')\n .option('--expires <date>', 'expiration date (ISO 8601)')\n .action(async (url: string, opts) => {\n const backend = createJsonBackend(program.opts().db);\n await shorten(url, backend, {\n slug: opts.slug,\n description: opts.description,\n utm_source: opts.utmSource,\n utm_medium: opts.utmMedium,\n utm_campaign: opts.utmCampaign,\n utm_term: opts.utmTerm,\n utm_content: opts.utmContent,\n expires: opts.expires,\n });\n });\n\nprogram\n .command('list')\n .alias('ls')\n .description('List all shortened URLs')\n .action(async () => {\n const backend = createJsonBackend(program.opts().db);\n await list(backend);\n });\n\nprogram\n .command('delete')\n .alias('rm')\n .description('Delete a shortened URL')\n .argument('<slug>', 'slug to delete')\n .action(async (slug: string) => {\n const backend = createJsonBackend(program.opts().db);\n await del(slug, backend);\n });\n\nprogram\n .command('info')\n .description('Show details for a slug')\n .argument('<slug>', 'slug to inspect')\n .action(async (slug: string) => {\n const backend = createJsonBackend(program.opts().db);\n await info(slug, backend);\n });\n\nprogram\n .command('config')\n .description('Get or set configuration')\n .argument('<key>', 'config key (e.g. baseUrl)')\n .argument('[value]', 'value to set (omit to read)')\n .action(async (key: string, value: string | undefined) => {\n const backend = createJsonBackend(program.opts().db);\n await config(key, value, backend);\n });\n\nprogram\n .command('init')\n .description('Initialize a new urls.json database')\n .option('-b, --base-url <url>', 'base URL for short links')\n .option('-f, --force', 'overwrite existing database')\n .action(async (opts) => {\n const dbPath = resolve(program.opts().db ?? DEFAULT_DB_PATH);\n await init(dbPath, { baseUrl: opts.baseUrl, force: opts.force });\n });\n\nprogram\n .command('deploy')\n .description('Deploy URLs to Cloudflare KV')\n .requiredOption('--namespace-id <id>', 'Cloudflare KV namespace ID')\n .option('--preview', 'deploy to preview KV namespace')\n .action(async (opts) => {\n const backend = createJsonBackend(program.opts().db);\n await deploy(backend, {\n namespaceId: opts.namespaceId,\n preview: opts.preview,\n });\n });\n\nprogram\n .command('edit')\n .description('Edit an existing shortened URL')\n .argument('<slug>', 'slug to edit')\n .option('--url <url>', 'new target URL')\n .option('--slug <slug>', 'new slug')\n .option('--tags <tags>', 'comma-separated tags')\n .option('--expires <date>', 'expiration date (ISO 8601)')\n .option('--description <text>', 'description')\n .action(async (slug: string, opts) => {\n const backend = await resolveBackend(program.opts());\n await edit(slug, backend, opts);\n });\n\nprogram\n .command('stats')\n .description('Show click analytics for a slug')\n .argument('<slug>', 'slug to inspect')\n .option('--json', 'output raw JSON')\n .option('--period <period>', 'time period (e.g. 7d, 30d)')\n .action(async (slug: string, opts) => {\n const backend = await resolveBackend(program.opts());\n await stats(slug, backend, opts);\n });\n\nprogram\n .command('qr')\n .description('Generate a QR code for a slug')\n .argument('<slug>', 'slug to generate QR for')\n .option('-o, --output <file>', 'output file path')\n .option('-f, --format <format>', 'output format (svg|png)', 'svg')\n .option('-s, --size <px>', 'image size in pixels', '256')\n .action(async (slug: string, opts) => {\n const backend = await resolveBackend(program.opts());\n await qr(slug, backend, opts);\n });\n\nprogram\n .command('import')\n .description('Import URLs from a file')\n .argument('<file>', 'input file path (JSON or CSV)')\n .option('-f, --format <format>', 'file format (json|csv)')\n .action(async (file: string, opts) => {\n const backend = await resolveBackend(program.opts());\n await importUrls(file, backend, opts);\n });\n\nprogram\n .command('export')\n .description('Export all URLs to a file')\n .option('-f, --format <format>', 'output format (json|csv)', 'json')\n .option('-o, --output <file>', 'output file path (stdout if omitted)')\n .action(async (opts) => {\n const backend = await resolveBackend(program.opts());\n await exportUrls(backend, opts);\n });\n\nprogram\n .command('build')\n .description('Build static HTML redirect pages')\n .option('-i, --input <path>', 'path to urls.json', 'urls.json')\n .option('-o, --output <dir>', 'output directory', 'dist')\n .action(async (opts) => {\n await build({ input: opts.input, output: opts.output });\n });\n\nprogram.parseAsync().catch((err: Error) => {\n error(err.message);\n process.exit(1);\n});\n","import { existsSync, mkdirSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { UrlDatabase, UrlEntry } from '@clipr/core';\nimport { appendUtm, hasUtm } from '@clipr/core';\nimport { dim, error, info, success } from '../utils/output.js';\n\ninterface BuildOptions {\n input?: string;\n output?: string;\n}\n\n/**\n * Generate a bare-minimum HTML redirect page (~500 bytes).\n * Uses both meta refresh and JavaScript redirect for maximum compatibility.\n */\nfunction buildRedirectHtml(entry: UrlEntry): string {\n const target = hasUtm(entry.utm) ? appendUtm(entry.url, entry.utm!) : entry.url;\n const escaped = target.replace(/\"/g, '&quot;').replace(/</g, '&lt;');\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta http-equiv=\"refresh\" content=\"0;url=${escaped}\">\n<title>Redirecting...</title>\n<link rel=\"canonical\" href=\"${escaped}\">\n</head>\n<body>\n<script>window.location.replace(\"${target.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\")</script>\n<noscript><a href=\"${escaped}\">Click here</a></noscript>\n</body>\n</html>\n`;\n}\n\nexport async function build(opts: BuildOptions): Promise<void> {\n const inputPath = opts.input ?? 'urls.json';\n const outputDir = opts.output ?? 'dist';\n\n if (!existsSync(inputPath)) {\n error(`Input file not found: ${inputPath}`);\n info('Run `clipr init` to create a urls.json database, or specify --input <path>.');\n process.exit(1);\n }\n\n let db: UrlDatabase;\n try {\n const raw = await readFile(inputPath, 'utf-8');\n db = JSON.parse(raw) as UrlDatabase;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n error(`Failed to parse ${inputPath}: ${msg}`);\n process.exit(1);\n }\n\n const entries = Object.values(db.urls);\n if (entries.length === 0) {\n info('No URLs to build. Add some with `clipr shorten <url>`.');\n return;\n }\n\n // Create output directory\n mkdirSync(outputDir, { recursive: true });\n\n info(`Building ${entries.length} redirect page${entries.length === 1 ? '' : 's'}...`);\n\n let built = 0;\n for (const entry of entries) {\n // Skip expired entries\n if (entry.expiresAt && new Date(entry.expiresAt) < new Date()) {\n console.log(` ${dim(`skip \"${entry.slug}\" (expired)`)}`);\n continue;\n }\n\n const slugDir = join(outputDir, entry.slug);\n mkdirSync(slugDir, { recursive: true });\n\n const html = buildRedirectHtml(entry);\n await writeFile(join(slugDir, 'index.html'), html);\n built++;\n }\n\n // Generate a simple index page\n const indexHtml = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>clipr</title>\n</head>\n<body>\n<p>${built} redirect${built === 1 ? '' : 's'} active.</p>\n</body>\n</html>\n`;\n await writeFile(join(outputDir, 'index.html'), indexHtml);\n\n success(`Built ${built} redirect page${built === 1 ? '' : 's'} to ${outputDir}/`);\n}\n","import pc from 'picocolors';\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.cyan('ℹ'), msg);\n}\n\nexport function dim(msg: string): string {\n return pc.dim(msg);\n}\n","import type { JsonBackend } from '@clipr/core';\nimport { dim, error, success } from '../utils/output.js';\n\nexport async function config(\n key: string,\n value: string | undefined,\n backend: JsonBackend,\n): Promise<void> {\n if (key === 'baseUrl') {\n if (value === undefined) {\n const current = await backend.getBaseUrl();\n console.log(current || dim('(not set)'));\n } else {\n await backend.setBaseUrl(value);\n success(`baseUrl set to \"${value}\"`);\n }\n return;\n }\n\n error(`Unknown config key \"${key}\". Available keys: baseUrl`);\n process.exit(1);\n}\n","import type { JsonBackend } from '@clipr/core';\nimport { error, success } from '../utils/output.js';\n\nexport async function del(slug: string, backend: JsonBackend): Promise<void> {\n const deleted = await backend.delete(slug);\n if (deleted) {\n success(`Deleted slug \"${slug}\"`);\n } else {\n error(`Slug \"${slug}\" not found`);\n process.exit(1);\n }\n}\n","import { execFile } from 'node:child_process';\nimport { writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { JsonBackend } from '@clipr/core';\nimport { dim, error, info, success } from '../utils/output.js';\n\nconst exec = promisify(execFile);\n\ninterface DeployOptions {\n namespaceId: string;\n preview?: boolean;\n}\n\nexport async function deploy(backend: JsonBackend, opts: DeployOptions): Promise<void> {\n // Validate namespace ID format to prevent injection\n if (!/^[a-f0-9]{32}$/.test(opts.namespaceId)) {\n error('Invalid namespace ID format. Expected 32-character hex string.');\n process.exit(1);\n }\n\n const entries = await backend.list();\n\n if (entries.length === 0) {\n info('No URLs to deploy. Run `clipr shorten <url>` first.');\n return;\n }\n\n // Build KV bulk data: each entry is a { key, value } pair\n const kvPairs = entries.map((entry) => ({\n key: entry.slug,\n value: JSON.stringify(entry),\n }));\n\n // Add the _index key for list() support in the worker\n kvPairs.push({\n key: '_index',\n value: JSON.stringify(entries.map((e) => e.slug)),\n });\n\n // Write to a temp file for wrangler kv bulk put\n const tmpFile = join(tmpdir(), `clipr-deploy-${Date.now()}.json`);\n await writeFile(tmpFile, JSON.stringify(kvPairs, null, 2));\n\n info(`Deploying ${entries.length} URL${entries.length === 1 ? '' : 's'} to KV...`);\n\n try {\n const args = ['kv', 'bulk', 'put', tmpFile, '--namespace-id', opts.namespaceId];\n if (opts.preview) {\n args.push('--preview');\n }\n\n const { stdout, stderr } = await exec('npx', ['wrangler', ...args]);\n if (stdout) console.log(dim(stdout.trim()));\n if (stderr && !stderr.includes('deprecated')) console.error(dim(stderr.trim()));\n\n success(`Deployed ${entries.length} URL${entries.length === 1 ? '' : 's'} to Cloudflare KV`);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n error(`Deploy failed: ${msg}`);\n info('Make sure wrangler is installed and you are logged in (`npx wrangler login`)');\n process.exit(1);\n }\n}\n","import type { ShortUrl, UrlBackend } from '@clipr/core';\nimport { normalizeSlug, validateSlug } from '@clipr/core';\nimport { error, success } from '../utils/output.js';\n\ninterface EditOptions {\n url?: string;\n slug?: string;\n tags?: string;\n expires?: string;\n description?: string;\n}\n\nexport async function edit(slug: string, backend: UrlBackend, opts: EditOptions): Promise<void> {\n const updates: Partial<ShortUrl> = {};\n\n if (opts.url) {\n updates.url = opts.url;\n }\n\n if (opts.slug) {\n const newSlug = normalizeSlug(opts.slug);\n const result = validateSlug(newSlug);\n if (!result.valid) {\n error(`Invalid slug: ${result.reason}`);\n process.exit(1);\n }\n updates.slug = newSlug;\n }\n\n if (opts.tags) {\n updates.tags = opts.tags.split(',').map((t) => t.trim());\n }\n\n if (opts.expires) {\n updates.expiresAt = new Date(opts.expires).toISOString();\n }\n\n if (opts.description) {\n updates.description = opts.description;\n }\n\n if (Object.keys(updates).length === 0) {\n error('No updates specified. Use --url, --slug, --tags, --expires, or --description.');\n process.exit(1);\n }\n\n try {\n const updated = await backend.update(slug, updates);\n success(`Updated \"${slug}\"${updates.slug ? ` → \"${updated.slug}\"` : ''}`);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n error(msg);\n process.exit(1);\n }\n}\n","import { writeFile } from 'node:fs/promises';\nimport type { ShortUrl, UrlBackend } from '@clipr/core';\nimport { dim, info, success } from '../utils/output.js';\n\ninterface ExportOptions {\n format?: 'json' | 'csv';\n output?: string;\n}\n\n/** Convert entries to CSV format. */\nfunction toCsv(entries: ShortUrl[]): string {\n const header = 'slug,url,description,createdAt,expiresAt,tags';\n const rows = entries.map((e) => {\n const desc = (e.description ?? '').replace(/,/g, ';');\n const tags = (e.tags ?? []).join(';');\n return `${e.slug},${e.url},${desc},${e.createdAt},${e.expiresAt ?? ''},${tags}`;\n });\n return `${[header, ...rows].join('\\n')}\\n`;\n}\n\nexport async function exportUrls(backend: UrlBackend, opts: ExportOptions): Promise<void> {\n const entries = await backend.list();\n\n if (entries.length === 0) {\n info('No URLs to export.');\n return;\n }\n\n const format = opts.format ?? 'json';\n let output: string;\n\n if (format === 'csv') {\n output = toCsv(entries);\n } else {\n output = `${JSON.stringify(entries, null, 2)}\\n`;\n }\n\n if (opts.output) {\n await writeFile(opts.output, output);\n success(`Exported ${entries.length} URL${entries.length === 1 ? '' : 's'} to ${opts.output}`);\n } else {\n process.stdout.write(output);\n if (process.stderr.isTTY) {\n info(`${dim(`${entries.length} URL${entries.length === 1 ? '' : 's'} exported`)}`);\n }\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { UrlBackend } from '@clipr/core';\nimport { dim, error, info, success } from '../utils/output.js';\n\ninterface ImportOptions {\n format?: 'json' | 'csv';\n}\n\ninterface ImportEntry {\n slug: string;\n url: string;\n description?: string;\n expiresAt?: string;\n tags?: string[];\n}\n\n/** Parse a CSV string into import entries. */\nfunction parseCsv(content: string): ImportEntry[] {\n const lines = content.trim().split('\\n');\n if (lines.length < 2) return [];\n\n const header = lines[0]?.split(',').map((h) => h.trim().toLowerCase());\n const slugIdx = header.indexOf('slug');\n const urlIdx = header.indexOf('url');\n const descIdx = header.indexOf('description');\n const expiresIdx = header.indexOf('expiresat');\n const tagsIdx = header.indexOf('tags');\n\n if (slugIdx === -1 || urlIdx === -1) {\n throw new Error('CSV must have \"slug\" and \"url\" columns');\n }\n\n return lines.slice(1).map((line) => {\n const cols = line.split(',').map((c) => c.trim());\n const entry: ImportEntry = {\n slug: cols[slugIdx]!,\n url: cols[urlIdx]!,\n };\n if (descIdx !== -1 && cols[descIdx]) entry.description = cols[descIdx];\n if (expiresIdx !== -1 && cols[expiresIdx]) entry.expiresAt = cols[expiresIdx];\n if (tagsIdx !== -1 && cols[tagsIdx])\n entry.tags = cols[tagsIdx]?.split(';').map((t) => t.trim());\n return entry;\n });\n}\n\nexport async function importUrls(\n file: string,\n backend: UrlBackend,\n opts: ImportOptions,\n): Promise<void> {\n let content: string;\n try {\n const resolvedPath = resolve(file);\n content = await readFile(resolvedPath, 'utf-8');\n } catch {\n error(`Cannot read file: ${file}`);\n process.exit(1);\n }\n\n const format = opts.format ?? (file.endsWith('.csv') ? 'csv' : 'json');\n let entries: ImportEntry[];\n\n try {\n if (format === 'csv') {\n entries = parseCsv(content);\n } else {\n const parsed = JSON.parse(content);\n // Support both array and { urls: [...] } shapes\n entries = Array.isArray(parsed) ? parsed : (parsed.urls ?? Object.values(parsed));\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n error(`Failed to parse ${format.toUpperCase()} file: ${msg}`);\n process.exit(1);\n }\n\n if (entries.length === 0) {\n info('No entries found in import file.');\n return;\n }\n\n info(`Importing ${entries.length} URL${entries.length === 1 ? '' : 's'}...`);\n\n let imported = 0;\n let skipped = 0;\n\n for (const entry of entries) {\n if (!entry.slug || !entry.url) {\n skipped++;\n continue;\n }\n\n try {\n await backend.create(entry.slug, entry.url, {\n description: entry.description,\n expiresAt: entry.expiresAt,\n tags: entry.tags,\n });\n imported++;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.log(` ${dim(`skip \"${entry.slug}\": ${msg}`)}`);\n skipped++;\n }\n }\n\n success(`Imported ${imported} URL${imported === 1 ? '' : 's'}`);\n if (skipped > 0) {\n info(`${skipped} entr${skipped === 1 ? 'y' : 'ies'} skipped (conflicts or invalid)`);\n }\n}\n","import type { JsonBackend } from '@clipr/core';\nimport { appendUtm, hasUtm } from '@clipr/core';\nimport pc from 'picocolors';\nimport { dim, error } from '../utils/output.js';\n\nexport async function info(slug: string, backend: JsonBackend): Promise<void> {\n const entry = await backend.get(slug);\n if (!entry) {\n error(`Slug \"${slug}\" not found`);\n process.exit(1);\n }\n\n const baseUrl = await backend.getBaseUrl();\n const shortUrl = baseUrl ? `${baseUrl}/${entry.slug}` : entry.slug;\n const redirectUrl = hasUtm(entry.utm) ? appendUtm(entry.url, entry.utm) : entry.url;\n\n console.log();\n console.log(` ${pc.bold('Slug:')} ${pc.cyan(entry.slug)}`);\n console.log(` ${pc.bold('Short URL:')} ${shortUrl}`);\n console.log(` ${pc.bold('Target:')} ${entry.url}`);\n if (redirectUrl !== entry.url) {\n console.log(` ${pc.bold('Redirect:')} ${dim(redirectUrl)}`);\n }\n if (entry.description) {\n console.log(` ${pc.bold('Note:')} ${entry.description}`);\n }\n console.log(` ${pc.bold('Created:')} ${entry.createdAt}`);\n if (entry.expiresAt) {\n console.log(` ${pc.bold('Expires:')} ${entry.expiresAt}`);\n }\n if (hasUtm(entry.utm)) {\n console.log(` ${pc.bold('UTM:')}`);\n for (const [key, value] of Object.entries(entry.utm)) {\n if (value) console.log(` ${dim(key)}: ${value}`);\n }\n }\n console.log();\n}\n","import { existsSync } from 'node:fs';\nimport { writeFile } from 'node:fs/promises';\nimport type { UrlDatabase } from '@clipr/core';\nimport { DB_VERSION } from '@clipr/core';\nimport { dim, error, info, success } from '../utils/output.js';\n\ninterface InitOptions {\n baseUrl?: string;\n force?: boolean;\n}\n\nexport async function init(dbPath: string, opts: InitOptions): Promise<void> {\n if (existsSync(dbPath) && !opts.force) {\n error(`${dbPath} already exists. Use --force to overwrite.`);\n process.exit(1);\n }\n\n const db: UrlDatabase = {\n version: DB_VERSION,\n counter: 0,\n baseUrl: opts.baseUrl ?? '',\n urls: {},\n };\n\n await writeFile(dbPath, `${JSON.stringify(db, null, 2)}\\n`);\n\n success(`Created ${dbPath}`);\n if (opts.baseUrl) {\n info(`Base URL set to ${dim(opts.baseUrl)}`);\n } else {\n info(`Set a base URL with: clipr config baseUrl https://your-domain.com`);\n }\n}\n","import type { JsonBackend } from '@clipr/core';\nimport pc from 'picocolors';\nimport { dim, info } from '../utils/output.js';\n\nexport async function list(backend: JsonBackend): Promise<void> {\n const entries = await backend.list();\n\n if (entries.length === 0) {\n info('No shortened URLs yet. Run `clipr shorten <url>` to create one.');\n return;\n }\n\n const baseUrl = await backend.getBaseUrl();\n\n console.log(dim(`\\n ${entries.length} shortened URL${entries.length === 1 ? '' : 's'}:\\n`));\n\n for (const entry of entries) {\n const short = baseUrl ? `${baseUrl}/${entry.slug}` : entry.slug;\n console.log(` ${pc.bold(pc.cyan(short))}`);\n console.log(` → ${entry.url}`);\n if (entry.description) {\n console.log(` ${dim(entry.description)}`);\n }\n if (entry.expiresAt) {\n console.log(` ${dim(`expires: ${entry.expiresAt}`)}`);\n }\n console.log();\n }\n}\n","import { writeFile } from 'node:fs/promises';\nimport type { UrlBackend } from '@clipr/core';\nimport { error, info, success } from '../utils/output.js';\nimport { generateQR, type QrFormat } from '../utils/qr.js';\n\ninterface QrOptions {\n output?: string;\n format?: QrFormat;\n size?: string;\n}\n\nexport async function qr(slug: string, backend: UrlBackend, opts: QrOptions): Promise<void> {\n // Resolve slug to get the full short URL\n const result = await backend.resolve(slug);\n if (!result) {\n error(`Slug \"${slug}\" not found`);\n process.exit(1);\n }\n\n // Determine the short URL (the URL to encode is the short link, not the target)\n // We use the target URL since we don't always have the baseUrl in the backend interface\n // However, the backend.list approach can give us what we need\n const entries = await backend.list({ search: slug, limit: 1 });\n const entry = entries.find((e) => e.slug === slug);\n\n // Build the short URL to encode\n // If we can't determine baseUrl, encode the target URL\n const urlToEncode = result.url;\n\n const format: QrFormat = opts.format ?? 'svg';\n const size = opts.size ? parseInt(opts.size, 10) : 256;\n\n if (Number.isNaN(size) || size < 64 || size > 4096) {\n error('Size must be between 64 and 4096 pixels');\n process.exit(1);\n }\n\n try {\n const qrData = await generateQR(urlToEncode, format, size);\n\n if (opts.output) {\n await writeFile(opts.output, qrData);\n success(`QR code saved to ${opts.output}`);\n } else {\n // Write to stdout\n if (typeof qrData === 'string') {\n console.log(qrData);\n } else {\n process.stdout.write(qrData);\n }\n }\n\n if (entry) {\n info(`QR code for slug \"${slug}\" → ${entry.url}`);\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n error(`Failed to generate QR code: ${msg}`);\n process.exit(1);\n }\n}\n","import QRCode from 'qrcode';\n\nexport type QrFormat = 'svg' | 'png';\n\n/**\n * Generate a QR code for the given URL.\n *\n * @param url - The URL to encode in the QR code.\n * @param format - Output format: 'svg' returns a string, 'png' returns a Buffer.\n * @param size - Width/height in pixels (for PNG). Defaults to 256.\n * @returns The QR code as an SVG string or PNG Buffer.\n */\nexport async function generateQR(\n url: string,\n format: QrFormat = 'svg',\n size: number = 256,\n): Promise<string | Buffer> {\n if (format === 'svg') {\n return QRCode.toString(url, {\n type: 'svg',\n width: size,\n margin: 2,\n });\n }\n\n return QRCode.toBuffer(url, {\n type: 'png',\n width: size,\n margin: 2,\n });\n}\n","import type { JsonBackend } from '@clipr/core';\nimport {\n generateRandomSlug,\n hasUtm,\n normalizeSlug,\n type UrlEntry,\n type UtmParams,\n validateSlug,\n validateUrl,\n} from '@clipr/core';\nimport { dim, error, success } from '../utils/output.js';\n\ninterface ShortenOptions {\n slug?: string;\n description?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_term?: string;\n utm_content?: string;\n expires?: string;\n}\n\nexport async function shorten(\n url: string,\n backend: JsonBackend,\n opts: ShortenOptions,\n): Promise<void> {\n const urlResult = validateUrl(url);\n if (!urlResult.valid) {\n error(`Invalid URL: ${urlResult.reason}`);\n process.exit(1);\n }\n\n let slug: string;\n if (opts.slug) {\n slug = normalizeSlug(opts.slug);\n const slugResult = validateSlug(slug);\n if (!slugResult.valid) {\n error(`Invalid slug: ${slugResult.reason}`);\n process.exit(1);\n }\n } else {\n slug = generateRandomSlug();\n while (await backend.has(slug)) {\n slug = generateRandomSlug();\n }\n }\n\n const utm: UtmParams = {};\n if (opts.utm_source) utm.utm_source = opts.utm_source;\n if (opts.utm_medium) utm.utm_medium = opts.utm_medium;\n if (opts.utm_campaign) utm.utm_campaign = opts.utm_campaign;\n if (opts.utm_term) utm.utm_term = opts.utm_term;\n if (opts.utm_content) utm.utm_content = opts.utm_content;\n\n const entry: UrlEntry = {\n slug,\n url,\n createdAt: new Date().toISOString(),\n ...(opts.description && { description: opts.description }),\n ...(opts.expires && { expiresAt: new Date(opts.expires).toISOString() }),\n ...(hasUtm(utm) && { utm }),\n };\n\n await backend.set(entry);\n\n const baseUrl = await backend.getBaseUrl();\n const shortUrl = baseUrl ? `${baseUrl}/${slug}` : slug;\n\n success(`Shortened ${dim(url)}`);\n console.log(` ${shortUrl}`);\n}\n","import type { UrlBackend } from '@clipr/core';\nimport pc from 'picocolors';\nimport { dim, error, info } from '../utils/output.js';\n\ninterface StatsOptions {\n json?: boolean;\n period?: string;\n}\n\nexport async function stats(slug: string, backend: UrlBackend, opts: StatsOptions): Promise<void> {\n try {\n const data = await backend.getStats(slug);\n\n if (data === null) {\n error('Stats are not available in the current backend mode.');\n info(\n 'Click analytics require the Workers API backend. Deploy with `clipr deploy` and set mode to \"api\".',\n );\n process.exit(1);\n }\n\n if (opts.json) {\n console.log(JSON.stringify(data, null, 2));\n return;\n }\n\n console.log();\n console.log(` ${pc.bold('Stats for')} ${pc.cyan(slug)}`);\n console.log(` ${pc.bold('Total clicks:')} ${data.total}`);\n\n if (Object.keys(data.daily).length > 0) {\n console.log();\n console.log(` ${pc.bold('Daily clicks:')}`);\n const sorted = Object.entries(data.daily).sort(([a], [b]) => a.localeCompare(b));\n for (const [date, count] of sorted) {\n const bar = '█'.repeat(Math.min(count, 40));\n console.log(` ${dim(date)} ${bar} ${count}`);\n }\n }\n\n if (Object.keys(data.geo).length > 0) {\n console.log();\n console.log(` ${pc.bold('Top countries:')}`);\n const sorted = Object.entries(data.geo)\n .sort(([, a], [, b]) => b - a)\n .slice(0, 10);\n for (const [country, count] of sorted) {\n console.log(` ${country}: ${count}`);\n }\n }\n\n if (Object.keys(data.referrer).length > 0) {\n console.log();\n console.log(` ${pc.bold('Top referrers:')}`);\n const sorted = Object.entries(data.referrer)\n .sort(([, a], [, b]) => b - a)\n .slice(0, 10);\n for (const [ref, count] of sorted) {\n console.log(` ${ref || dim('(direct)')}: ${count}`);\n }\n }\n\n if (Object.keys(data.device).length > 0) {\n console.log();\n console.log(` ${pc.bold('Devices:')}`);\n for (const [device, count] of Object.entries(data.device)) {\n console.log(` ${device}: ${count}`);\n }\n }\n\n console.log();\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n error(msg);\n process.exit(1);\n }\n}\n","import type { CliprConfig, UrlBackend } from '@clipr/core';\n\nimport { JsonBackendAdapter } from './backends/json-adapter.js';\n\n/**\n * Factory function (composition root).\n * Creates the appropriate UrlBackend based on the config mode.\n * Uses dynamic imports so backend modules are only loaded when needed.\n */\nexport async function createBackend(config: CliprConfig): Promise<UrlBackend> {\n switch (config.mode) {\n case 'github': {\n if (!config.github) {\n throw new Error(\n 'GitHub backend requires github config. Run `clipr config` to set owner, repo, branch, path, and token.',\n );\n }\n const { GitHubBackend } = await import('./backends/github.js');\n return new GitHubBackend(config.github, config.baseUrl);\n }\n\n case 'api': {\n if (!config.api) {\n throw new Error(\n 'API backend requires api config. Run `clipr config` to set baseUrl and token.',\n );\n }\n const { ApiBackend } = await import('./backends/api.js');\n return new ApiBackend(config.api, config.baseUrl);\n }\n\n default: {\n // Fall back to local JsonBackend wrapped as UrlBackend\n return new JsonBackendAdapter(config.dbPath, config.baseUrl);\n }\n }\n}\n","import { resolve } from 'node:path';\nimport { DEFAULT_DB_PATH, JsonBackend } from '@clipr/core';\n\n/** Resolve the database path and create a backend instance. */\nexport function createBackend(dbPath?: string): JsonBackend {\n const resolved = resolve(dbPath ?? DEFAULT_DB_PATH);\n return new JsonBackend(resolved);\n}\n"],"mappings":";;;;;;AAAA,SAAS,WAAAA,gBAAe;AAExB,SAAS,mBAAAC,kBAAiB,kBAAkB;AAC5C,SAAS,eAAe;;;ACHxB,SAAS,YAAY,iBAAiB;AACtC,SAAS,UAAU,iBAAiB;AACpC,SAAS,YAAY;AAErB,SAAS,WAAW,cAAc;;;ACJlC,OAAO,QAAQ;AAER,SAAS,QAAQ,KAAmB;AACzC,UAAQ,IAAI,GAAG,MAAM,QAAG,GAAG,GAAG;AAChC;AAEO,SAAS,MAAM,KAAmB;AACvC,UAAQ,MAAM,GAAG,IAAI,QAAG,GAAG,GAAG;AAChC;AAEO,SAAS,KAAK,KAAmB;AACtC,UAAQ,IAAI,GAAG,KAAK,QAAG,GAAG,GAAG;AAC/B;AAEO,SAAS,IAAI,KAAqB;AACvC,SAAO,GAAG,IAAI,GAAG;AACnB;;;ADAA,SAAS,kBAAkB,OAAyB;AAClD,QAAM,SAAS,OAAO,MAAM,GAAG,IAAI,UAAU,MAAM,KAAK,MAAM,GAAI,IAAI,MAAM;AAC5E,QAAM,UAAU,OAAO,QAAQ,MAAM,QAAQ,EAAE,QAAQ,MAAM,MAAM;AAEnE,SAAO;AAAA;AAAA;AAAA;AAAA,4CAImC,OAAO;AAAA;AAAA,8BAErB,OAAO;AAAA;AAAA;AAAA,mCAGF,OAAO,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK,CAAC;AAAA,qBAChE,OAAO;AAAA;AAAA;AAAA;AAI5B;AAEA,eAAsB,MAAM,MAAmC;AAC7D,QAAM,YAAY,KAAK,SAAS;AAChC,QAAM,YAAY,KAAK,UAAU;AAEjC,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,UAAM,yBAAyB,SAAS,EAAE;AAC1C,SAAK,6EAA6E;AAClF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,WAAW,OAAO;AAC7C,SAAK,KAAK,MAAM,GAAG;AAAA,EACrB,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,mBAAmB,SAAS,KAAK,GAAG,EAAE;AAC5C,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,OAAO,OAAO,GAAG,IAAI;AACrC,MAAI,QAAQ,WAAW,GAAG;AACxB,SAAK,wDAAwD;AAC7D;AAAA,EACF;AAGA,YAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAExC,OAAK,YAAY,QAAQ,MAAM,iBAAiB,QAAQ,WAAW,IAAI,KAAK,GAAG,KAAK;AAEpF,MAAI,QAAQ;AACZ,aAAW,SAAS,SAAS;AAE3B,QAAI,MAAM,aAAa,IAAI,KAAK,MAAM,SAAS,IAAI,oBAAI,KAAK,GAAG;AAC7D,cAAQ,IAAI,KAAK,IAAI,SAAS,MAAM,IAAI,aAAa,CAAC,EAAE;AACxD;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,WAAW,MAAM,IAAI;AAC1C,cAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAEtC,UAAM,OAAO,kBAAkB,KAAK;AACpC,UAAM,UAAU,KAAK,SAAS,YAAY,GAAG,IAAI;AACjD;AAAA,EACF;AAGA,QAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAOf,KAAK,YAAY,UAAU,IAAI,KAAK,GAAG;AAAA;AAAA;AAAA;AAI1C,QAAM,UAAU,KAAK,WAAW,YAAY,GAAG,SAAS;AAExD,UAAQ,SAAS,KAAK,iBAAiB,UAAU,IAAI,KAAK,GAAG,OAAO,SAAS,GAAG;AAClF;;;AE/FA,eAAsB,OACpB,KACA,OACA,SACe;AACf,MAAI,QAAQ,WAAW;AACrB,QAAI,UAAU,QAAW;AACvB,YAAM,UAAU,MAAM,QAAQ,WAAW;AACzC,cAAQ,IAAI,WAAW,IAAI,WAAW,CAAC;AAAA,IACzC,OAAO;AACL,YAAM,QAAQ,WAAW,KAAK;AAC9B,cAAQ,mBAAmB,KAAK,GAAG;AAAA,IACrC;AACA;AAAA,EACF;AAEA,QAAM,uBAAuB,GAAG,4BAA4B;AAC5D,UAAQ,KAAK,CAAC;AAChB;;;AClBA,eAAsB,IAAI,MAAc,SAAqC;AAC3E,QAAM,UAAU,MAAM,QAAQ,OAAO,IAAI;AACzC,MAAI,SAAS;AACX,YAAQ,iBAAiB,IAAI,GAAG;AAAA,EAClC,OAAO;AACL,UAAM,SAAS,IAAI,aAAa;AAChC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ACXA,SAAS,gBAAgB;AACzB,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,QAAAC,aAAY;AACrB,SAAS,iBAAiB;AAI1B,IAAM,OAAO,UAAU,QAAQ;AAO/B,eAAsB,OAAO,SAAsB,MAAoC;AAErF,MAAI,CAAC,iBAAiB,KAAK,KAAK,WAAW,GAAG;AAC5C,UAAM,gEAAgE;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,MAAI,QAAQ,WAAW,GAAG;AACxB,SAAK,qDAAqD;AAC1D;AAAA,EACF;AAGA,QAAM,UAAU,QAAQ,IAAI,CAAC,WAAW;AAAA,IACtC,KAAK,MAAM;AAAA,IACX,OAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,EAAE;AAGF,UAAQ,KAAK;AAAA,IACX,KAAK;AAAA,IACL,OAAO,KAAK,UAAU,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAAA,EAClD,CAAC;AAGD,QAAM,UAAUC,MAAK,OAAO,GAAG,gBAAgB,KAAK,IAAI,CAAC,OAAO;AAChE,QAAMC,WAAU,SAAS,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAEzD,OAAK,aAAa,QAAQ,MAAM,OAAO,QAAQ,WAAW,IAAI,KAAK,GAAG,WAAW;AAEjF,MAAI;AACF,UAAM,OAAO,CAAC,MAAM,QAAQ,OAAO,SAAS,kBAAkB,KAAK,WAAW;AAC9E,QAAI,KAAK,SAAS;AAChB,WAAK,KAAK,WAAW;AAAA,IACvB;AAEA,UAAM,EAAE,QAAQ,OAAO,IAAI,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;AAClE,QAAI,OAAQ,SAAQ,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC;AAC1C,QAAI,UAAU,CAAC,OAAO,SAAS,YAAY,EAAG,SAAQ,MAAM,IAAI,OAAO,KAAK,CAAC,CAAC;AAE9E,YAAQ,YAAY,QAAQ,MAAM,OAAO,QAAQ,WAAW,IAAI,KAAK,GAAG,mBAAmB;AAAA,EAC7F,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,kBAAkB,GAAG,EAAE;AAC7B,SAAK,8EAA8E;AACnF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AC/DA,SAAS,eAAe,oBAAoB;AAW5C,eAAsB,KAAK,MAAc,SAAqB,MAAkC;AAC9F,QAAM,UAA6B,CAAC;AAEpC,MAAI,KAAK,KAAK;AACZ,YAAQ,MAAM,KAAK;AAAA,EACrB;AAEA,MAAI,KAAK,MAAM;AACb,UAAM,UAAU,cAAc,KAAK,IAAI;AACvC,UAAM,SAAS,aAAa,OAAO;AACnC,QAAI,CAAC,OAAO,OAAO;AACjB,YAAM,iBAAiB,OAAO,MAAM,EAAE;AACtC,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,OAAO;AAAA,EACjB;AAEA,MAAI,KAAK,MAAM;AACb,YAAQ,OAAO,KAAK,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,EACzD;AAEA,MAAI,KAAK,SAAS;AAChB,YAAQ,YAAY,IAAI,KAAK,KAAK,OAAO,EAAE,YAAY;AAAA,EACzD;AAEA,MAAI,KAAK,aAAa;AACpB,YAAQ,cAAc,KAAK;AAAA,EAC7B;AAEA,MAAI,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACrC,UAAM,+EAA+E;AACrF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ,OAAO,MAAM,OAAO;AAClD,YAAQ,YAAY,IAAI,IAAI,QAAQ,OAAO,YAAO,QAAQ,IAAI,MAAM,EAAE,EAAE;AAAA,EAC1E,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,GAAG;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ACtDA,SAAS,aAAAC,kBAAiB;AAU1B,SAAS,MAAM,SAA6B;AAC1C,QAAM,SAAS;AACf,QAAM,OAAO,QAAQ,IAAI,CAAC,MAAM;AAC9B,UAAM,QAAQ,EAAE,eAAe,IAAI,QAAQ,MAAM,GAAG;AACpD,UAAM,QAAQ,EAAE,QAAQ,CAAC,GAAG,KAAK,GAAG;AACpC,WAAO,GAAG,EAAE,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,IAAI,EAAE,SAAS,IAAI,EAAE,aAAa,EAAE,IAAI,IAAI;AAAA,EAC/E,CAAC;AACD,SAAO,GAAG,CAAC,QAAQ,GAAG,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA;AACxC;AAEA,eAAsB,WAAW,SAAqB,MAAoC;AACxF,QAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,MAAI,QAAQ,WAAW,GAAG;AACxB,SAAK,oBAAoB;AACzB;AAAA,EACF;AAEA,QAAM,SAAS,KAAK,UAAU;AAC9B,MAAI;AAEJ,MAAI,WAAW,OAAO;AACpB,aAAS,MAAM,OAAO;AAAA,EACxB,OAAO;AACL,aAAS,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA;AAAA,EAC9C;AAEA,MAAI,KAAK,QAAQ;AACf,UAAMC,WAAU,KAAK,QAAQ,MAAM;AACnC,YAAQ,YAAY,QAAQ,MAAM,OAAO,QAAQ,WAAW,IAAI,KAAK,GAAG,OAAO,KAAK,MAAM,EAAE;AAAA,EAC9F,OAAO;AACL,YAAQ,OAAO,MAAM,MAAM;AAC3B,QAAI,QAAQ,OAAO,OAAO;AACxB,WAAK,GAAG,IAAI,GAAG,QAAQ,MAAM,OAAO,QAAQ,WAAW,IAAI,KAAK,GAAG,WAAW,CAAC,EAAE;AAAA,IACnF;AAAA,EACF;AACF;;;AC9CA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,eAAe;AAiBxB,SAAS,SAAS,SAAgC;AAChD,QAAM,QAAQ,QAAQ,KAAK,EAAE,MAAM,IAAI;AACvC,MAAI,MAAM,SAAS,EAAG,QAAO,CAAC;AAE9B,QAAM,SAAS,MAAM,CAAC,GAAG,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC;AACrE,QAAM,UAAU,OAAO,QAAQ,MAAM;AACrC,QAAM,SAAS,OAAO,QAAQ,KAAK;AACnC,QAAM,UAAU,OAAO,QAAQ,aAAa;AAC5C,QAAM,aAAa,OAAO,QAAQ,WAAW;AAC7C,QAAM,UAAU,OAAO,QAAQ,MAAM;AAErC,MAAI,YAAY,MAAM,WAAW,IAAI;AACnC,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AAEA,SAAO,MAAM,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS;AAClC,UAAM,OAAO,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAChD,UAAM,QAAqB;AAAA,MACzB,MAAM,KAAK,OAAO;AAAA,MAClB,KAAK,KAAK,MAAM;AAAA,IAClB;AACA,QAAI,YAAY,MAAM,KAAK,OAAO,EAAG,OAAM,cAAc,KAAK,OAAO;AACrE,QAAI,eAAe,MAAM,KAAK,UAAU,EAAG,OAAM,YAAY,KAAK,UAAU;AAC5E,QAAI,YAAY,MAAM,KAAK,OAAO;AAChC,YAAM,OAAO,KAAK,OAAO,GAAG,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC5D,WAAO;AAAA,EACT,CAAC;AACH;AAEA,eAAsB,WACpB,MACA,SACA,MACe;AACf,MAAI;AACJ,MAAI;AACF,UAAM,eAAe,QAAQ,IAAI;AACjC,cAAU,MAAMC,UAAS,cAAc,OAAO;AAAA,EAChD,QAAQ;AACN,UAAM,qBAAqB,IAAI,EAAE;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,KAAK,WAAW,KAAK,SAAS,MAAM,IAAI,QAAQ;AAC/D,MAAI;AAEJ,MAAI;AACF,QAAI,WAAW,OAAO;AACpB,gBAAU,SAAS,OAAO;AAAA,IAC5B,OAAO;AACL,YAAM,SAAS,KAAK,MAAM,OAAO;AAEjC,gBAAU,MAAM,QAAQ,MAAM,IAAI,SAAU,OAAO,QAAQ,OAAO,OAAO,MAAM;AAAA,IACjF;AAAA,EACF,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,mBAAmB,OAAO,YAAY,CAAC,UAAU,GAAG,EAAE;AAC5D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,SAAK,kCAAkC;AACvC;AAAA,EACF;AAEA,OAAK,aAAa,QAAQ,MAAM,OAAO,QAAQ,WAAW,IAAI,KAAK,GAAG,KAAK;AAE3E,MAAI,WAAW;AACf,MAAI,UAAU;AAEd,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,KAAK;AAC7B;AACA;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAQ,OAAO,MAAM,MAAM,MAAM,KAAK;AAAA,QAC1C,aAAa,MAAM;AAAA,QACnB,WAAW,MAAM;AAAA,QACjB,MAAM,MAAM;AAAA,MACd,CAAC;AACD;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,IAAI,KAAK,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,EAAE,CAAC,EAAE;AACtD;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,YAAY,QAAQ,OAAO,aAAa,IAAI,KAAK,GAAG,EAAE;AAC9D,MAAI,UAAU,GAAG;AACf,SAAK,GAAG,OAAO,QAAQ,YAAY,IAAI,MAAM,KAAK,iCAAiC;AAAA,EACrF;AACF;;;AC/GA,SAAS,aAAAC,YAAW,UAAAC,eAAc;AAClC,OAAOC,SAAQ;AAGf,eAAsBC,MAAK,MAAc,SAAqC;AAC5E,QAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI;AACpC,MAAI,CAAC,OAAO;AACV,UAAM,SAAS,IAAI,aAAa;AAChC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,MAAM,QAAQ,WAAW;AACzC,QAAM,WAAW,UAAU,GAAG,OAAO,IAAI,MAAM,IAAI,KAAK,MAAM;AAC9D,QAAM,cAAcC,QAAO,MAAM,GAAG,IAAIC,WAAU,MAAM,KAAK,MAAM,GAAG,IAAI,MAAM;AAEhF,UAAQ,IAAI;AACZ,UAAQ,IAAI,KAAKC,IAAG,KAAK,OAAO,CAAC,UAAUA,IAAG,KAAK,MAAM,IAAI,CAAC,EAAE;AAChE,UAAQ,IAAI,KAAKA,IAAG,KAAK,YAAY,CAAC,KAAK,QAAQ,EAAE;AACrD,UAAQ,IAAI,KAAKA,IAAG,KAAK,SAAS,CAAC,QAAQ,MAAM,GAAG,EAAE;AACtD,MAAI,gBAAgB,MAAM,KAAK;AAC7B,YAAQ,IAAI,KAAKA,IAAG,KAAK,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,EAAE;AAAA,EAC/D;AACA,MAAI,MAAM,aAAa;AACrB,YAAQ,IAAI,KAAKA,IAAG,KAAK,OAAO,CAAC,UAAU,MAAM,WAAW,EAAE;AAAA,EAChE;AACA,UAAQ,IAAI,KAAKA,IAAG,KAAK,UAAU,CAAC,OAAO,MAAM,SAAS,EAAE;AAC5D,MAAI,MAAM,WAAW;AACnB,YAAQ,IAAI,KAAKA,IAAG,KAAK,UAAU,CAAC,OAAO,MAAM,SAAS,EAAE;AAAA,EAC9D;AACA,MAAIF,QAAO,MAAM,GAAG,GAAG;AACrB,YAAQ,IAAI,KAAKE,IAAG,KAAK,MAAM,CAAC,EAAE;AAClC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG,GAAG;AACpD,UAAI,MAAO,SAAQ,IAAI,OAAO,IAAI,GAAG,CAAC,KAAK,KAAK,EAAE;AAAA,IACpD;AAAA,EACF;AACA,UAAQ,IAAI;AACd;;;ACrCA,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,aAAAC,kBAAiB;AAE1B,SAAS,kBAAkB;AAQ3B,eAAsB,KAAK,QAAgB,MAAkC;AAC3E,MAAIC,YAAW,MAAM,KAAK,CAAC,KAAK,OAAO;AACrC,UAAM,GAAG,MAAM,4CAA4C;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,KAAkB;AAAA,IACtB,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS,KAAK,WAAW;AAAA,IACzB,MAAM,CAAC;AAAA,EACT;AAEA,QAAMC,WAAU,QAAQ,GAAG,KAAK,UAAU,IAAI,MAAM,CAAC,CAAC;AAAA,CAAI;AAE1D,UAAQ,WAAW,MAAM,EAAE;AAC3B,MAAI,KAAK,SAAS;AAChB,SAAK,mBAAmB,IAAI,KAAK,OAAO,CAAC,EAAE;AAAA,EAC7C,OAAO;AACL,SAAK,mEAAmE;AAAA,EAC1E;AACF;;;AC/BA,OAAOC,SAAQ;AAGf,eAAsB,KAAK,SAAqC;AAC9D,QAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,MAAI,QAAQ,WAAW,GAAG;AACxB,SAAK,iEAAiE;AACtE;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,QAAQ,WAAW;AAEzC,UAAQ,IAAI,IAAI;AAAA,IAAO,QAAQ,MAAM,iBAAiB,QAAQ,WAAW,IAAI,KAAK,GAAG;AAAA,CAAK,CAAC;AAE3F,aAAW,SAAS,SAAS;AAC3B,UAAM,QAAQ,UAAU,GAAG,OAAO,IAAI,MAAM,IAAI,KAAK,MAAM;AAC3D,YAAQ,IAAI,KAAKC,IAAG,KAAKA,IAAG,KAAK,KAAK,CAAC,CAAC,EAAE;AAC1C,YAAQ,IAAI,cAAS,MAAM,GAAG,EAAE;AAChC,QAAI,MAAM,aAAa;AACrB,cAAQ,IAAI,OAAO,IAAI,MAAM,WAAW,CAAC,EAAE;AAAA,IAC7C;AACA,QAAI,MAAM,WAAW;AACnB,cAAQ,IAAI,OAAO,IAAI,YAAY,MAAM,SAAS,EAAE,CAAC,EAAE;AAAA,IACzD;AACA,YAAQ,IAAI;AAAA,EACd;AACF;;;AC5BA,SAAS,aAAAC,kBAAiB;;;ACA1B,OAAO,YAAY;AAYnB,eAAsB,WACpB,KACA,SAAmB,OACnB,OAAe,KACW;AAC1B,MAAI,WAAW,OAAO;AACpB,WAAO,OAAO,SAAS,KAAK;AAAA,MAC1B,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,SAAO,OAAO,SAAS,KAAK;AAAA,IAC1B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,EACV,CAAC;AACH;;;ADnBA,eAAsB,GAAG,MAAc,SAAqB,MAAgC;AAE1F,QAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI;AACzC,MAAI,CAAC,QAAQ;AACX,UAAM,SAAS,IAAI,aAAa;AAChC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAKA,QAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,QAAQ,MAAM,OAAO,EAAE,CAAC;AAC7D,QAAM,QAAQ,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAIjD,QAAM,cAAc,OAAO;AAE3B,QAAM,SAAmB,KAAK,UAAU;AACxC,QAAM,OAAO,KAAK,OAAO,SAAS,KAAK,MAAM,EAAE,IAAI;AAEnD,MAAI,OAAO,MAAM,IAAI,KAAK,OAAO,MAAM,OAAO,MAAM;AAClD,UAAM,yCAAyC;AAC/C,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,WAAW,aAAa,QAAQ,IAAI;AAEzD,QAAI,KAAK,QAAQ;AACf,YAAMC,WAAU,KAAK,QAAQ,MAAM;AACnC,cAAQ,oBAAoB,KAAK,MAAM,EAAE;AAAA,IAC3C,OAAO;AAEL,UAAI,OAAO,WAAW,UAAU;AAC9B,gBAAQ,IAAI,MAAM;AAAA,MACpB,OAAO;AACL,gBAAQ,OAAO,MAAM,MAAM;AAAA,MAC7B;AAAA,IACF;AAEA,QAAI,OAAO;AACT,WAAK,qBAAqB,IAAI,YAAO,MAAM,GAAG,EAAE;AAAA,IAClD;AAAA,EACF,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,+BAA+B,GAAG,EAAE;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AE3DA;AAAA,EACE;AAAA,EACA,UAAAC;AAAA,EACA,iBAAAC;AAAA,EAGA,gBAAAC;AAAA,EACA;AAAA,OACK;AAcP,eAAsB,QACpB,KACA,SACA,MACe;AACf,QAAM,YAAY,YAAY,GAAG;AACjC,MAAI,CAAC,UAAU,OAAO;AACpB,UAAM,gBAAgB,UAAU,MAAM,EAAE;AACxC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI,KAAK,MAAM;AACb,WAAOC,eAAc,KAAK,IAAI;AAC9B,UAAM,aAAaC,cAAa,IAAI;AACpC,QAAI,CAAC,WAAW,OAAO;AACrB,YAAM,iBAAiB,WAAW,MAAM,EAAE;AAC1C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,OAAO;AACL,WAAO,mBAAmB;AAC1B,WAAO,MAAM,QAAQ,IAAI,IAAI,GAAG;AAC9B,aAAO,mBAAmB;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,MAAiB,CAAC;AACxB,MAAI,KAAK,WAAY,KAAI,aAAa,KAAK;AAC3C,MAAI,KAAK,WAAY,KAAI,aAAa,KAAK;AAC3C,MAAI,KAAK,aAAc,KAAI,eAAe,KAAK;AAC/C,MAAI,KAAK,SAAU,KAAI,WAAW,KAAK;AACvC,MAAI,KAAK,YAAa,KAAI,cAAc,KAAK;AAE7C,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;AAAA,IACxD,GAAI,KAAK,WAAW,EAAE,WAAW,IAAI,KAAK,KAAK,OAAO,EAAE,YAAY,EAAE;AAAA,IACtE,GAAIC,QAAO,GAAG,KAAK,EAAE,IAAI;AAAA,EAC3B;AAEA,QAAM,QAAQ,IAAI,KAAK;AAEvB,QAAM,UAAU,MAAM,QAAQ,WAAW;AACzC,QAAM,WAAW,UAAU,GAAG,OAAO,IAAI,IAAI,KAAK;AAElD,UAAQ,aAAa,IAAI,GAAG,CAAC,EAAE;AAC/B,UAAQ,IAAI,KAAK,QAAQ,EAAE;AAC7B;;;ACvEA,OAAOC,SAAQ;AAQf,eAAsB,MAAM,MAAc,SAAqB,MAAmC;AAChG,MAAI;AACF,UAAM,OAAO,MAAM,QAAQ,SAAS,IAAI;AAExC,QAAI,SAAS,MAAM;AACjB,YAAM,sDAAsD;AAC5D;AAAA,QACE;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACzC;AAAA,IACF;AAEA,YAAQ,IAAI;AACZ,YAAQ,IAAI,KAAKC,IAAG,KAAK,WAAW,CAAC,IAAIA,IAAG,KAAK,IAAI,CAAC,EAAE;AACxD,YAAQ,IAAI,KAAKA,IAAG,KAAK,eAAe,CAAC,IAAI,KAAK,KAAK,EAAE;AAEzD,QAAI,OAAO,KAAK,KAAK,KAAK,EAAE,SAAS,GAAG;AACtC,cAAQ,IAAI;AACZ,cAAQ,IAAI,KAAKA,IAAG,KAAK,eAAe,CAAC,EAAE;AAC3C,YAAM,SAAS,OAAO,QAAQ,KAAK,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAC/E,iBAAW,CAAC,MAAM,KAAK,KAAK,QAAQ;AAClC,cAAM,MAAM,SAAI,OAAO,KAAK,IAAI,OAAO,EAAE,CAAC;AAC1C,gBAAQ,IAAI,OAAO,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,KAAK,EAAE;AAAA,MAChD;AAAA,IACF;AAEA,QAAI,OAAO,KAAK,KAAK,GAAG,EAAE,SAAS,GAAG;AACpC,cAAQ,IAAI;AACZ,cAAQ,IAAI,KAAKA,IAAG,KAAK,gBAAgB,CAAC,EAAE;AAC5C,YAAM,SAAS,OAAO,QAAQ,KAAK,GAAG,EACnC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,EAC5B,MAAM,GAAG,EAAE;AACd,iBAAW,CAAC,SAAS,KAAK,KAAK,QAAQ;AACrC,gBAAQ,IAAI,OAAO,OAAO,KAAK,KAAK,EAAE;AAAA,MACxC;AAAA,IACF;AAEA,QAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,GAAG;AACzC,cAAQ,IAAI;AACZ,cAAQ,IAAI,KAAKA,IAAG,KAAK,gBAAgB,CAAC,EAAE;AAC5C,YAAM,SAAS,OAAO,QAAQ,KAAK,QAAQ,EACxC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,EAC5B,MAAM,GAAG,EAAE;AACd,iBAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,gBAAQ,IAAI,OAAO,OAAO,IAAI,UAAU,CAAC,KAAK,KAAK,EAAE;AAAA,MACvD;AAAA,IACF;AAEA,QAAI,OAAO,KAAK,KAAK,MAAM,EAAE,SAAS,GAAG;AACvC,cAAQ,IAAI;AACZ,cAAQ,IAAI,KAAKA,IAAG,KAAK,UAAU,CAAC,EAAE;AACtC,iBAAW,CAAC,QAAQ,KAAK,KAAK,OAAO,QAAQ,KAAK,MAAM,GAAG;AACzD,gBAAQ,IAAI,OAAO,MAAM,KAAK,KAAK,EAAE;AAAA,MACvC;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,EACd,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,GAAG;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ACnEA,eAAsB,cAAcC,SAA0C;AAC5E,UAAQA,QAAO,MAAM;AAAA,IACnB,KAAK,UAAU;AACb,UAAI,CAACA,QAAO,QAAQ;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAAsB;AAC7D,aAAO,IAAI,cAAcA,QAAO,QAAQA,QAAO,OAAO;AAAA,IACxD;AAAA,IAEA,KAAK,OAAO;AACV,UAAI,CAACA,QAAO,KAAK;AACf,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,mBAAmB;AACvD,aAAO,IAAI,WAAWA,QAAO,KAAKA,QAAO,OAAO;AAAA,IAClD;AAAA,IAEA,SAAS;AAEP,aAAO,IAAI,mBAAmBA,QAAO,QAAQA,QAAO,OAAO;AAAA,IAC7D;AAAA,EACF;AACF;;;ACpCA,SAAS,WAAAC,gBAAe;AACxB,SAAS,iBAAiB,mBAAmB;AAGtC,SAASC,eAAc,QAA8B;AAC1D,QAAM,WAAWD,SAAQ,UAAU,eAAe;AAClD,SAAO,IAAI,YAAY,QAAQ;AACjC;;;AjBcA,IAAM,UAAU,IAAI,QAAQ;AAO5B,eAAe,eAAe,YAAkD;AAC9E,MAAI,WAAW,IAAI;AAGjB,UAAM,EAAE,oBAAAE,oBAAmB,IAAI,MAAM,OAAO,4BAA4B;AACxE,UAAM,SAASC,SAAQ,WAAW,EAAE;AACpC,WAAO,IAAID,oBAAmB,QAAQ,EAAE;AAAA,EAC1C;AAEA,QAAME,UAAS,WAAW;AAC1B,SAAO,cAAqBA,OAAM;AACpC;AAEA,QACG,KAAK,OAAO,EACZ,YAAY,iCAAiC,EAC7C,QAAQ,OAAO,EACf,OAAO,eAAe,iCAAiC;AAE1D,QACG,QAAQ,SAAS,EACjB,YAAY,eAAe,EAC3B,SAAS,SAAS,gBAAgB,EAClC,OAAO,qBAAqB,iCAAiC,EAC7D,OAAO,4BAA4B,0BAA0B,EAC7D,OAAO,wBAAwB,YAAY,EAC3C,OAAO,wBAAwB,YAAY,EAC3C,OAAO,0BAA0B,cAAc,EAC/C,OAAO,sBAAsB,UAAU,EACvC,OAAO,yBAAyB,aAAa,EAC7C,OAAO,oBAAoB,4BAA4B,EACvD,OAAO,OAAO,KAAa,SAAS;AACnC,QAAM,UAAUC,eAAkB,QAAQ,KAAK,EAAE,EAAE;AACnD,QAAM,QAAQ,KAAK,SAAS;AAAA,IAC1B,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB,YAAY,KAAK;AAAA,IACjB,YAAY,KAAK;AAAA,IACjB,cAAc,KAAK;AAAA,IACnB,UAAU,KAAK;AAAA,IACf,aAAa,KAAK;AAAA,IAClB,SAAS,KAAK;AAAA,EAChB,CAAC;AACH,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,MAAM,IAAI,EACV,YAAY,yBAAyB,EACrC,OAAO,YAAY;AAClB,QAAM,UAAUA,eAAkB,QAAQ,KAAK,EAAE,EAAE;AACnD,QAAM,KAAK,OAAO;AACpB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,MAAM,IAAI,EACV,YAAY,wBAAwB,EACpC,SAAS,UAAU,gBAAgB,EACnC,OAAO,OAAO,SAAiB;AAC9B,QAAM,UAAUA,eAAkB,QAAQ,KAAK,EAAE,EAAE;AACnD,QAAM,IAAI,MAAM,OAAO;AACzB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,yBAAyB,EACrC,SAAS,UAAU,iBAAiB,EACpC,OAAO,OAAO,SAAiB;AAC9B,QAAM,UAAUA,eAAkB,QAAQ,KAAK,EAAE,EAAE;AACnD,QAAMC,MAAK,MAAM,OAAO;AAC1B,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,SAAS,SAAS,2BAA2B,EAC7C,SAAS,WAAW,6BAA6B,EACjD,OAAO,OAAO,KAAa,UAA8B;AACxD,QAAM,UAAUD,eAAkB,QAAQ,KAAK,EAAE,EAAE;AACnD,QAAM,OAAO,KAAK,OAAO,OAAO;AAClC,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,qCAAqC,EACjD,OAAO,wBAAwB,0BAA0B,EACzD,OAAO,eAAe,6BAA6B,EACnD,OAAO,OAAO,SAAS;AACtB,QAAM,SAASF,SAAQ,QAAQ,KAAK,EAAE,MAAMI,gBAAe;AAC3D,QAAM,KAAK,QAAQ,EAAE,SAAS,KAAK,SAAS,OAAO,KAAK,MAAM,CAAC;AACjE,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,8BAA8B,EAC1C,eAAe,uBAAuB,4BAA4B,EAClE,OAAO,aAAa,gCAAgC,EACpD,OAAO,OAAO,SAAS;AACtB,QAAM,UAAUF,eAAkB,QAAQ,KAAK,EAAE,EAAE;AACnD,QAAM,OAAO,SAAS;AAAA,IACpB,aAAa,KAAK;AAAA,IAClB,SAAS,KAAK;AAAA,EAChB,CAAC;AACH,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,gCAAgC,EAC5C,SAAS,UAAU,cAAc,EACjC,OAAO,eAAe,gBAAgB,EACtC,OAAO,iBAAiB,UAAU,EAClC,OAAO,iBAAiB,sBAAsB,EAC9C,OAAO,oBAAoB,4BAA4B,EACvD,OAAO,wBAAwB,aAAa,EAC5C,OAAO,OAAO,MAAc,SAAS;AACpC,QAAM,UAAU,MAAM,eAAe,QAAQ,KAAK,CAAC;AACnD,QAAM,KAAK,MAAM,SAAS,IAAI;AAChC,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,iCAAiC,EAC7C,SAAS,UAAU,iBAAiB,EACpC,OAAO,UAAU,iBAAiB,EAClC,OAAO,qBAAqB,4BAA4B,EACxD,OAAO,OAAO,MAAc,SAAS;AACpC,QAAM,UAAU,MAAM,eAAe,QAAQ,KAAK,CAAC;AACnD,QAAM,MAAM,MAAM,SAAS,IAAI;AACjC,CAAC;AAEH,QACG,QAAQ,IAAI,EACZ,YAAY,+BAA+B,EAC3C,SAAS,UAAU,yBAAyB,EAC5C,OAAO,uBAAuB,kBAAkB,EAChD,OAAO,yBAAyB,2BAA2B,KAAK,EAChE,OAAO,mBAAmB,wBAAwB,KAAK,EACvD,OAAO,OAAO,MAAc,SAAS;AACpC,QAAM,UAAU,MAAM,eAAe,QAAQ,KAAK,CAAC;AACnD,QAAM,GAAG,MAAM,SAAS,IAAI;AAC9B,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,yBAAyB,EACrC,SAAS,UAAU,+BAA+B,EAClD,OAAO,yBAAyB,wBAAwB,EACxD,OAAO,OAAO,MAAc,SAAS;AACpC,QAAM,UAAU,MAAM,eAAe,QAAQ,KAAK,CAAC;AACnD,QAAM,WAAW,MAAM,SAAS,IAAI;AACtC,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,2BAA2B,EACvC,OAAO,yBAAyB,4BAA4B,MAAM,EAClE,OAAO,uBAAuB,sCAAsC,EACpE,OAAO,OAAO,SAAS;AACtB,QAAM,UAAU,MAAM,eAAe,QAAQ,KAAK,CAAC;AACnD,QAAM,WAAW,SAAS,IAAI;AAChC,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,kCAAkC,EAC9C,OAAO,sBAAsB,qBAAqB,WAAW,EAC7D,OAAO,sBAAsB,oBAAoB,MAAM,EACvD,OAAO,OAAO,SAAS;AACtB,QAAM,MAAM,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO,CAAC;AACxD,CAAC;AAEH,QAAQ,WAAW,EAAE,MAAM,CAAC,QAAe;AACzC,QAAM,IAAI,OAAO;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["resolve","DEFAULT_DB_PATH","writeFile","join","join","writeFile","writeFile","writeFile","readFile","readFile","appendUtm","hasUtm","pc","info","hasUtm","appendUtm","pc","existsSync","writeFile","existsSync","writeFile","pc","pc","writeFile","writeFile","hasUtm","normalizeSlug","validateSlug","normalizeSlug","validateSlug","hasUtm","pc","pc","config","resolve","createBackend","JsonBackendAdapter","resolve","config","createBackend","info","DEFAULT_DB_PATH"]}
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ JsonBackendAdapter
4
+ } from "./chunk-I7CHG5Z3.js";
5
+ export {
6
+ JsonBackendAdapter
7
+ };
8
+ //# sourceMappingURL=json-adapter-5YGDHNVJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "clipr-cli",
3
+ "version": "0.0.5",
4
+ "description": "A fast, git-friendly URL shortener. CLI + programmatic API.",
5
+ "type": "module",
6
+ "bin": {
7
+ "clipr": "./dist/index.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/api.js",
12
+ "types": "./dist/api.d.ts"
13
+ },
14
+ "./cli": {
15
+ "import": "./dist/index.js",
16
+ "types": "./dist/index.d.ts"
17
+ }
18
+ },
19
+ "main": "./dist/api.js",
20
+ "types": "./dist/api.d.ts",
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "dev": "tsup --watch",
29
+ "test": "vitest run",
30
+ "test:watch": "vitest",
31
+ "typecheck": "tsc --noEmit",
32
+ "clean": "rm -rf dist",
33
+ "prepublishOnly": "pnpm build"
34
+ },
35
+ "keywords": [
36
+ "url-shortener",
37
+ "short-url",
38
+ "cli",
39
+ "clipr",
40
+ "shorten",
41
+ "redirect",
42
+ "cloudflare-workers",
43
+ "github-pages",
44
+ "qrcode",
45
+ "utm"
46
+ ],
47
+ "homepage": "https://github.com/hammadkhanxcm/clipr#readme",
48
+ "bugs": {
49
+ "url": "https://github.com/hammadkhanxcm/clipr/issues"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/hammadkhanxcm/clipr.git",
54
+ "directory": "packages/cli"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "dependencies": {
60
+ "@clipr/core": "workspace:*",
61
+ "commander": "^14.0.3",
62
+ "picocolors": "^1.1.0",
63
+ "qrcode": "^1.5.4"
64
+ },
65
+ "devDependencies": {
66
+ "@types/node": "^25.5.0",
67
+ "@types/qrcode": "^1.5.6",
68
+ "tsup": "^8.0.0",
69
+ "vitest": "^4.1.2"
70
+ },
71
+ "engines": {
72
+ "node": ">=22"
73
+ },
74
+ "license": "MIT",
75
+ "author": "Hammad Khan"
76
+ }