@unitio-code/url-shortener
Version:
A simple URL shortening library
116 lines (115 loc) • 4.01 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createShortUrl = createShortUrl;
exports.decodeUrl = decodeUrl;
const utils_1 = require("./utils");
/**
* Default options for creating a short URL
*/
const DEFAULT_CREATE_OPTIONS = {
...utils_1.DEFAULT_SHORT_URL_OPTIONS,
hashAlgorithm: "djb2",
maxRetries: 10,
};
/**
* Creates a short URL from a long URL with customizable options
* @param longUrl - The original long URL to shorten
* @param domain - Domain name for the short URL
* @param options - Optional configuration options
* @returns The shortened URL
*/
async function createShortUrl(longUrl, domain, options) {
if (!(0, utils_1.isValidUrl)(longUrl)) {
throw new Error("Invalid URL provided");
}
const opts = {
...DEFAULT_CREATE_OPTIONS,
domain,
...options,
};
const storage = opts.storage || utils_1.defaultStorage;
const maxRetries = opts.maxRetries || 10;
let hash;
let attempts = 0;
let positiveHash;
if (opts.hashAlgorithm === "custom" && opts.customHashFn) {
hash = opts.customHashFn(longUrl);
}
else if (opts.hashAlgorithm === "sdbm") {
hash = 0;
for (let i = 0; i < longUrl.length; i++) {
const char = longUrl.charCodeAt(i);
hash = char + (hash << 6) + (hash << 16) - hash;
}
}
else {
hash = 5381;
for (let i = 0; i < longUrl.length; i++) {
const char = longUrl.charCodeAt(i);
hash = (hash << 5) + hash + char;
}
}
positiveHash = Math.abs(hash);
if (await (0, utils_1.hasUrlMapping)(positiveHash, storage)) {
const existingUrl = await (0, utils_1.getOriginalUrl)(positiveHash, storage);
if (existingUrl === longUrl) {
return (0, utils_1.buildShortUrl)(positiveHash, opts);
}
}
do {
if (attempts > 0) {
if (opts.hashAlgorithm === "custom" && opts.customHashFn) {
hash = opts.customHashFn(longUrl + attempts);
}
else if (opts.hashAlgorithm === "sdbm") {
hash = 0;
const input = longUrl + attempts;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = char + (hash << 6) + (hash << 16) - hash;
}
}
else {
hash = 5381;
const input = longUrl + attempts;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = (hash << 5) + hash + char;
}
}
positiveHash = Math.abs(hash);
}
attempts++;
} while (await (0, utils_1.hasUrlMapping)(positiveHash, storage) && attempts < maxRetries);
if (attempts >= maxRetries) {
throw new Error("Failed to generate unique short URL after maximum retries");
}
await (0, utils_1.storeUrlMapping)(positiveHash, longUrl, storage, opts.ttl);
return (0, utils_1.buildShortUrl)(positiveHash, opts);
}
/**
* Extracts the short code from a short URL
* @param shortUrl - The short URL
* @returns The extracted short code
*/
function extractShortCode(shortUrl) {
const urlWithoutProtocol = shortUrl.replace(/^https?:\/\//, "");
const parts = urlWithoutProtocol.split("/");
return parts[parts.length - 1];
}
/**
* Decodes a short URL back to its original URL
* @param shortUrl - The shortened URL to decode
* @param storage - Storage instance to use (optional)
* @returns The original URL if found, or undefined if not found
*/
async function decodeUrl(shortUrl, storage) {
try {
const shortCode = extractShortCode(shortUrl);
const id = (0, utils_1.decodeShortUrl)(shortCode);
return await (0, utils_1.getOriginalUrl)(id, storage || utils_1.defaultStorage);
}
catch (error) {
return undefined;
}
}