@vulform/core
Version:
Core utilities and types for VulForm packages
5 lines (3 loc) • 11.5 kB
JavaScript
var{defineProperty:h,getOwnPropertyNames:G,getOwnPropertyDescriptor:I}=Object,L=Object.prototype.hasOwnProperty;var T=new WeakMap,M=(x)=>{var b=T.get(x),P;if(b)return b;if(b=h({},"__esModule",{value:!0}),x&&typeof x==="object"||typeof x==="function")G(x).map((A)=>!L.call(b,A)&&h(b,A,{get:()=>x[A],enumerable:!(P=I(x,A))||P.enumerable}));return T.set(x,b),b};var c=(x,b)=>{for(var P in b)h(x,P,{get:b[P],enumerable:!0,configurable:!0,set:(A)=>b[P]=()=>A})};var y={};c(y,{resolveApiUrl:()=>k,normalizeApiUrl:()=>H,isValidVulFormUrl:()=>w,isSelfHosted:()=>O,isProduction:()=>C,isDevelopment:()=>N,getDefaultConfig:()=>Z,createSelfHostedConfig:()=>g,createSaaSConfig:()=>_,createCustomConfig:()=>D,VulFormError:()=>Q,VulFormApiClient:()=>q,VULFORM_CONFIG:()=>j,RealtimeValidator:()=>S,FormValidator:()=>z,ConfigBuilder:()=>$});module.exports=M(y);var E=()=>{let x=typeof process!=="undefined"?process.env:{};return{baseUrl:x.VULFORM_BASE_URL||x.NEXT_PUBLIC_VULFORM_BASE_URL||x.REACT_APP_VULFORM_BASE_URL||x.VITE_VULFORM_BASE_URL,cdnUrl:x.VULFORM_CDN_URL||x.NEXT_PUBLIC_VULFORM_CDN_URL||x.REACT_APP_VULFORM_CDN_URL||x.VITE_VULFORM_CDN_URL,version:x.VULFORM_VERSION||"1.0.0",environment:x.NODE_ENV||"production"}},j={DEFAULT_BASE_URL:"https://api.vulform.dev",DEFAULT_CDN_URL:"https://cdn.vulform.dev",DEFAULT_TIMEOUT:1e4,DEFAULT_RETRIES:3,SELF_HOSTED_BASE_URL:"/api/v1",LOCAL_DEV_BASE_URL:"http://localhost:3000/api/v1",SDK_VERSION:"1.0.0",SDK_NAME:"vulform-sdk",AUTHOR:"Dogu Yilmaz",ORGANIZATION:"Dogu Yilmaz",HOMEPAGE:"https://vulform.dev",REPOSITORY:"https://github.com/doguyilmaz/vulform",...E()};class ${config;strategy="auto";constructor(x={},b="auto"){this.strategy=b,this.config={baseUrl:this.resolveBaseUrl(x.baseUrl),cdnUrl:this.resolveCdnUrl(x.cdnUrl),timeout:x.timeout||j.DEFAULT_TIMEOUT,retries:x.retries||j.DEFAULT_RETRIES,version:x.version||j.SDK_VERSION,environment:x.environment||j.environment||"production"}}resolveBaseUrl(x){if(x)return x;let b=j.baseUrl;if(b)return b;switch(this.strategy){case"saas":return j.DEFAULT_BASE_URL;case"self-hosted":return j.SELF_HOSTED_BASE_URL;case"custom":return j.DEFAULT_BASE_URL;case"auto":default:if(this.isLocalDevelopment())return j.LOCAL_DEV_BASE_URL;if(typeof window!=="undefined"){let P=window.location.origin;if(this.strategy==="auto")return j.SELF_HOSTED_BASE_URL}return j.DEFAULT_BASE_URL}}resolveCdnUrl(x){if(x)return x;let b=j.cdnUrl;if(b)return b;return j.DEFAULT_CDN_URL}isLocalDevelopment(){return this.config.environment==="development"||typeof window!=="undefined"&&(window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1"||window.location.hostname.endsWith(".local"))}setBaseUrl(x){return this.config.baseUrl=x,this}setCdnUrl(x){return this.config.cdnUrl=x,this}setEnvironment(x){return this.config.environment=x,this}setStrategy(x){return this.strategy=x,this.config.baseUrl=this.resolveBaseUrl(),this}build(){return{...this.config}}}var Z=(x={},b="auto")=>{return new $(x,b).build()},k=(x,b="auto")=>{return new $({baseUrl:x},b).build().baseUrl},_=(x={})=>{return Z(x,"saas")},g=(x={})=>{return Z(x,"self-hosted")},D=(x,b={})=>{return Z({...b,baseUrl:x},"custom")},N=()=>{return j.environment==="development"},C=()=>{return j.environment==="production"},O=()=>{let x=Z();return x.baseUrl.startsWith("/")||x.baseUrl.includes(typeof window!=="undefined"?window.location.hostname:"localhost")},w=(x)=>{try{if(x.startsWith("/"))return!0;let b=new URL(x);return b.protocol==="http:"||b.protocol==="https:"}catch{return!1}},H=(x)=>{if(!x)return j.DEFAULT_BASE_URL;if(x=x.replace(/\/$/,""),!x.startsWith("http")&&!x.startsWith("/"))x="/"+x;return x};class q{config;constructor(x){if(!x.apiKey)throw new Q("MISSING_API_KEY","API key is required");if(!x.apiKey.startsWith("vf_"))throw new Q("INVALID_API_KEY",'Invalid API key format. API key must start with "vf_"');let b=this.resolveBaseUrl(x.baseUrl);if(this.config={baseUrl:b,timeout:x.timeout||1e4,retries:x.retries||3,debug:x.debug||!1,apiKey:x.apiKey},this.config.debug)console.log("[VulForm API] Client initialized:",{apiKey:x.apiKey.substring(0,8)+"...",baseUrl:this.config.baseUrl,resolvedFrom:x.baseUrl?"provided":"auto-detected"})}resolveBaseUrl(x){if(x&&w(x))return H(x);let b=k(x,"auto");if(this.config?.debug)console.log("[VulForm API] URL Resolution:",{provided:x,resolved:b,strategy:"auto"});return b}async getTemplate(x){let b=await this.makeRequest(`/templates/${x}`,{method:"GET"});if(!b.data?.template)throw new Q("TEMPLATE_NOT_FOUND",`Template ${x} not found`);return b.data.template}async submitForm(x){let b={...x,api_key:this.config.apiKey,metadata:{...x.metadata,userAgent:typeof navigator!=="undefined"?navigator.userAgent:void 0,timestamp:new Date().toISOString()}},P=await this.makeRequest("/submit",{method:"POST",body:JSON.stringify(b),headers:{"Content-Type":"application/json"}});if(!P.success||!P.data)throw new Q(P.error?.code||"SUBMISSION_FAILED",P.error?.message||"Form submission failed");return P.data}async validateField(x,b,P){let A=this.validateFieldClientSide(x,b,P);if(A)return A;if(P.pattern||x==="email")try{let K=await this.makeRequest("/validate",{method:"POST",body:JSON.stringify({field:x,value:b,rules:P}),headers:{"Content-Type":"application/json"}});if(K.data&&!K.data.valid)return K.data.error||"Validation failed"}catch(K){if(this.config.debug)console.warn("[VulForm API] Server validation failed, using client-side validation:",K)}return null}async trackEvent(x){try{await this.makeRequest("/analytics/events",{method:"POST",body:JSON.stringify({events:[x]}),headers:{"Content-Type":"application/json"}})}catch(b){if(this.config.debug)console.warn("[VulForm API] Failed to track analytics event:",b)}}async exportTemplate(x){let b=await this.makeRequest(`/templates/${x.templateId}/export`,{method:"POST",body:JSON.stringify({options:x.options}),headers:{"Content-Type":"application/json"}});if(!b.data)throw new Q("EXPORT_FAILED","Failed to export template");return b.data}validateFieldClientSide(x,b,P){if(P.required&&(!b||typeof b==="string"&&b.trim().length===0))return P.customMessage||`${x} is required`;if(!b||b.length===0)return null;if(typeof b==="string"){if(P.minLength&&b.length<P.minLength)return P.customMessage||`Must be at least ${P.minLength} characters`;if(P.maxLength&&b.length>P.maxLength)return P.customMessage||`Must be no more than ${P.maxLength} characters`;if(x==="email"&&!this.isValidEmail(b))return P.customMessage||"Please enter a valid email address";if(P.pattern&&!new RegExp(P.pattern).test(b))return P.customMessage||"Invalid format"}if(typeof b==="number"||typeof b==="string"&&!isNaN(Number(b))){let A=Number(b);if(P.min!==void 0&&A<P.min)return P.customMessage||`Must be at least ${P.min}`;if(P.max!==void 0&&A>P.max)return P.customMessage||`Must be no more than ${P.max}`}return null}isValidEmail(x){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(x)}async makeRequest(x,b){let P=`${this.config.baseUrl}${x}`,A=null;for(let K=1;K<=this.config.retries;K++)try{let J=new AbortController,X=setTimeout(()=>J.abort(),this.config.timeout),W=await fetch(P,{...b,headers:{Authorization:`Bearer ${this.config.apiKey}`,"X-VulForm-SDK":"core/1.0.0",...b.headers},signal:J.signal});clearTimeout(X);let Y,B=W.headers.get("content-type");if(B&&B.includes("application/json"))Y=await W.json();else Y={message:await W.text()};if(!W.ok){if(W.status===429){let R=parseInt(W.headers.get("Retry-After")||"60");throw new Q("RATE_LIMIT_EXCEEDED",`Rate limit exceeded. Try again in ${R} seconds`,W.status,{retryAfter:R})}throw new Q(Y.code||"API_ERROR",Y.message||`HTTP ${W.status}: ${W.statusText}`,W.status,Y)}return{success:!0,data:Y}}catch(J){if(A=J instanceof Error?J:new Error(String(J)),J instanceof Q){if(["INVALID_API_KEY","TEMPLATE_NOT_FOUND","VALIDATION_ERROR"].includes(J.code))throw J}if(K<this.config.retries){let X=Math.min(1000*Math.pow(2,K-1),1e4);await this.sleep(X);continue}if(J instanceof Q)throw J;throw new Q("NETWORK_ERROR",A.message||"Network request failed",0,A)}throw new Q("UNKNOWN_ERROR","An unknown error occurred")}sleep(x){return new Promise((b)=>setTimeout(b,x))}}class Q extends Error{code;statusCode;details;constructor(x,b,P,A){super(b);this.name="VulFormError",this.code=x,this.statusCode=P,this.details=A}}class z{static validateForm(x,b){let P={};for(let A of b.fields){if(["divider","heading","paragraph","hidden"].includes(A.type))continue;let K=x[A.name],J=this.validateField(A,K);if(J)P[A.name]=J}return P}static validateField(x,b){if(!x.validation)return null;return this.validateValue(x.name,b,x.validation,x.type)}static validateValue(x,b,P,A){if(P.required&&this.isEmpty(b))return P.customMessage||`${x} is required`;if(this.isEmpty(b))return null;if(typeof b==="string"){if(P.minLength&&b.length<P.minLength)return P.customMessage||`Must be at least ${P.minLength} characters`;if(P.maxLength&&b.length>P.maxLength)return P.customMessage||`Must be no more than ${P.maxLength} characters`;if(P.pattern&&!new RegExp(P.pattern).test(b))return P.customMessage||"Invalid format";if(A==="email"&&!this.isValidEmail(b))return P.customMessage||"Please enter a valid email address";if(A==="url"&&!this.isValidUrl(b))return P.customMessage||"Please enter a valid URL";if(A==="tel"&&!this.isValidPhone(b))return P.customMessage||"Please enter a valid phone number"}if(A==="number"&&b!==""){let K=parseFloat(b);if(isNaN(K))return P.customMessage||"Please enter a valid number";if(P.min!==void 0&&K<P.min)return P.customMessage||`Must be at least ${P.min}`;if(P.max!==void 0&&K>P.max)return P.customMessage||`Must be no more than ${P.max}`}if(["date","datetime-local","time"].includes(A||"")){if(!this.isValidDate(b))return P.customMessage||"Please enter a valid date"}if(A==="file"&&b instanceof FileList){let K=this.validateFiles(b,P);if(K)return P.customMessage||K}return null}static isEmpty(x){if(x===null||x===void 0)return!0;if(typeof x==="string")return x.trim().length===0;if(Array.isArray(x))return x.length===0;if(x instanceof FileList)return x.length===0;return!1}static isValidEmail(x){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(x)}static isValidUrl(x){try{return new URL(x),!0}catch{return!1}}static isValidPhone(x){let b=/^[\+]?[1-9][\d]{0,15}$/,P=x.replace(/[\s\-\(\)\.]/g,"");return b.test(P)}static isValidDate(x){if(!x)return!1;let b=new Date(x);return!isNaN(b.getTime())}static validateFiles(x,b){if(x.length===0)return null;if(b.max&&x.length>b.max)return`Too many files. Maximum ${b.max} files allowed`;for(let P=0;P<x.length;P++){let A=x[P];if(b.min&&A.size<b.min)return`File "${A.name}" is too small. Minimum size: ${this.formatFileSize(b.min)}`;if(b.max&&A.size>b.max)return`File "${A.name}" is too large. Maximum size: ${this.formatFileSize(b.max)}`}return null}static formatFileSize(x){let b=["Bytes","KB","MB","GB"];if(x===0)return"0 Bytes";let P=Math.floor(Math.log(x)/Math.log(1024));return Math.round(x/Math.pow(1024,P)*100)/100+" "+b[P]}}class S{validators=new Map;debounceTimers=new Map;registerField(x,b){this.validators.set(x,b)}async validateField(x,b,P,A=300){let K=this.debounceTimers.get(x);if(K)clearTimeout(K);let J=setTimeout(async()=>{let X=this.validators.get(x);if(X)try{let W=await X(b);P(W)}catch(W){console.warn(`Validation error for field ${x}:`,W),P(null)}this.debounceTimers.delete(x)},A);this.debounceTimers.set(x,J)}destroy(){this.debounceTimers.forEach((x)=>clearTimeout(x)),this.debounceTimers.clear(),this.validators.clear()}}
//# debugId=0CB2D4E364263BD464756E2164756E21
//# sourceMappingURL=index.js.map