UNPKG

@chop-url/lib

Version:

A TypeScript library for URL shortening functionality with Cloudflare D1 database support

127 lines (126 loc) 4.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChopUrl = void 0; const types_1 = require("./types"); /** * ChopUrl - A URL shortening library */ class ChopUrl { constructor(config) { this.validateConfig(config); this.baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash this.db = config.db; } /** * Creates a short URL for the given original URL * @throws {ChopUrlError} If the URL is invalid or database operation fails */ async createShortUrl(url) { this.validateUrl(url); const shortId = this.generateShortId(); const now = new Date(); try { await this.db.prepare(`INSERT INTO urls (short_id, original_url, created_at, visits) VALUES (?, ?, ?, 0)`) .bind(shortId, url, now.toISOString()) .run(); return { shortId, originalUrl: url, shortUrl: `${this.baseUrl}/${shortId}`, createdAt: now, visits: 0 }; } catch (error) { throw new types_1.ChopUrlError('Failed to create short URL', types_1.ChopUrlErrorCode.DATABASE_ERROR, { originalError: error }); } } /** * Retrieves the original URL for a given short ID * @throws {ChopUrlError} If the URL is not found or database operation fails */ async getOriginalUrl(shortId) { this.validateShortId(shortId); try { const result = await this.db.prepare(`SELECT original_url FROM urls WHERE short_id = ?`) .bind(shortId) .first(); if (!result) { throw new types_1.ChopUrlError('Short URL not found', types_1.ChopUrlErrorCode.URL_NOT_FOUND, { shortId }); } // Increment visit count await this.db.prepare(`UPDATE urls SET visits = visits + 1 WHERE short_id = ?`) .bind(shortId) .run(); return result.original_url; } catch (error) { if (error instanceof types_1.ChopUrlError) throw error; throw new types_1.ChopUrlError('Failed to retrieve original URL', types_1.ChopUrlErrorCode.DATABASE_ERROR, { originalError: error }); } } /** * Gets detailed information about a shortened URL * @throws {ChopUrlError} If the URL is not found or database operation fails */ async getUrlInfo(shortId) { this.validateShortId(shortId); try { const result = await this.db.prepare(`SELECT * FROM urls WHERE short_id = ?`) .bind(shortId) .first(); if (!result) { throw new types_1.ChopUrlError('Short URL not found', types_1.ChopUrlErrorCode.URL_NOT_FOUND, { shortId }); } return { shortId: result.short_id, originalUrl: result.original_url, shortUrl: `${this.baseUrl}/${result.short_id}`, createdAt: new Date(result.created_at), visits: result.visits }; } catch (error) { if (error instanceof types_1.ChopUrlError) throw error; throw new types_1.ChopUrlError('Failed to retrieve URL information', types_1.ChopUrlErrorCode.DATABASE_ERROR, { originalError: error }); } } validateConfig(config) { if (!config.baseUrl) { throw new types_1.ChopUrlError('Base URL is required', types_1.ChopUrlErrorCode.INVALID_URL); } try { new URL(config.baseUrl); } catch (_a) { throw new types_1.ChopUrlError('Invalid base URL', types_1.ChopUrlErrorCode.INVALID_URL, { baseUrl: config.baseUrl }); } if (!config.db) { throw new types_1.ChopUrlError('Database instance is required', types_1.ChopUrlErrorCode.DATABASE_ERROR); } } validateUrl(url) { if (!url) { throw new types_1.ChopUrlError('URL is required', types_1.ChopUrlErrorCode.INVALID_URL); } try { new URL(url); } catch (_a) { throw new types_1.ChopUrlError('Invalid URL format', types_1.ChopUrlErrorCode.INVALID_URL, { url }); } } validateShortId(shortId) { if (!shortId || !/^[a-zA-Z0-9]{7}$/.test(shortId)) { throw new types_1.ChopUrlError('Invalid short ID format', types_1.ChopUrlErrorCode.INVALID_SHORT_ID, { shortId }); } } generateShortId() { const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; return Array.from({ length: 7 }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join(''); } } exports.ChopUrl = ChopUrl;