code-craft-studio
Version:
A comprehensive QR code and barcode scanning/generation library for React. Works with or without Capacitor. Supports 22+ QR data types and 14+ barcode formats (EAN, UPC, Code 128, etc.), with customizable designs, analytics, and React components. Provider
265 lines • 11.7 kB
JavaScript
import { QRType } from '../../definitions';
import { logger } from '../../utils/logger';
export class QRValidationError extends Error {
constructor(message, field) {
super(message);
this.field = field;
this.name = 'QRValidationError';
}
}
export const validators = {
[QRType.WEBSITE]: (data) => {
if (!data.url)
throw new QRValidationError('URL is required', 'url');
if (!isValidUrl(data.url))
throw new QRValidationError('Invalid URL format', 'url');
},
[QRType.PDF]: (data) => {
if (!data.url)
throw new QRValidationError('PDF URL is required', 'url');
if (!isValidUrl(data.url))
throw new QRValidationError('Invalid URL format', 'url');
if (!data.url.toLowerCase().endsWith('.pdf') && !data.url.includes('pdf')) {
logger.warn('URL does not appear to be a PDF file');
}
},
[QRType.IMAGES]: (data) => {
if (!data.images || !Array.isArray(data.images) || data.images.length === 0) {
throw new QRValidationError('At least one image is required', 'images');
}
data.images.forEach((img, index) => {
if (!img.url)
throw new QRValidationError(`Image ${index + 1} URL is required`, `images[${index}].url`);
if (!isValidUrl(img.url))
throw new QRValidationError(`Invalid URL format for image ${index + 1}`, `images[${index}].url`);
});
},
[QRType.VIDEO]: (data) => {
if (!data.url)
throw new QRValidationError('Video URL is required', 'url');
if (!isValidUrl(data.url))
throw new QRValidationError('Invalid URL format', 'url');
},
[QRType.WIFI]: (data) => {
if (!data.ssid)
throw new QRValidationError('Network name (SSID) is required', 'ssid');
if (!data.security)
throw new QRValidationError('Security type is required', 'security');
if (!['WEP', 'WPA', 'WPA2', 'WPA3', 'nopass'].includes(data.security)) {
throw new QRValidationError('Invalid security type', 'security');
}
if (data.security !== 'nopass' && !data.password) {
throw new QRValidationError('Password is required for secured networks', 'password');
}
},
[QRType.MENU]: (data) => {
if (!data.restaurantName)
throw new QRValidationError('Restaurant name is required', 'restaurantName');
if (!data.categories || !Array.isArray(data.categories) || data.categories.length === 0) {
throw new QRValidationError('At least one menu category is required', 'categories');
}
data.categories.forEach((cat, catIndex) => {
if (!cat.name)
throw new QRValidationError(`Category ${catIndex + 1} name is required`, `categories[${catIndex}].name`);
if (!cat.items || !Array.isArray(cat.items) || cat.items.length === 0) {
throw new QRValidationError(`Category "${cat.name}" must have at least one item`, `categories[${catIndex}].items`);
}
cat.items.forEach((item, itemIndex) => {
if (!item.name)
throw new QRValidationError(`Item name is required`, `categories[${catIndex}].items[${itemIndex}].name`);
if (!item.price)
throw new QRValidationError(`Item price is required`, `categories[${catIndex}].items[${itemIndex}].price`);
});
});
},
[QRType.BUSINESS]: (data) => {
if (!data.name)
throw new QRValidationError('Business name is required', 'name');
if (data.email && !isValidEmail(data.email)) {
throw new QRValidationError('Invalid email format', 'email');
}
if (data.website && !isValidUrl(data.website)) {
throw new QRValidationError('Invalid website URL', 'website');
}
},
[QRType.VCARD]: (data) => {
if (!data.firstName && !data.lastName && !data.organization) {
throw new QRValidationError('At least one of firstName, lastName, or organization is required');
}
if (data.email && !isValidEmail(data.email)) {
throw new QRValidationError('Invalid email format', 'email');
}
if (data.website && !isValidUrl(data.website)) {
throw new QRValidationError('Invalid website URL', 'website');
}
},
[QRType.MP3]: (data) => {
if (!data.url)
throw new QRValidationError('Audio URL is required', 'url');
if (!isValidUrl(data.url))
throw new QRValidationError('Invalid URL format', 'url');
},
[QRType.APPS]: (data) => {
if (!data.appStoreUrl && !data.playStoreUrl && !data.windowsStoreUrl && !data.customUrl) {
throw new QRValidationError('At least one app store URL is required');
}
if (data.appStoreUrl && !isValidUrl(data.appStoreUrl)) {
throw new QRValidationError('Invalid App Store URL', 'appStoreUrl');
}
if (data.playStoreUrl && !isValidUrl(data.playStoreUrl)) {
throw new QRValidationError('Invalid Play Store URL', 'playStoreUrl');
}
if (data.windowsStoreUrl && !isValidUrl(data.windowsStoreUrl)) {
throw new QRValidationError('Invalid Windows Store URL', 'windowsStoreUrl');
}
if (data.customUrl && !isValidUrl(data.customUrl)) {
throw new QRValidationError('Invalid custom URL', 'customUrl');
}
},
[QRType.LINKS_LIST]: (data) => {
if (!data.links || !Array.isArray(data.links) || data.links.length === 0) {
throw new QRValidationError('At least one link is required', 'links');
}
data.links.forEach((link, index) => {
if (!link.title)
throw new QRValidationError(`Link ${index + 1} title is required`, `links[${index}].title`);
if (!link.url)
throw new QRValidationError(`Link ${index + 1} URL is required`, `links[${index}].url`);
if (!isValidUrl(link.url))
throw new QRValidationError(`Invalid URL format for link ${index + 1}`, `links[${index}].url`);
});
},
[QRType.COUPON]: (data) => {
if (!data.code)
throw new QRValidationError('Coupon code is required', 'code');
if (data.validUntil && !isValidDate(data.validUntil)) {
throw new QRValidationError('Invalid date format for validUntil', 'validUntil');
}
},
[QRType.FACEBOOK]: (data) => {
if (!data.pageUrl)
throw new QRValidationError('Facebook page URL is required', 'pageUrl');
if (!isValidUrl(data.pageUrl) || !data.pageUrl.includes('facebook.com')) {
throw new QRValidationError('Invalid Facebook URL', 'pageUrl');
}
},
[QRType.INSTAGRAM]: (data) => {
if (!data.profileUrl)
throw new QRValidationError('Instagram profile URL is required', 'profileUrl');
if (!isValidUrl(data.profileUrl) || !data.profileUrl.includes('instagram.com')) {
throw new QRValidationError('Invalid Instagram URL', 'profileUrl');
}
},
[QRType.SOCIAL_MEDIA]: (data) => {
const socialNetworks = Object.keys(data).filter(key => data[key]);
if (socialNetworks.length === 0) {
throw new QRValidationError('At least one social media link is required');
}
socialNetworks.forEach(network => {
if (data[network] && !isValidUrl(data[network])) {
throw new QRValidationError(`Invalid URL for ${network}`, network);
}
});
},
[QRType.WHATSAPP]: (data) => {
if (!data.phoneNumber)
throw new QRValidationError('Phone number is required', 'phoneNumber');
if (!isValidPhoneNumber(data.phoneNumber)) {
throw new QRValidationError('Invalid phone number format', 'phoneNumber');
}
},
[QRType.TEXT]: (data) => {
if (!data.text)
throw new QRValidationError('Text content is required', 'text');
if (data.text.length > 2000) {
throw new QRValidationError('Text is too long (max 2000 characters)', 'text');
}
},
[QRType.EMAIL]: (data) => {
if (!data.to)
throw new QRValidationError('Recipient email is required', 'to');
if (!isValidEmail(data.to))
throw new QRValidationError('Invalid email format', 'to');
if (data.cc && !isValidEmail(data.cc)) {
throw new QRValidationError('Invalid CC email format', 'cc');
}
if (data.bcc && !isValidEmail(data.bcc)) {
throw new QRValidationError('Invalid BCC email format', 'bcc');
}
},
[QRType.SMS]: (data) => {
if (!data.phoneNumber)
throw new QRValidationError('Phone number is required', 'phoneNumber');
if (!isValidPhoneNumber(data.phoneNumber)) {
throw new QRValidationError('Invalid phone number format', 'phoneNumber');
}
},
[QRType.PHONE]: (data) => {
if (!data.phoneNumber)
throw new QRValidationError('Phone number is required', 'phoneNumber');
if (!isValidPhoneNumber(data.phoneNumber)) {
throw new QRValidationError('Invalid phone number format', 'phoneNumber');
}
},
[QRType.LOCATION]: (data) => {
if (typeof data.latitude !== 'number') {
throw new QRValidationError('Latitude must be a number', 'latitude');
}
if (typeof data.longitude !== 'number') {
throw new QRValidationError('Longitude must be a number', 'longitude');
}
if (data.latitude < -90 || data.latitude > 90) {
throw new QRValidationError('Latitude must be between -90 and 90', 'latitude');
}
if (data.longitude < -180 || data.longitude > 180) {
throw new QRValidationError('Longitude must be between -180 and 180', 'longitude');
}
},
[QRType.EVENT]: (data) => {
if (!data.title)
throw new QRValidationError('Event title is required', 'title');
if (!data.startDate)
throw new QRValidationError('Start date is required', 'startDate');
if (!isValidDate(data.startDate)) {
throw new QRValidationError('Invalid start date format', 'startDate');
}
if (data.endDate && !isValidDate(data.endDate)) {
throw new QRValidationError('Invalid end date format', 'endDate');
}
if (data.endDate && new Date(data.endDate) < new Date(data.startDate)) {
throw new QRValidationError('End date must be after start date', 'endDate');
}
},
};
export function validateQRData(type, data) {
const validator = validators[type];
if (!validator) {
throw new QRValidationError(`Unknown QR type: ${type}`);
}
validator(data);
}
// Helper validation functions
function isValidUrl(url) {
try {
new URL(url);
return true;
}
catch (_a) {
return false;
}
}
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
function isValidPhoneNumber(phone) {
// Basic phone validation - accepts digits, spaces, +, -, (, )
const phoneRegex = /^[\d\s+\-()]+$/;
const cleaned = phone.replace(/[\s\-()]/g, '');
return phoneRegex.test(phone) && cleaned.length >= 7 && cleaned.length <= 15;
}
function isValidDate(dateStr) {
const date = new Date(dateStr);
return date instanceof Date && !isNaN(date.getTime());
}
//# sourceMappingURL=qr-validators.js.map