@chop-url/lib
Version:
A TypeScript library for URL shortening functionality with Cloudflare D1 database support
127 lines (126 loc) • 4.89 kB
JavaScript
;
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;