collectlie
Version: 
TypeScript SDK for Collectlie - flexible data collection platform with custom types, schema validation, and Supabase backend integration
2 lines (1 loc) • 19 kB
JavaScript
import{createClient as e}from"@supabase/supabase-js";class t extends Error{constructor(e,n,o,i,r,s){super(e),this.name="CollectlieError",this.status=n,this.code=o,this.details=i,this.debugInfo=r,this.upgradeRequired=s,Error.captureStackTrace&&Error.captureStackTrace(this,t)}toDetailedString(){let e=`${this.name} (${this.status}): ${this.message}`;if(this.details){e+=`\nDetails: ${Array.isArray(this.details)?this.details.join(", "):this.details}`}return this.upgradeRequired&&(e+="\nPlan upgrade required"),this.debugInfo&&(e+=`\nDebug: ${JSON.stringify(this.debugInfo,null,2)}`),e}}class n extends t{constructor(e,t,n){super(e,400,"VALIDATION_ERROR",t,n),this.name="ValidationError"}}class o extends t{constructor(e,t,n,o){super(e,403,"AUTHENTICATION_ERROR",t,n,o),this.name="AuthenticationError"}}class i extends t{constructor(e,t,n){super(e,413,"PAYLOAD_TOO_LARGE",t,n),this.name="PayloadTooLargeError"}}class r extends t{constructor(e,t,n,o){super(e,429,"RATE_LIMIT_ERROR",t,n),this.name="RateLimitError",o&&(this.resetAt=new Date(o))}}class s extends t{constructor(e,t=500,n,o){super(e,t,"SERVER_ERROR",n,o),this.name="ServerError"}}class a extends t{constructor(e,t){super(e,0,"NETWORK_ERROR",void 0,t?{originalMessage:t.message,originalStack:t.stack}:void 0),this.name="NetworkError"}}class c extends t{constructor(e,t){super(e,0,"CONFIGURATION_ERROR",t),this.name="ConfigurationError"}}function d(e,a){const c=a.error||a.message||`HTTP ${e} error`,d=a.details,l=a.debug_info,u=a.upgrade_required;switch(e){case 400:return new n(c,d,l);case 403:return new o(c,d,l,u);case 413:return new i(c,d,l);case 429:return new r(c,d,l);case 500:case 502:case 503:case 504:return new s(c,e,d,l);default:return new t(c,e,"UNKNOWN_ERROR",d,l,u)}}class l{constructor(e){this.config={baseUrl:"https://mmnnfybraewpuvttamvb.supabase.co/functions/v1",validateSchemas:!0,...e}}async makeRequest(e){const{method:t,endpoint:n,data:o,headers:i={},authRequired:r=!1}=e,s=`${this.config.baseUrl}${n}`,c={"Content-Type":"application/json",...i};try{const e=await fetch(s,{method:t,headers:c,body:o?JSON.stringify(o):void 0});let n;try{n=await e.json()}catch{n=null}if(!e.ok){const t=n;throw d(e.status,t||{error:`HTTP ${e.status}: ${e.statusText}`})}return n}catch(e){if(e&&"object"==typeof e&&"status"in e)throw e;if(e instanceof Error)throw new a(e.message||"Network request failed",e);throw new a("Unknown error occurred")}}async submitSubmission(t){const n=e("https://mmnnfybraewpuvttamvb.supabase.co","eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1tbm5meWJyYWV3cHV2dHRhbXZiIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTU2MzUwMTUsImV4cCI6MjA3MTIxMTAxNX0.915hb2NCk8CV-ZEurWBlB49KxwrLwwhGeDPWSafS7kE"),{data:o,error:i}=await n.functions.invoke("submit-submission",{body:t});if(i){if(i.status)throw d(i.status,{error:i.message||"Edge function call failed"});throw new a(i.message||"Edge function call failed")}return o}async submitWithFiles(e){const t=new FormData,n=[...e.attachments||[]],o={...e};o.attachments&&(o.attachments=n.map(e=>({filename:e.filename,mimeType:e.mimeType,size:e.size}))),t.append("data",JSON.stringify(o)),n.forEach((e,n)=>{if(e.data){const o=`attachment_${n}`;t.append(o,e.data,e.filename)}});const i=`${this.config.baseUrl}/submit-submission`,r=await fetch(i,{method:"POST",body:t,headers:{Authorization:"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1tbm5meWJyYWV3cHV2dHRhbXZiIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTU2MzUwMTUsImV4cCI6MjA3MTIxMTAxNX0.915hb2NCk8CV-ZEurWBlB49KxwrLwwhGeDPWSafS7kE"}});if(!r.ok){let e=null;try{e=await r.json()}catch{e={error:`HTTP ${r.status}: ${r.statusText}`}}throw d(r.status,e||{error:`HTTP ${r.status}: ${r.statusText}`})}return await r.json()}hasFilesToUpload(e){return e.attachments?.some(e=>null!=e.data)??!1}}function u(e,t={}){const n=[];if(e.project_id&&"string"==typeof e.project_id||n.push("project_id is required and must be a string"),e.type&&"string"==typeof e.type||n.push("type is required and must be a string"),void 0!==e.title&&"string"!=typeof e.title&&n.push("title must be a string"),void 0!==e.content&&"string"!=typeof e.content&&n.push("content must be a string"),void 0!==e.author)if("object"!=typeof e.author||null===e.author)n.push("author must be an object");else{const t=function(e){const t=[];void 0!==e.email&&"string"!=typeof e.email&&t.push("author.email must be a string");void 0!==e.name&&"string"!=typeof e.name&&t.push("author.name must be a string");void 0!==e.avatar&&"string"!=typeof e.avatar&&t.push("author.avatar must be a string");void 0!==e.userId&&"string"!=typeof e.userId&&t.push("author.userId must be a string");void 0!==e.username&&"string"!=typeof e.username&&t.push("author.username must be a string");return t}(e.author);n.push(...t)}if(void 0!==e.attachments)if(Array.isArray(e.attachments)){const o=function(e){const t=[];return e.forEach((e,n)=>{e.filename&&"string"==typeof e.filename||t.push(`Attachment ${n+1}: filename is required and must be a string`),e.data&&(e.data instanceof File||e.data instanceof Blob?(!e.mimeType&&e.data.type&&(e.mimeType=e.data.type),e.size||(e.size=e.data.size)):t.push(`Attachment ${n+1}: data must be a File or Blob object`)),e.url&&"string"!=typeof e.url&&t.push(`Attachment ${n+1}: url must be a string`),e.data||e.url||t.push(`Attachment ${n+1}: must have either data (for upload) or url (if pre-uploaded)`)}),t}(e.attachments,t);n.push(...o)}else n.push("attachments must be an array");if(void 0!==e.tags){const t=function(e){const t=[];"string"==typeof e?0===e.length&&t.push("tag cannot be empty"):Array.isArray(e)?e.forEach((e,n)=>{"string"!=typeof e?t.push(`tag at index ${n} must be a string`):0===e.length&&t.push(`tag at index ${n} cannot be empty`)}):t.push("tags must be a string or array of strings");return t}(e.tags);n.push(...t)}return n}function m(e){const t={};if("undefined"==typeof window)return t;const n=e||{url:!0,path:!0,title:!0,search:!0,hash:!0,host:!0,referrer:!0};try{window.location&&(n.url&&(t.url=window.location.href),n.path&&(t.path=window.location.pathname),n.search&&window.location.search&&(t.search=window.location.search),n.hash&&window.location.hash&&(t.hash=window.location.hash),n.host&&(t.host=window.location.host)),n.title&&document.title&&(t.title=document.title),n.referrer&&document.referrer&&(t.referrer=document.referrer)}catch(e){}return t}function h(e){const t={};if("undefined"==typeof window)return t;const n=e||{viewport:!0,screen:!0,language:!0,languages:!0,timezone:!0,cookiesEnabled:!0,localStorageAvailable:!0,sessionStorageAvailable:!0};try{if(n.viewport&&window.innerWidth&&window.innerHeight&&(t.viewport={width:window.innerWidth,height:window.innerHeight}),n.screen&&window.screen&&(t.screen={width:window.screen.width,height:window.screen.height,colorDepth:window.screen.colorDepth||24}),n.language&&navigator.language&&(t.language=navigator.language),n.languages&&navigator.languages&&navigator.languages.length>0&&(t.languages=Array.from(navigator.languages)),n.timezone)try{t.timezone=Intl.DateTimeFormat().resolvedOptions().timeZone}catch{const e=(new Date).getTimezoneOffset();t.timezone=`UTC${e>0?"-":"+"}${Math.abs(e/60)}`}if(n.cookiesEnabled&&(t.cookiesEnabled=navigator.cookieEnabled),n.localStorageAvailable)try{localStorage.setItem("test","test"),localStorage.removeItem("test"),t.localStorageAvailable=!0}catch{t.localStorageAvailable=!1}if(n.sessionStorageAvailable)try{sessionStorage.setItem("test","test"),sessionStorage.removeItem("test"),t.sessionStorageAvailable=!0}catch{t.sessionStorageAvailable=!1}}catch(e){}return t}function p(e){const t={};if("undefined"==typeof window||"undefined"==typeof navigator)return t;const n=e||{deviceType:!0,touchSupport:!0,pixelRatio:!0};try{if(n.deviceType){const e=navigator.userAgent.toLowerCase(),n=/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(e),o=/ipad|android(?=.*\bmobile\b)/i.test(e)||window.screen&&window.screen.width>=768&&window.screen.width<=1024;t.deviceType=n&&!o?"mobile":o?"tablet":"desktop"}n.touchSupport&&(t.touchSupport="ontouchstart"in window||navigator.maxTouchPoints>0),n.pixelRatio&&window.devicePixelRatio&&(t.pixelRatio=window.devicePixelRatio)}catch(e){}return t}function f(e){const t={};if("undefined"==typeof window)return t;const n=e||{errorCount:!0,recentErrors:!0,failedResources:!0};try{if(window.console&&window.console.__errors){const e=window.console.__errors||[];n.errorCount&&(t.error_count=e.length),n.recentErrors&&(t.recent_errors=e.slice(-5).map(e=>{return{message:(t=e.message||"Unknown error",t.replace(/\/[^\/\s]+\.(js|ts|jsx|tsx)/g,"[file]").replace(/https?:\/\/[^\s]+/g,"[url]").replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,"[email]").replace(/\b\d{4,}\b/g,"[number]").replace(/['"].*?['"]/g,"[string]").substring(0,200)),timestamp:e.timestamp||Date.now(),type:e.type||"error"};var t}))}if(n.failedResources){const e={};if(window.performance&&window.performance.getEntriesByType){window.performance.getEntriesByType("resource").forEach(t=>{if(0===t.responseStatus||t.responseStatus>=400){const n=t.name.split(".").pop()?.split("?")[0]||"unknown";e[n]=(e[n]||0)+1}}),t.failed_resources=Object.entries(e).map(([e,t])=>({type:e,count:t}))}}}catch(e){}return t}function g(e){const t={};if("undefined"==typeof window||"undefined"==typeof navigator)return t;const n=e||{online:!0,connection:!0,colorScheme:!0,reducedMotion:!0,contrast:!0,transparency:!0,invertedColors:!0,monochrome:!0,forcedColors:!0,storage:!0,permissions:!0};try{if(n.online&&(t.online=navigator.onLine),n.connection&&"connection"in navigator){const e=navigator.connection;e&&(t.connection={type:e.type,effective_type:e.effectiveType,downlink:e.downlink,rtt:e.rtt,save_data:e.saveData})}if(window.matchMedia&&(n.colorScheme&&(window.matchMedia("(prefers-color-scheme: dark)").matches?t.color_scheme="dark":window.matchMedia("(prefers-color-scheme: light)").matches?t.color_scheme="light":t.color_scheme="no-preference"),n.reducedMotion&&(t.reduced_motion=window.matchMedia("(prefers-reduced-motion: reduce)").matches),n.contrast&&(window.matchMedia("(prefers-contrast: high)").matches?t.contrast="high":window.matchMedia("(prefers-contrast: low)").matches?t.contrast="low":t.contrast="no-preference"),n.transparency&&(t.transparency=window.matchMedia("(prefers-reduced-transparency: reduce)").matches?"reduce":"no-preference"),n.invertedColors&&(t.inverted_colors=window.matchMedia("(inverted-colors: inverted)").matches),n.forcedColors&&(t.forced_colors=window.matchMedia("(forced-colors: active)").matches),n.monochrome)){const e=window.matchMedia("(monochrome)");t.monochrome=e.matches?1:0}if(n.storage){t.storage={cookies_enabled:navigator.cookieEnabled,local_storage:!1,session_storage:!1,indexed_db:!1};try{localStorage.setItem("test","test"),localStorage.removeItem("test"),t.storage.local_storage=!0}catch{}try{sessionStorage.setItem("test","test"),sessionStorage.removeItem("test"),t.storage.session_storage=!0}catch{}"indexedDB"in window&&(t.storage.indexed_db=!0)}n.permissions&&"permissions"in navigator&&navigator.permissions.query&&Promise.all([navigator.permissions.query({name:"notifications"}).catch(()=>null),navigator.permissions.query({name:"geolocation"}).catch(()=>null)]).then(([e,n])=>{e&&(t.permissions={...t.permissions,notifications:e.state}),n&&(t.permissions={...t.permissions,geolocation:n.state})}).catch(()=>{})}catch(e){}return t}function w(e){const t={};if("undefined"==typeof window||"undefined"==typeof document)return t;const n=e||{hasInteraction:!0,focusedElementType:!0,formState:!0,visibility:!0};try{if(n.hasInteraction&&(t.has_interaction=document.hasFocus()||!1),n.focusedElementType){const e=document.activeElement;if(e&&e!==document.body&&(t.focused_element_type=e.tagName.toLowerCase(),"INPUT"===e.tagName)){const n=e.type;t.focused_element_type=`input[type=${n}]`}}if(n.formState){const e=document.forms;if(e&&e.length>0){let n=!1,o=0;Array.from(e).forEach(e=>{const t=e.querySelectorAll("input, textarea, select");o+=t.length,t.forEach(e=>{e.value!==e.defaultValue&&(n=!0)})}),t.form_state={has_forms:!0,has_unsaved_changes:n,active_form_fields:o}}else t.form_state={has_forms:!1,has_unsaved_changes:!1,active_form_fields:0}}n.visibility&&"visibilityState"in document&&(t.visibility={visible:!document.hidden,visibility_state:document.visibilityState},document.hidden&&window.__hiddenSince&&(t.visibility.hidden_duration=Date.now()-window.__hiddenSince),window.__visibilityTracking||(window.__visibilityTracking=!0,document.addEventListener("visibilitychange",()=>{document.hidden?window.__hiddenSince=Date.now():delete window.__hiddenSince})))}catch(e){}return t}function v(e){const t={browser:!0,device:!0,performance:!0,errors:!0,environment:!0,interaction:!0,...e},n={};if(t.browser){const e=h();Object.keys(e).length>0&&(n.browser=e)}if(t.device){const e=p();Object.keys(e).length>0&&(n.device=e)}if(t.performance){const e=y();Object.keys(e).length>0&&(n.performance=e)}if(t.errors){const e=f();Object.keys(e).length>0&&(n.errors=e)}if(t.environment){const e=g();Object.keys(e).length>0&&(n.environment=e)}if(t.interaction){const e=w();Object.keys(e).length>0&&(n.interaction=e)}return n}function y(e){const t={};if("undefined"==typeof window)return t;const n=e||{pageLoadTime:!0,connectionType:!0,timeOnPage:!0,scrollPosition:!0,pageDimensions:!0,userInteractions:!0};try{if(n.pageLoadTime&&window.performance&&window.performance.timing){const e=window.performance.timing;e.loadEventEnd&&e.navigationStart&&(t.pageLoadTime=e.loadEventEnd-e.navigationStart)}if(n.connectionType&&"connection"in navigator){const e=navigator.connection;e&&e.effectiveType&&(t.connectionType=e.effectiveType)}if(n.scrollPosition){const e=window.pageXOffset||document.documentElement.scrollLeft||0,n=window.pageYOffset||document.documentElement.scrollTop||0;0===e&&0===n||(t.scrollPosition={x:e,y:n})}if(n.timeOnPage&&window.performance&&window.performance.timing&&window.performance.timing.navigationStart&&(t.timeOnPage=Date.now()-window.performance.timing.navigationStart),n.pageDimensions){const e=document.documentElement,n=document.body;if(e&&n){const o=Math.max(n.scrollHeight,e.scrollHeight,n.offsetHeight,e.offsetHeight,n.clientHeight,e.clientHeight),i=Math.max(n.scrollWidth,e.scrollWidth,n.offsetWidth,e.offsetWidth,n.clientWidth,e.clientWidth);t.pageDimensions={scrollHeight:o,scrollWidth:i}}}if(n.userInteractions&&window.performance&&window.performance.eventCounts){const e=window.performance.eventCounts;e.get&&(t.userInteractions={clicks:e.get("click")||0,keyPresses:e.get("keydown")||0,pointerEvents:e.get("pointerdown")||0})}}catch(e){}return t}class E{constructor(e={}){const t=function(){let e,t;try{"undefined"!=typeof process&&process?.env&&(e=process.env.COLLECTLIE_PROJECT_ID,t=process.env.COLLECTLIE_API_KEY,e||(e=process.env.REACT_APP_COLLECTLIE_PROJECT_ID),t||(t=process.env.REACT_APP_COLLECTLIE_API_KEY),e||(e=process.env.NEXT_PUBLIC_COLLECTLIE_PROJECT_ID),t||(t=process.env.NEXT_PUBLIC_COLLECTLIE_API_KEY),e||(e=process.env.VITE_COLLECTLIE_PROJECT_ID),t||(t=process.env.VITE_COLLECTLIE_API_KEY),e||(e=process.env.NUXT_PUBLIC_COLLECTLIE_PROJECT_ID),t||(t=process.env.NUXT_PUBLIC_COLLECTLIE_API_KEY),e||(e=process.env.NUXT_COLLECTLIE_PROJECT_ID),t||(t=process.env.NUXT_COLLECTLIE_API_KEY),e||(e=process.env.PUBLIC_COLLECTLIE_PROJECT_ID),t||(t=process.env.PUBLIC_COLLECTLIE_API_KEY))}catch{}try{const n=globalThis.Deno;n&&n.env&&(e||(e=n.env.get("COLLECTLIE_PROJECT_ID")),t||(t=n.env.get("COLLECTLIE_API_KEY")))}catch{}try{const n=globalThis.Bun;if(n&&n.env){const o=n.env;e||(e=o.COLLECTLIE_PROJECT_ID),t||(t=o.COLLECTLIE_API_KEY)}}catch{}try{if(void 0!==import.meta&&import.meta.env){const n=import.meta.env;e||(e=n.COLLECTLIE_PROJECT_ID),t||(t=n.COLLECTLIE_API_KEY),e||(e=n.VITE_COLLECTLIE_PROJECT_ID),t||(t=n.VITE_COLLECTLIE_API_KEY),e||(e=n.PUBLIC_COLLECTLIE_PROJECT_ID),t||(t=n.PUBLIC_COLLECTLIE_API_KEY)}}catch{}return{projectId:e,apiKey:t}}(),n=void 0!==e.isDevelopment?e.isDevelopment:function(){try{if("undefined"!=typeof process&&process?.env&&"development"===process.env.NODE_ENV)return!0}catch{}if("undefined"!=typeof window&&window.location){const e=window.location.hostname;return"localhost"===e||"127.0.0.1"===e||"0.0.0.0"===e||"::1"===e||e.endsWith(".localhost")||e.startsWith("192.168.")||e.startsWith("10.")||e.startsWith("172.")}return!1}();this.config={baseUrl:"https://mmnnfybraewpuvttamvb.supabase.co/functions/v1",validateSchemas:!0,isDevelopment:n,projectId:t.projectId,apiKey:t.apiKey,...e},this.httpClient=new l(this.config)}async submit(e,n){const o=function(e,t=!1){const n=[];t&&(e.projectId||n.push("project_id is required. Set COLLECTLIE_PROJECT_ID environment variable or provide projectId in config."),e.apiKey?"string"==typeof(o=e.apiKey)&&o.length>0||n.push("API key is required."):n.push("API key is required for all environments. Set COLLECTLIE_API_KEY environment variable or provide apiKey in config."));var o;return n}(this.config,!0);if(o.length>0)throw new c("Invalid SDK configuration",o);const i={...e};!i.project_id&&this.config.projectId&&(i.project_id=this.config.projectId),!0===n?(i.browser=h(),i.device=p(),i.source=m(),i.performance=y(),i.errors=f(),i.environment=g(),i.interaction=w()):n&&"object"==typeof n&&(!0===n.browser?i.browser=h():n.browser&&"object"==typeof n.browser&&(i.browser=h(n.browser)),!0===n.device?i.device=p():n.device&&"object"==typeof n.device&&(i.device=p(n.device)),!0===n.source?i.source=m():n.source&&"object"==typeof n.source&&(i.source=m(n.source)),!0===n.performance?i.performance=y():n.performance&&"object"==typeof n.performance&&(i.performance=y(n.performance)),!0===n.errors?i.errors=f():n.errors&&"object"==typeof n.errors&&(i.errors=f(n.errors)),!0===n.environment?i.environment=g():n.environment&&"object"==typeof n.environment&&(i.environment=g(n.environment)),!0===n.interaction?i.interaction=w():n.interaction&&"object"==typeof n.interaction&&(i.interaction=w(n.interaction))),this.config.apiKey&&(i.api_key=this.config.apiKey);const r=u(i,this.config);if(r.length>0)throw new c("Submission data validation failed",r);try{let e;return e=this.httpClient.hasFilesToUpload(i)?await this.httpClient.submitWithFiles(i):await this.httpClient.submitSubmission(i),e}catch(e){throw this.config.isDevelopment&&e instanceof t&&console.error("Collectlie Error:",e.toDetailedString()),e}}}async function _(e,t,n){let o,i;"boolean"==typeof t||"object"==typeof t&&("browser"in t||"device"in t||"source"in t||"performance"in t||"errors"in t||"environment"in t||"interaction"in t)?i=t:(o=t,i=n);return new E(o).submit(e,i)}export{o as AuthenticationError,E as Collectlie,t as CollectlieError,c as ConfigurationError,a as NetworkError,i as PayloadTooLargeError,r as RateLimitError,s as ServerError,n as ValidationError,h as browserInfo,E as default,p as deviceInfo,g as environmentInfo,f as errorInfo,v as gatherContext,w as interactionInfo,y as performanceInfo,m as sourceInfo,_ as submit,u as validateSubmissionData};